PageRenderTime 50ms CodeModel.GetById 24ms RepoModel.GetById 0ms app.codeStats 0ms

/src/org/diffkit/db/DKSqlGenerator.java

http://diffkit.googlecode.com/
Java | 334 lines | 285 code | 30 blank | 19 comment | 79 complexity | 0aa95a06fcbaad81d375c19b60742cb9 MD5 | raw file
  1. /**
  2. * Copyright 2010-2011 Joseph Panico
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. package org.diffkit.db;
  17. import java.sql.SQLException;
  18. import java.util.ArrayList;
  19. import java.util.Arrays;
  20. import java.util.List;
  21. import java.util.Map;
  22. import org.apache.commons.collections.MapUtils;
  23. import org.apache.commons.lang.ArrayUtils;
  24. import org.apache.commons.lang.StringUtils;
  25. import org.slf4j.Logger;
  26. import org.slf4j.LoggerFactory;
  27. import org.diffkit.common.DKValidate;
  28. import org.diffkit.util.DKSqlUtil;
  29. import org.diffkit.util.DKStringUtil;
  30. /**
  31. * @author jpanico
  32. */
  33. public class DKSqlGenerator {
  34. private final DKDatabase _database;
  35. private final Logger _log = LoggerFactory.getLogger(this.getClass());
  36. public DKSqlGenerator(DKDatabase database_) {
  37. _database = database_;
  38. DKValidate.notNull(database_);
  39. }
  40. public String generateCreateDDL(DKDBColumn column_) throws SQLException {
  41. DKDBType concreteType = this.getConcreteType(column_);
  42. _log.debug("concreteType->{}", concreteType);
  43. if (!_database.supportsType(concreteType))
  44. return null;
  45. StringBuilder builder = new StringBuilder();
  46. String notNullSpecifier = column_.isPartOfPrimaryKey() ? " NOT NULL" : "";
  47. builder.append(String.format("%s\t\t%s%s%s",
  48. this.generateIdentifierString(column_.getName()), concreteType.getSqlTypeName(),
  49. this.generateSizeSpecifier(column_), notNullSpecifier));
  50. return builder.toString();
  51. }
  52. public String generateSizeSpecifier(DKDBColumn column_) throws SQLException {
  53. if (column_ == null)
  54. return null;
  55. DKDBTypeInfo dbTypeInfo = _database.getConcreteTypeInfo(column_.getDBTypeName());
  56. if (dbTypeInfo == null)
  57. _log.warn("no dbTypeInfo for column_->{}", column_);
  58. else if (dbTypeInfo.getType().ignoresLengthSpecifier()) {
  59. return "";
  60. }
  61. int size = column_.getSize();
  62. int scale = column_.getScale();
  63. if (size <= 0)
  64. return "";
  65. if (scale <= 0)
  66. return String.format("(%s)", size);
  67. return String.format("(%s,%s)", size, scale);
  68. }
  69. public String generateDropDDL(DKDBTable table_) {
  70. return String.format("DROP TABLE %s",
  71. this.generateQualifiedTableIdentifierString(table_));
  72. }
  73. public String generateCreateDDL(DKDBTable table_) throws SQLException {
  74. StringBuilder builder = new StringBuilder();
  75. builder.append(String.format("CREATE TABLE %s\n(\n",
  76. this.generateQualifiedTableIdentifierString(table_)));
  77. DKDBColumn[] columns = table_.getColumns();
  78. DKDBPrimaryKey primaryKey = table_.getPrimaryKey();
  79. for (int i = 0; i < columns.length; i++) {
  80. String columnDDL = this.generateCreateDDL(columns[i]);
  81. if (columnDDL == null)
  82. continue;
  83. builder.append(String.format("\t\t%s", columnDDL));
  84. if ((i < (columns.length - 1)) || (primaryKey != null))
  85. builder.append(",");
  86. builder.append("\n");
  87. }
  88. if (_log.isDebugEnabled())
  89. _log.debug("primaryKey->{}", primaryKey);
  90. if (primaryKey != null)
  91. builder.append(String.format("\t\t%s", this.generateCreateDDL(primaryKey)));
  92. builder.append(")\n");
  93. String ddlString = builder.toString();
  94. if (_log.isDebugEnabled())
  95. _log.debug("ddlString->{}", ddlString);
  96. return ddlString;
  97. }
  98. public String generateCreateDDL(DKDBPrimaryKey primaryKey_) {
  99. if (primaryKey_ == null)
  100. return "";
  101. StringBuilder builder = new StringBuilder();
  102. builder.append(String.format("CONSTRAINT %s PRIMARY KEY (",
  103. this.generateIdentifierString(primaryKey_.getName())));
  104. String[] columnNames = primaryKey_.getColumnNames();
  105. for (int i = 0; i < columnNames.length; i++) {
  106. builder.append(this.generateIdentifierString(columnNames[i]));
  107. if (i < (columnNames.length - 1))
  108. builder.append(",");
  109. }
  110. builder.append(")");
  111. return builder.toString();
  112. }
  113. public String generateInsertDML(Map<String, ?> row_, DKDBTable table_)
  114. throws SQLException {
  115. if (_log.isDebugEnabled()) {
  116. _log.debug("row_->{}", row_);
  117. _log.debug("table_->{}", table_);
  118. }
  119. DKValidate.notNull(table_);
  120. if (MapUtils.isEmpty(row_))
  121. return null;
  122. List<Object> values = new ArrayList<Object>(row_.size());
  123. List<DKDBTypeInfo> typeInfos = new ArrayList<DKDBTypeInfo>(row_.size());
  124. List<String> columnNames = new ArrayList<String>(row_.size());
  125. DKDBColumn[] columns = table_.getColumns();
  126. for (DKDBColumn column : columns) {
  127. if (!row_.containsKey(column.getName()))
  128. continue;
  129. values.add(row_.get(column.getName()));
  130. typeInfos.add(_database.getConcreteTypeInfo(column.getDBTypeName()));
  131. columnNames.add(column.getName());
  132. }
  133. return generateInsertDML(values.toArray(),
  134. typeInfos.toArray(new DKDBTypeInfo[typeInfos.size()]),
  135. columnNames.toArray(new String[columnNames.size()]), table_.getSchema(),
  136. table_.getTableName());
  137. }
  138. public String generateUpdateDML(Object[] values_, DKDBTypeInfo[] typeInfos_,
  139. String[] columnNames_, int[] keyIndices_,
  140. int[] updateIndices_, String schemaName_,
  141. String tableName_) {
  142. if (_log.isDebugEnabled()) {
  143. _log.debug("values_->{}", Arrays.toString(values_));
  144. _log.debug("typeInfos_->{}", Arrays.toString(typeInfos_));
  145. _log.debug("columnNames_->{}", Arrays.toString(columnNames_));
  146. _log.debug("keyIndices_->{}", Arrays.toString(keyIndices_));
  147. _log.debug("updateIndices_->{}", Arrays.toString(updateIndices_));
  148. _log.debug("schemaName_->{}", schemaName_);
  149. _log.debug("tableName_->{}", tableName_);
  150. }
  151. if (ArrayUtils.isEmpty(values_) || ArrayUtils.isEmpty(typeInfos_)
  152. || ArrayUtils.isEmpty(columnNames_) || (tableName_ == null))
  153. throw new IllegalArgumentException("null or empty value not allowed here");
  154. if (!((values_.length == typeInfos_.length) && (typeInfos_.length == columnNames_.length)))
  155. throw new IllegalArgumentException(
  156. String.format(
  157. "values_ must be same size as typeInfos_ must be the same size as columnNames_; values_->%s, typeInfos_->%s, columnNames_->%s",
  158. values_, typeInfos_, columnNames_));
  159. String tableIdentifier = this.generateQualifiedTableIdentifierString(schemaName_,
  160. tableName_);
  161. String setClause = this.generateSetClause(values_, typeInfos_, columnNames_,
  162. updateIndices_);
  163. String whereClause = this.generateWhereClause(values_, typeInfos_, columnNames_,
  164. keyIndices_);
  165. if (_log.isDebugEnabled()) {
  166. _log.debug("tableIdentifier->{}", tableIdentifier);
  167. _log.debug("setClause->{}", setClause);
  168. _log.debug("whereClause->{}", whereClause);
  169. }
  170. return String.format("UPDATE %s\n%s\n%s", tableIdentifier, setClause, whereClause);
  171. }
  172. private String generateSetClause(Object[] values_, DKDBTypeInfo[] typeInfos_,
  173. String[] columnNames_, int[] setIndices_) {
  174. if (ArrayUtils.isEmpty(values_) || ArrayUtils.isEmpty(setIndices_))
  175. return null;
  176. StringBuilder builder = new StringBuilder();
  177. builder.append("SET ");
  178. for (int i = 0; i < setIndices_.length; i++) {
  179. builder.append(String.format(
  180. "%s=%s",
  181. this.generateIdentifierString(columnNames_[setIndices_[i]]),
  182. DKSqlUtil.formatForSql(values_[setIndices_[i]],
  183. typeInfos_[setIndices_[i]].getWriteType())));
  184. if (i < setIndices_.length - 1)
  185. builder.append(", ");
  186. }
  187. return builder.toString();
  188. }
  189. public String generateDeleteDML(Object[] keyValues_, DKDBTypeInfo[] keyTypeInfos_,
  190. String[] keyColumnNames_, String schemaName_,
  191. String tableName_) {
  192. if (_log.isDebugEnabled()) {
  193. _log.debug("keyValues_->{}", Arrays.toString(keyValues_));
  194. _log.debug("keyTypeInfos_->{}", Arrays.toString(keyTypeInfos_));
  195. _log.debug("keyColumnNames_->{}", Arrays.toString(keyColumnNames_));
  196. _log.debug("schemaName_->{}", schemaName_);
  197. _log.debug("tableName_->{}", tableName_);
  198. }
  199. if (ArrayUtils.isEmpty(keyValues_) || ArrayUtils.isEmpty(keyTypeInfos_)
  200. || ArrayUtils.isEmpty(keyColumnNames_) || (tableName_ == null))
  201. throw new IllegalArgumentException("null or empty value not allowed here");
  202. if (!((keyValues_.length == keyTypeInfos_.length) && (keyTypeInfos_.length == keyColumnNames_.length)))
  203. throw new IllegalArgumentException(
  204. String.format(
  205. "keyValues_ must be same size as keyTypeInfos_ must be the same size as keyColumnNames_; keyValues_->%s, keyTypeInfos_->%s, keyColumnNames_->%s",
  206. keyValues_, keyTypeInfos_, keyColumnNames_));
  207. StringBuilder builder = new StringBuilder();
  208. builder.append(String.format("DELETE FROM %s\n",
  209. this.generateQualifiedTableIdentifierString(schemaName_, tableName_)));
  210. String whereClause = this.generateWhereClause(keyValues_, keyTypeInfos_,
  211. keyColumnNames_);
  212. if (!StringUtils.isEmpty(whereClause))
  213. builder.append(whereClause);
  214. return builder.toString();
  215. }
  216. private String generateWhereClause(Object[] values_, DKDBTypeInfo[] typeInfos_,
  217. String[] columnNames_) {
  218. if (ArrayUtils.isEmpty(values_))
  219. return null;
  220. int[] clauseIndices = new int[values_.length];
  221. for (int i = 0; i < values_.length; i++)
  222. clauseIndices[i] = i;
  223. return this.generateWhereClause(values_, typeInfos_, columnNames_, clauseIndices);
  224. }
  225. private String generateWhereClause(Object[] values_, DKDBTypeInfo[] typeInfos_,
  226. String[] columnNames_, int[] clauseIndices_) {
  227. if (ArrayUtils.isEmpty(values_) || ArrayUtils.isEmpty(clauseIndices_))
  228. return null;
  229. StringBuilder builder = new StringBuilder();
  230. builder.append("WHERE ");
  231. for (int i = 0; i < clauseIndices_.length; i++) {
  232. builder.append(String.format("(%s=%s )",
  233. this.generateIdentifierString(columnNames_[clauseIndices_[i]]),
  234. DKSqlUtil.formatForSql(values_[clauseIndices_[i]],
  235. typeInfos_[clauseIndices_[i]].getWriteType())));
  236. if (i < clauseIndices_.length - 1)
  237. builder.append(" AND ");
  238. }
  239. return builder.toString();
  240. }
  241. public String generateInsertDML(Object[] values_, DKDBTypeInfo[] typeInfos_,
  242. String[] columnNames_, String schemaName_,
  243. String tableName_) {
  244. if (_log.isDebugEnabled()) {
  245. _log.debug("values_->{}", Arrays.toString(values_));
  246. _log.debug("typeInfos_->{}", Arrays.toString(typeInfos_));
  247. _log.debug("columnNames_->{}", Arrays.toString(columnNames_));
  248. _log.debug("schemaName_->{}", schemaName_);
  249. _log.debug("tableName_->{}", tableName_);
  250. }
  251. if (ArrayUtils.isEmpty(values_) || ArrayUtils.isEmpty(typeInfos_)
  252. || ArrayUtils.isEmpty(columnNames_) || (tableName_ == null))
  253. throw new IllegalArgumentException("null or empty value not allowed here");
  254. if (!((values_.length == typeInfos_.length) && (typeInfos_.length == columnNames_.length)))
  255. throw new IllegalArgumentException(
  256. String.format(
  257. "values_ must be same size as typeInfos_ must be the same size as columnNames_; values_->%s, typeInfos_->%s, columnNames_->%s",
  258. values_, typeInfos_, columnNames_));
  259. // deal with case sensitivity
  260. String tableIdentifier = this.generateQualifiedTableIdentifierString(schemaName_,
  261. tableName_);
  262. String[] columnNames = new String[columnNames_.length];
  263. for (int i = 0; i < columnNames_.length; i++)
  264. columnNames[i] = this.generateIdentifierString(columnNames_[i]);
  265. String[] valueStrings = new String[values_.length];
  266. for (int i = 0; i < values_.length; i++)
  267. valueStrings[i] = DKSqlUtil.formatForSql(values_[i],
  268. typeInfos_[i].getWriteType());
  269. String insertDML = String.format("INSERT INTO %s %s\nVALUES %s", tableIdentifier,
  270. DKStringUtil.toSetString(columnNames), DKStringUtil.toSetString(valueStrings));
  271. _log.debug("insertDML->{}", insertDML);
  272. return insertDML;
  273. }
  274. public String generateSelectDML(DKDBTable table_) {
  275. return String.format(
  276. "SELECT * FROM %s",
  277. this.generateIdentifierString(this.generateQualifiedTableIdentifierString(table_)));
  278. }
  279. private DKDBType getConcreteType(DKDBColumn column_) {
  280. if (column_ == null)
  281. return null;
  282. return DKDBType.getConcreteType(_database.getFlavor(), column_.getDBTypeName());
  283. }
  284. public String generateQualifiedTableIdentifierString(DKDBTable table_) {
  285. if (table_ == null)
  286. return null;
  287. return this.generateQualifiedTableIdentifierString(table_.getSchema(),
  288. table_.getTableName());
  289. }
  290. public String generateQualifiedTableIdentifierString(String schemaName_,
  291. String tableName_) {
  292. if (tableName_ == null)
  293. return null;
  294. if (StringUtils.isEmpty(schemaName_))
  295. return this.generateIdentifierString(tableName_);
  296. return String.format("%s.%s", this.generateIdentifierString(schemaName_),
  297. this.generateIdentifierString(tableName_));
  298. }
  299. public String generateIdentifierString(String rawIdentifier_) {
  300. if (!_database.getCaseSensitive())
  301. return rawIdentifier_;
  302. return DKStringUtil.quote(rawIdentifier_, DKStringUtil.Quote.DOUBLE);
  303. }
  304. }