001 package liquibase.database.core;
002
003 import liquibase.database.DatabaseConnection;
004 import liquibase.database.AbstractDatabase;
005 import liquibase.exception.DatabaseException;
006 import liquibase.exception.DateParseException;
007 import liquibase.statement.DatabaseFunction;
008 import liquibase.util.ISODateFormat;
009
010 import java.text.ParseException;
011 import java.text.SimpleDateFormat;
012 import java.text.DateFormat;
013 import java.util.Date;
014 import java.util.Arrays;
015 import java.util.List;
016
017 public class H2Database extends AbstractDatabase {
018
019 private static String START_CONCAT = "CONCAT(";
020 private static String END_CONCAT = ")";
021 private static String SEP_CONCAT = ", ";
022
023 public H2Database() {
024 this.databaseFunctions.add(new DatabaseFunction("CURRENT_TIMESTAMP()"));
025 }
026
027 public String getTypeName() {
028 return "h2";
029 }
030
031 public String getDefaultDriver(String url) {
032 if (url.startsWith("jdbc:h2")) {
033 return "org.h2.Driver";
034 }
035 return null;
036 }
037
038 public int getPriority() {
039 return PRIORITY_DATABASE;
040 }
041
042 public boolean isCorrectDatabaseImplementation(DatabaseConnection conn) throws DatabaseException {
043 return "H2".equals(conn.getDatabaseProductName());
044 }
045
046 // public void dropDatabaseObjects(String schema) throws DatabaseException {
047 // DatabaseConnection conn = getConnection();
048 // Statement dropStatement = null;
049 // try {
050 // dropStatement = conn.createStatement();
051 // dropStatement.executeUpdate("DROP ALL OBJECTS");
052 // changeLogTableExists = false;
053 // changeLogLockTableExists = false;
054 // changeLogCreateAttempted = false;
055 // changeLogLockCreateAttempted = false;
056 // } catch (SQLException e) {
057 // throw new DatabaseException(e);
058 // } finally {
059 // try {
060 // if (dropStatement != null) {
061 // dropStatement.close();
062 // }
063 // conn.commit();
064 // } catch (SQLException e) {
065 // ;
066 // }
067 // }
068 //
069 // }
070
071 public boolean supportsTablespaces() {
072 return false;
073 }
074
075 @Override
076 public String getViewDefinition(String schemaName, String name) throws DatabaseException {
077 String definition = super.getViewDefinition(schemaName, name);
078 if (!definition.startsWith("SELECT")) {
079 definition = definition.replaceFirst(".*?\n", ""); // some h2 versions return "create view....as\nselect
080 }
081
082 definition = definition.replaceFirst("/\\*.*", ""); // sometimes includes comments at the end
083 return definition;
084 }
085
086 @Override
087 public Date parseDate(String dateAsString) throws DateParseException {
088 try {
089 if (dateAsString.indexOf(' ') > 0) {
090 return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSSSSSSSS").parse(dateAsString);
091 } else {
092 if (dateAsString.indexOf(':') > 0) {
093 return new SimpleDateFormat("HH:mm:ss").parse(dateAsString);
094 } else {
095 return new SimpleDateFormat("yyyy-MM-dd").parse(dateAsString);
096 }
097 }
098 } catch (ParseException e) {
099 throw new DateParseException(dateAsString);
100 }
101 }
102
103 @Override
104 public boolean isLocalDatabase() throws DatabaseException {
105 String url = getConnection().getURL();
106 boolean isLocalURL = (super.isLocalDatabase() || url.startsWith("jdbc:h2:file:")
107 || url.startsWith("jdbc:h2:mem:") || url.startsWith("jdbc:h2:zip:") || url.startsWith("jdbc:h2:~"));
108 return isLocalURL;
109 }
110
111 // @Override
112 // public String convertRequestedSchemaToSchema(String requestedSchema) throws DatabaseException {
113 // return super.convertRequestedSchemaToSchema(requestedSchema).toLowerCase();
114 // }
115
116 @Override
117 public boolean supportsSequences() {
118 return true;
119 }
120
121 @Override
122 protected String getDefaultDatabaseSchemaName() throws DatabaseException {
123 return "PUBLIC";
124 }
125
126 @Override
127 public String getAutoIncrementClause() {
128 return "GENERATED BY DEFAULT AS IDENTITY IDENTITY";
129 }
130
131 @Override
132 public String getConcatSql(String... values) {
133 if (values == null) {
134 return null;
135 }
136
137 return getConcatSql(Arrays.asList(values));
138 }
139
140 /**
141 * Recursive way of building CONCAT instruction
142 *
143 * @param values
144 * a non null List of String
145 * @return a String containing the CONCAT instruction with all elements, or only a value if there is only one
146 * element in the list
147 */
148 private String getConcatSql(List<String> values) {
149 if (values.size() == 1) {
150 return values.get(0);
151 } else {
152 return START_CONCAT + values.get(0) + SEP_CONCAT + getConcatSql(values.subList(1, values.size()))
153 + END_CONCAT;
154 }
155 }
156
157 @Override
158 public String getDateLiteral(String isoDate) {
159 String returnString = isoDate;
160 try {
161 if (isDateTime(isoDate)) {
162 ISODateFormat isoTimestampFormat = new ISODateFormat();
163 DateFormat dbTimestampFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.S");
164 returnString = dbTimestampFormat.format(isoTimestampFormat.parse(isoDate));
165 }
166 } catch (ParseException e) {
167 throw new RuntimeException("Unexpected date format: " + isoDate, e);
168 }
169 return "'" + returnString + "'";
170 }
171
172 @Override
173 public String convertRequestedSchemaToSchema(String requestedSchema) throws DatabaseException {
174 return super.convertRequestedSchemaToSchema(requestedSchema).toUpperCase();
175 }
176
177 @Override
178 public String escapeDatabaseObject(String objectName) {
179 if (objectName != null) {
180 if (isReservedWord(objectName)) {
181 return "\"" + objectName + "\"";
182 }
183 }
184 return objectName;
185 }
186
187 @Override
188 public boolean isReservedWord(String objectName) {
189 return keywords.contains(objectName.toUpperCase());
190 }
191
192 private static List keywords = Arrays.asList(
193 // "ADD",
194 // "ALL",
195 // "ALLOCATE",
196 // "ALTER",
197 // "AND",
198 // "ANY",
199 // "ARE",
200 // "ARRAY",
201 // "AS",
202 // "ASENSITIVE",
203 // "ASYMMETRIC",
204 // "AT",
205 // "ATOMIC",
206 // "AUTHORIZATION",
207 // "BEGIN",
208 // "BETWEEN",
209 // "BIGINT",
210 // "BINARY",
211 // "BLOB",
212 // "BOOLEAN",
213 // "BOTH",
214 // "BY",
215 // "CALL",
216 // "CALLED",
217 // "CASCADED",
218 // "CASE",
219 // "CAST",
220 // "CHAR",
221 // "CHARACTER",
222 // "CHECK",
223 // "CLOB",
224 // "CLOSE",
225 // "COLLATE",
226 // "COLUMN",
227 // "COMMIT",
228 // "CONDITION",
229 // "CONNECT",
230 // "CONSTRAINT",
231 // "CONTINUE",
232 // "CORRESPONDING",
233 // "CREATE",
234 // "CROSS",
235 // "CUBE",
236 // "CURRENT",
237 // "CURRENT_DATE",
238 // "CURRENT_DEFAULT_TRANSFORM_GRO",
239 // "CURRENT_PATH",
240 // "CURRENT_ROLE",
241 // "CURRENT_TIME",
242 // "CURRENT_TIMESTAMP",
243 // "CURRENT_TRANSFORM_GROUP_FOR_T",
244 // "CURRENT_USER",
245 // "CURSOR",
246 // "CYCLE",
247 // "DATE",
248 // "DAY",
249 // "DEALLOCATE",
250 // "DEC",
251 // "DECIMAL",
252 // "DECLARE",
253 // "DEFAULT",
254 // "DELETE",
255 // "DEREF",
256 // "DESCRIBE",
257 // "DETERMINISTIC",
258 // "DISCONNECT",
259 // "DISTINCT",
260 // "DO",
261 // "DOUBLE",
262 // "DROP",
263 // "DYNAMIC",
264 // "EACH",
265 // "ELEMENT",
266 // "ELSE",
267 // "ELSEIF",
268 // "END",
269 // "ESCAPE",
270 // "EXCEPT",
271 // "EXEC",
272 // "EXECUTE",
273 // "EXISTS",
274 // "EXIT",
275 // "EXTERNAL",
276 // "FALSE",
277 // "FETCH",
278 // "FILTER",
279 // "FLOAT",
280 // "FOR",
281 // "FOREIGN",
282 // "FREE",
283 // "FROM",
284 // "FULL",
285 // "FUNCTION",
286 // "GET",
287 // "GLOBAL",
288 // "GRANT",
289 // "GROUP",
290 // "GROUPING",
291 // "HANDLER",
292 // "HAVING",
293 // "HOLD",
294 // "HOUR",
295 // "IDENTITY",
296 // "IF",
297 // "IMMEDIATE",
298 // "IN",
299 // "INDICATOR",
300 // "INNER",
301 // "INOUT",
302 // "INPUT",
303 // "INSENSITIVE",
304 // "INSERT",
305 // "INT",
306 // "INTEGER",
307 // "INTERSECT",
308 // "INTERVAL",
309 // "INTO",
310 // "IS",
311 // "ITERATE",
312 // "JOIN",
313 // "LANGUAGE",
314 // "LARGE",
315 // "LATERAL",
316 // "LEADING",
317 // "LEAVE",
318 // "LEFT",
319 // "LIKE",
320 // "LOCAL",
321 // "LOCALTIME",
322 // "LOCALTIMESTAMP",
323 // "LOOP",
324 // "MATCH",
325 // "MEMBER",
326 // "MERGE",
327 // "METHOD",
328 // "MINUTE",
329 // "MODIFIES",
330 // "MODULE",
331 // "MONTH",
332 // "MULTISET",
333 // "NATIONAL",
334 // "NATURAL",
335 // "NCHAR",
336 // "NCLOB",
337 // "NEW",
338 // "NO",
339 // "NONE",
340 // "NOT",
341 // "NULL",
342 // "NUMERIC",
343 // "OF",
344 // "OLD",
345 // "ON",
346 // "ONLY",
347 // "OPEN",
348 // "OR",
349 // "ORDER",
350 // "OUT",
351 // "OUTER",
352 // "OUTPUT",
353 // "OVER",
354 // "OVERLAPS",
355 // "PARAMETER",
356 // "PARTITION",
357 // "PRECISION",
358 // "PREPARE",
359 // "PRIMARY",
360 // "PROCEDURE",
361 // "RANGE",
362 // "READS",
363 // "REAL",
364 // "RECURSIVE",
365 // "REF",
366 // "REFERENCES",
367 // "REFERENCING",
368 // "RELEASE",
369 // "REPEAT",
370 // "RESIGNAL",
371 // "RESULT",
372 // "RETURN",
373 // "RETURNS",
374 // "REVOKE",
375 // "RIGHT",
376 // "ROLLBACK",
377 // "ROLLUP",
378 // "ROW",
379 // "ROWS",
380 // "SAVEPOINT",
381 // "SCOPE",
382 // "SCROLL",
383 // "SEARCH",
384 // "SECOND",
385 // "SELECT",
386 // "SENSITIVE",
387 // "SESSION_USER",
388 // "SET",
389 // "SIGNAL",
390 // "SIMILAR",
391 // "SMALLINT",
392 // "SOME",
393 // "SPECIFIC",
394 // "SPECIFICTYPE",
395 // "SQL",
396 // "SQLEXCEPTION",
397 // "SQLSTATE",
398 // "SQLWARNING",
399 // "START",
400 // "STATIC",
401 // "SUBMULTISET",
402 // "SYMMETRIC",
403 // "SYSTEM",
404 // "SYSTEM_USER",
405 // "TABLE",
406 // "TABLESAMPLE",
407 // "THEN",
408 // "TIME",
409 // "TIMESTAMP",
410 // "TIMEZONE_HOUR",
411 // "TIMEZONE_MINUTE",
412 // "TO",
413 // "TRAILING",
414 // "TRANSLATION",
415 // "TREAT",
416 // "TRIGGER",
417 // "TRUE",
418 // "UNDO",
419 // "UNION",
420 // "UNIQUE",
421 // "UNKNOWN",
422 // "UNNEST",
423 // "UNTIL",
424 // "UPDATE",
425 "USER",
426 // "USING",
427 // "VALUE",
428 // "VALUES",
429 // "VARCHAR",
430 // "VARYING",
431 // "WHEN",
432 // "WHENEVER",
433 // "WHERE",
434 // "WHILE",
435 // "WINDOW",
436 // "WITH",
437 // "WITHIN",
438 // "WITHOUT",
439 // "YEAR",
440 // "ALIAS",
441 // "AUTOCOMMIT",
442 // "CACHED",
443 // "CHECKPOINT",
444 // "EXPLAIN",
445 // "IGNORECASE",
446 // "INDEX",
447 // "LOGSIZE",
448 // "MATCHED",
449 // "MAXROWS",
450 // "MEMORY",
451 // "MINUS",
452 // "NEXT",
453 // "OPENBRACKET",
454 "PASSWORD"
455 // "PLAN",
456 // "PROPERTY",
457 // "READONLY",
458 // "REFERENTIAL_INTEGRITY",
459 // "RENAME",
460 // "RESTART",
461 // "SCRIPT",
462 // "SCRIPTFORMAT",
463 // "SEMICOLON",
464 // "SEQUENCE",
465 // "SHUTDOWN",
466 // "SOURCE",
467 // "TEMP",
468 // "TEXT",
469 // "VIEW",
470 // "WRITE_DELAY",
471 // "VAR_POP",
472 // "VAR_SAMP",
473 // "STDDEV_POP",
474 // "STDDEV_SAMP",
475 // "DEFRAG",
476 // "INCREMENT",
477 // "TOCHAR",
478 // "DATABASE",
479 // "SCHEMA",
480 // "ROLE",
481 // "DOW",
482 // "INITIAL"
483 );
484
485 public boolean supportsInitiallyDeferrableColumns() {
486 return false;
487 }
488
489 public String getCurrentDateTimeFunction() {
490 if (currentDateTimeFunction != null) {
491 return currentDateTimeFunction;
492 }
493
494 return "NOW()";
495 }
496
497 }