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 }