1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26 package liquibase.sqlgenerator.ext;
27
28 import liquibase.database.Database;
29 import liquibase.database.core.*;
30 import liquibase.exception.ValidationErrors;
31 import liquibase.logging.LogFactory;
32 import liquibase.sql.Sql;
33 import liquibase.sql.UnparsedSql;
34 import liquibase.sqlgenerator.SqlGeneratorChain;
35 import liquibase.statement.AutoIncrementConstraint;
36 import liquibase.statement.ForeignKeyConstraint;
37 import liquibase.statement.UniqueConstraint;
38 import liquibase.statement.core.CreateTableStatement;
39 import liquibase.util.StringUtils;
40 import java.util.Iterator;
41 import java.math.BigDecimal;
42
43 import static liquibase.ext.Constants.EXTENSION_PRIORITY;
44
45
46
47
48
49 public class CreateTableGenerator extends liquibase.sqlgenerator.core.CreateTableGenerator {
50
51 @Override
52 public int getPriority() {
53 return EXTENSION_PRIORITY;
54 }
55
56 @Override
57 public ValidationErrors validate(CreateTableStatement createTableStatement, Database database, SqlGeneratorChain sqlGeneratorChain) {
58 ValidationErrors validationErrors = new ValidationErrors();
59 validationErrors.checkRequiredField("tableName", createTableStatement.getTableName());
60 validationErrors.checkRequiredField("columns", createTableStatement.getColumns());
61 return validationErrors;
62 }
63
64 @Override
65 public Sql[] generateSql(CreateTableStatement statement, Database database, SqlGeneratorChain sqlGeneratorChain) {
66 StringBuffer buffer = new StringBuffer();
67 buffer.append("CREATE TABLE ").append(database.escapeTableName(null, statement.getTableName())).append(" ");
68 buffer.append("(");
69
70 boolean isSinglePrimaryKeyColumn = statement.getPrimaryKeyConstraint() != null
71 && statement.getPrimaryKeyConstraint().getColumns().size() == 1;
72
73 boolean isPrimaryKeyAutoIncrement = false;
74
75 Iterator<String> columnIterator = statement.getColumns().iterator();
76 while (columnIterator.hasNext()) {
77 String column = columnIterator.next();
78
79 buffer.append(database.escapeColumnName(null, statement.getTableName(), column));
80 buffer.append(" ").append(statement.getColumnTypes().get(column));
81
82 AutoIncrementConstraint autoIncrementConstraint = null;
83
84 for (AutoIncrementConstraint currentAutoIncrementConstraint : statement.getAutoIncrementConstraints()) {
85 if (column.equals(currentAutoIncrementConstraint.getColumnName())) {
86 autoIncrementConstraint = currentAutoIncrementConstraint;
87 break;
88 }
89 }
90
91 boolean isAutoIncrementColumn = autoIncrementConstraint != null;
92 boolean isPrimaryKeyColumn = statement.getPrimaryKeyConstraint() != null
93 && statement.getPrimaryKeyConstraint().getColumns().contains(column);
94 isPrimaryKeyAutoIncrement = isPrimaryKeyAutoIncrement
95 || isPrimaryKeyColumn && isAutoIncrementColumn;
96
97 if ((database instanceof SQLiteDatabase) &&
98 isSinglePrimaryKeyColumn &&
99 isPrimaryKeyColumn &&
100 isAutoIncrementColumn) {
101 String pkName = StringUtils.trimToNull(statement.getPrimaryKeyConstraint().getConstraintName());
102 if (pkName == null) {
103 pkName = database.generatePrimaryKeyName(statement.getTableName());
104 }
105 if (pkName != null) {
106 buffer.append(" CONSTRAINT ");
107 buffer.append(database.escapeConstraintName(pkName));
108 }
109 buffer.append(" PRIMARY KEY AUTOINCREMENT");
110 }
111
112
113 if ((database instanceof MySQLDatabase)
114 && statement.getColumnTypes().get(column).toString().startsWith("DATE")) {
115 statement.getDefaultValues().put(column, null);
116 }
117
118 if (statement.getDefaultValue(column) != null) {
119 Object defaultValue = statement.getDefaultValue(column);
120
121 if ((statement.getColumnTypes().get(column).toString().startsWith("DECIMAL")
122 || statement.getColumnTypes().get(column).toString().startsWith("NUMERIC"))
123 && !"null".equalsIgnoreCase(defaultValue.toString())) {
124 int[] bounds = parseBounds(statement.getColumnTypes().get(column).toString());
125 BigDecimal parsedValue = new BigDecimal(defaultValue.toString());
126
127 StringBuilder max = new StringBuilder();
128 for (int i = 0; i < bounds[0] - bounds[1]; i++) max.append("9");
129
130 if (bounds[1] > 0) {
131 max.append(".");
132 }
133 for (int i = 0; i < bounds[1]; i++) max.append("9");
134
135 if (parsedValue.compareTo(new BigDecimal(max.toString())) > 0) {
136 defaultValue = max;
137 }
138 }
139 if (database instanceof MSSQLDatabase) {
140 buffer.append(" CONSTRAINT ").append(((MSSQLDatabase) database).generateDefaultConstraintName(statement.getTableName(), column));
141 }
142 buffer.append(" DEFAULT ");
143 buffer.append(statement.getColumnTypes().get(column).convertObjectToString(defaultValue, database));
144 }
145
146 if (isAutoIncrementColumn) {
147
148 if (database.supportsAutoIncrement()) {
149 String autoIncrementClause = database.getAutoIncrementClause(autoIncrementConstraint.getStartWith(), autoIncrementConstraint.getIncrementBy());
150
151 if (!"".equals(autoIncrementClause)) {
152 buffer.append(" ").append(autoIncrementClause);
153 }
154 } else {
155 LogFactory.getLogger().warning(database.getTypeName()+" does not support autoincrement columns as request for "+(database.escapeTableName(null, statement.getTableName())));
156 }
157 }
158
159 if (statement.getNotNullColumns().contains(column)) {
160 buffer.append(" NOT NULL");
161 } else {
162 if (database instanceof SybaseDatabase || database instanceof SybaseASADatabase) {
163 buffer.append(" NULL");
164 }
165 }
166
167 if (database instanceof InformixDatabase && isSinglePrimaryKeyColumn) {
168 buffer.append(" PRIMARY KEY");
169 }
170
171 if (columnIterator.hasNext()) {
172 buffer.append(", ");
173 }
174 }
175
176 buffer.append(",");
177
178
179 if (!( (database instanceof SQLiteDatabase) &&
180 isSinglePrimaryKeyColumn &&
181 isPrimaryKeyAutoIncrement) &&
182
183 !((database instanceof InformixDatabase) &&
184 isSinglePrimaryKeyColumn
185 )) {
186
187
188
189
190 if (statement.getPrimaryKeyConstraint() != null && statement.getPrimaryKeyConstraint().getColumns().size() > 0) {
191 if (!(database instanceof InformixDatabase)) {
192 String pkName = StringUtils.trimToNull(statement.getPrimaryKeyConstraint().getConstraintName());
193 if (pkName == null) {
194
195
196
197 pkName = database.generatePrimaryKeyName(statement.getTableName());
198 }
199 if (pkName != null) {
200 buffer.append(" CONSTRAINT ");
201 buffer.append(database.escapeConstraintName(pkName));
202 }
203 }
204 buffer.append(" PRIMARY KEY (");
205 buffer.append(database.escapeColumnNameList(StringUtils.join(statement.getPrimaryKeyConstraint().getColumns(), ", ")));
206 buffer.append(")");
207
208 if (database instanceof OracleDatabase &&
209 statement.getPrimaryKeyConstraint().getTablespace() != null) {
210 buffer.append(" USING INDEX TABLESPACE ");
211 buffer.append(statement.getPrimaryKeyConstraint().getTablespace());
212 }
213 buffer.append(",");
214 }
215 }
216
217 for (ForeignKeyConstraint fkConstraint : statement.getForeignKeyConstraints()) {
218 if (!(database instanceof InformixDatabase)) {
219 buffer.append(" CONSTRAINT ");
220 buffer.append(database.escapeConstraintName(fkConstraint.getForeignKeyName()));
221 }
222 String referencesString = fkConstraint.getReferences();
223 buffer.append(" FOREIGN KEY (")
224 .append(database.escapeColumnName(null, statement.getTableName(), fkConstraint.getColumn()))
225 .append(") REFERENCES ")
226 .append(referencesString);
227
228 if (fkConstraint.isDeleteCascade()) {
229 buffer.append(" ON DELETE CASCADE");
230 }
231
232 if ((database instanceof InformixDatabase)) {
233 buffer.append(" CONSTRAINT ");
234 buffer.append(database.escapeConstraintName(fkConstraint.getForeignKeyName()));
235 }
236
237 if (fkConstraint.isInitiallyDeferred()) {
238 buffer.append(" INITIALLY DEFERRED");
239 }
240 if (fkConstraint.isDeferrable()) {
241 buffer.append(" DEFERRABLE");
242 }
243 buffer.append(",");
244 }
245
246 for (UniqueConstraint uniqueConstraint : statement.getUniqueConstraints()) {
247 if (uniqueConstraint.getConstraintName() != null && !constraintNameAfterUnique(database)) {
248 buffer.append(" CONSTRAINT ");
249 buffer.append(database.escapeConstraintName(uniqueConstraint.getConstraintName()));
250 }
251 buffer.append(" UNIQUE (");
252 buffer.append(database.escapeColumnNameList(StringUtils.join(uniqueConstraint.getColumns(), ", ")));
253 buffer.append(")");
254 if (uniqueConstraint.getConstraintName() != null && constraintNameAfterUnique(database)) {
255 buffer.append(" CONSTRAINT ");
256 buffer.append(database.escapeConstraintName(uniqueConstraint.getConstraintName()));
257 }
258 buffer.append(",");
259 }
260
261
262
263
264
265
266 String sql = buffer.toString().replaceFirst(",\\s*$", "") + ")";
267
268
269
270
271
272
273
274
275
276
277
278 if (statement.getTablespace() != null && database.supportsTablespaces()) {
279 if (database instanceof MSSQLDatabase || database instanceof SybaseASADatabase) {
280 sql += " ON " + statement.getTablespace();
281 } else if (database instanceof DB2Database || database instanceof InformixDatabase) {
282 sql += " IN " + statement.getTablespace();
283 } else {
284 sql += " TABLESPACE " + statement.getTablespace();
285 }
286 }
287
288 if (database instanceof MySQLDatabase) {
289 sql += " ENGINE = InnoDB ";
290 }
291
292 return new Sql[] {
293 new UnparsedSql(sql)
294 };
295 }
296
297 private boolean constraintNameAfterUnique(Database database) {
298 return database instanceof InformixDatabase;
299 }
300
301
302 protected int[] parseBounds(final String decimal) {
303 final int[] retval = new int[2];
304 int comma = decimal.indexOf(",");
305
306 try {
307 if (comma < 0) {
308 retval[0] = Integer.parseInt(decimal.substring(decimal.indexOf("(") + 1, decimal.lastIndexOf(")")));
309 retval[1] = 0;
310 }
311 else {
312 retval[0] = Integer.parseInt(decimal.substring(decimal.indexOf("(") + 1, comma));
313 retval[1] = Integer.parseInt(decimal.substring(comma + 1, decimal.lastIndexOf(")")));
314 }
315 }
316 catch (StringIndexOutOfBoundsException e) {
317 System.out.println("parsebounds " + decimal);
318 throw e;
319 }
320 return retval;
321 }
322 }