package liquibase.database.core; import liquibase.database.DatabaseConnection; import liquibase.database.AbstractDatabase; import liquibase.exception.DatabaseException; import liquibase.exception.DateParseException; import liquibase.statement.DatabaseFunction; import liquibase.util.ISODateFormat; import java.text.ParseException; import java.text.SimpleDateFormat; import java.text.DateFormat; import java.util.Date; import java.util.Arrays; import java.util.List; public class H2Database extends AbstractDatabase { private static String START_CONCAT = "CONCAT("; private static String END_CONCAT = ")"; private static String SEP_CONCAT = ", "; public H2Database() { this.databaseFunctions.add(new DatabaseFunction("CURRENT_TIMESTAMP()")); } public String getTypeName() { return "h2"; } public String getDefaultDriver(String url) { if (url.startsWith("jdbc:h2")) { return "org.h2.Driver"; } return null; } public int getPriority() { return PRIORITY_DATABASE; } public boolean isCorrectDatabaseImplementation(DatabaseConnection conn) throws DatabaseException { return "H2".equals(conn.getDatabaseProductName()); } // public void dropDatabaseObjects(String schema) throws DatabaseException { // DatabaseConnection conn = getConnection(); // Statement dropStatement = null; // try { // dropStatement = conn.createStatement(); // dropStatement.executeUpdate("DROP ALL OBJECTS"); // changeLogTableExists = false; // changeLogLockTableExists = false; // changeLogCreateAttempted = false; // changeLogLockCreateAttempted = false; // } catch (SQLException e) { // throw new DatabaseException(e); // } finally { // try { // if (dropStatement != null) { // dropStatement.close(); // } // conn.commit(); // } catch (SQLException e) { // ; // } // } // // } public boolean supportsTablespaces() { return false; } @Override public String getViewDefinition(String schemaName, String name) throws DatabaseException { String definition = super.getViewDefinition(schemaName, name); if (!definition.startsWith("SELECT")) { definition = definition.replaceFirst(".*?\n", ""); //some h2 versions return "create view....as\nselect } definition = definition.replaceFirst("/\\*.*",""); //sometimes includes comments at the end return definition; } @Override public Date parseDate(String dateAsString) throws DateParseException { try { if (dateAsString.indexOf(' ') > 0) { return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSSSSSSSS").parse(dateAsString); } else { if (dateAsString.indexOf(':') > 0) { return new SimpleDateFormat("HH:mm:ss").parse(dateAsString); } else { return new SimpleDateFormat("yyyy-MM-dd").parse(dateAsString); } } } catch (ParseException e) { throw new DateParseException(dateAsString); } } @Override public boolean isLocalDatabase() throws DatabaseException { String url = getConnection().getURL(); boolean isLocalURL = ( super.isLocalDatabase() || url.startsWith("jdbc:h2:file:") || url.startsWith("jdbc:h2:mem:") || url.startsWith("jdbc:h2:zip:") || url.startsWith("jdbc:h2:~") ); return isLocalURL; } // @Override // public String convertRequestedSchemaToSchema(String requestedSchema) throws DatabaseException { // return super.convertRequestedSchemaToSchema(requestedSchema).toLowerCase(); // } @Override public boolean supportsSequences() { return true; } @Override protected String getDefaultDatabaseSchemaName() throws DatabaseException { return "PUBLIC"; } @Override public String getAutoIncrementClause() { return "GENERATED BY DEFAULT AS IDENTITY IDENTITY"; } @Override public String getConcatSql(String... values) { if (values == null) { return null; } return getConcatSql(Arrays.asList(values)); } /** * Recursive way of building CONCAT instruction * * @param values a non null List of String * @return a String containing the CONCAT instruction with all elements, or only a value if there is only one element in the list */ private String getConcatSql(List<String> values) { if (values.size() == 1) { return values.get(0); } else { return START_CONCAT + values.get(0) + SEP_CONCAT + getConcatSql(values.subList(1, values.size())) + END_CONCAT; } } @Override public String getDateLiteral(String isoDate) { String returnString = isoDate; try { if (isDateTime(isoDate)) { ISODateFormat isoTimestampFormat = new ISODateFormat(); DateFormat dbTimestampFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.S"); returnString = dbTimestampFormat.format(isoTimestampFormat.parse(isoDate)); } } catch (ParseException e) { throw new RuntimeException("Unexpected date format: " + isoDate, e); } return "'" + returnString + "'"; } @Override public String convertRequestedSchemaToSchema(String requestedSchema) throws DatabaseException { return super.convertRequestedSchemaToSchema(requestedSchema).toUpperCase(); } @Override public String escapeDatabaseObject(String objectName) { if (objectName != null) { if (isReservedWord(objectName)) { return "\""+objectName+"\""; } } return objectName; } @Override public boolean isReservedWord(String objectName) { return keywords.contains(objectName.toUpperCase()); } private static List keywords = Arrays.asList( // "ADD", // "ALL", // "ALLOCATE", // "ALTER", // "AND", // "ANY", // "ARE", // "ARRAY", // "AS", // "ASENSITIVE", // "ASYMMETRIC", // "AT", // "ATOMIC", // "AUTHORIZATION", // "BEGIN", // "BETWEEN", // "BIGINT", // "BINARY", // "BLOB", // "BOOLEAN", // "BOTH", // "BY", // "CALL", // "CALLED", // "CASCADED", // "CASE", // "CAST", // "CHAR", // "CHARACTER", // "CHECK", // "CLOB", // "CLOSE", // "COLLATE", // "COLUMN", // "COMMIT", // "CONDITION", // "CONNECT", // "CONSTRAINT", // "CONTINUE", // "CORRESPONDING", // "CREATE", // "CROSS", // "CUBE", // "CURRENT", // "CURRENT_DATE", // "CURRENT_DEFAULT_TRANSFORM_GRO", // "CURRENT_PATH", // "CURRENT_ROLE", // "CURRENT_TIME", // "CURRENT_TIMESTAMP", // "CURRENT_TRANSFORM_GROUP_FOR_T", // "CURRENT_USER", // "CURSOR", // "CYCLE", // "DATE", // "DAY", // "DEALLOCATE", // "DEC", // "DECIMAL", // "DECLARE", // "DEFAULT", // "DELETE", // "DEREF", // "DESCRIBE", // "DETERMINISTIC", // "DISCONNECT", // "DISTINCT", // "DO", // "DOUBLE", // "DROP", // "DYNAMIC", // "EACH", // "ELEMENT", // "ELSE", // "ELSEIF", // "END", // "ESCAPE", // "EXCEPT", // "EXEC", // "EXECUTE", // "EXISTS", // "EXIT", // "EXTERNAL", // "FALSE", // "FETCH", // "FILTER", // "FLOAT", // "FOR", // "FOREIGN", // "FREE", // "FROM", // "FULL", // "FUNCTION", // "GET", // "GLOBAL", // "GRANT", // "GROUP", // "GROUPING", // "HANDLER", // "HAVING", // "HOLD", // "HOUR", // "IDENTITY", // "IF", // "IMMEDIATE", // "IN", // "INDICATOR", // "INNER", // "INOUT", // "INPUT", // "INSENSITIVE", // "INSERT", // "INT", // "INTEGER", // "INTERSECT", // "INTERVAL", // "INTO", // "IS", // "ITERATE", // "JOIN", // "LANGUAGE", // "LARGE", // "LATERAL", // "LEADING", // "LEAVE", // "LEFT", // "LIKE", // "LOCAL", // "LOCALTIME", // "LOCALTIMESTAMP", // "LOOP", // "MATCH", // "MEMBER", // "MERGE", // "METHOD", // "MINUTE", // "MODIFIES", // "MODULE", // "MONTH", // "MULTISET", // "NATIONAL", // "NATURAL", // "NCHAR", // "NCLOB", // "NEW", // "NO", // "NONE", // "NOT", // "NULL", // "NUMERIC", // "OF", // "OLD", // "ON", // "ONLY", // "OPEN", // "OR", // "ORDER", // "OUT", // "OUTER", // "OUTPUT", // "OVER", // "OVERLAPS", // "PARAMETER", // "PARTITION", // "PRECISION", // "PREPARE", // "PRIMARY", // "PROCEDURE", // "RANGE", // "READS", // "REAL", // "RECURSIVE", // "REF", // "REFERENCES", // "REFERENCING", // "RELEASE", // "REPEAT", // "RESIGNAL", // "RESULT", // "RETURN", // "RETURNS", // "REVOKE", // "RIGHT", // "ROLLBACK", // "ROLLUP", // "ROW", // "ROWS", // "SAVEPOINT", // "SCOPE", // "SCROLL", // "SEARCH", // "SECOND", // "SELECT", // "SENSITIVE", // "SESSION_USER", // "SET", // "SIGNAL", // "SIMILAR", // "SMALLINT", // "SOME", // "SPECIFIC", // "SPECIFICTYPE", // "SQL", // "SQLEXCEPTION", // "SQLSTATE", // "SQLWARNING", // "START", // "STATIC", // "SUBMULTISET", // "SYMMETRIC", // "SYSTEM", // "SYSTEM_USER", // "TABLE", // "TABLESAMPLE", // "THEN", // "TIME", // "TIMESTAMP", // "TIMEZONE_HOUR", // "TIMEZONE_MINUTE", // "TO", // "TRAILING", // "TRANSLATION", // "TREAT", // "TRIGGER", // "TRUE", // "UNDO", // "UNION", // "UNIQUE", // "UNKNOWN", // "UNNEST", // "UNTIL", // "UPDATE", "USER", // "USING", // "VALUE", // "VALUES", // "VARCHAR", // "VARYING", // "WHEN", // "WHENEVER", // "WHERE", // "WHILE", // "WINDOW", // "WITH", // "WITHIN", // "WITHOUT", // "YEAR", // "ALIAS", // "AUTOCOMMIT", // "CACHED", // "CHECKPOINT", // "EXPLAIN", // "IGNORECASE", // "INDEX", // "LOGSIZE", // "MATCHED", // "MAXROWS", // "MEMORY", // "MINUS", // "NEXT", // "OPENBRACKET", "PASSWORD" // "PLAN", // "PROPERTY", // "READONLY", // "REFERENTIAL_INTEGRITY", // "RENAME", // "RESTART", // "SCRIPT", // "SCRIPTFORMAT", // "SEMICOLON", // "SEQUENCE", // "SHUTDOWN", // "SOURCE", // "TEMP", // "TEXT", // "VIEW", // "WRITE_DELAY", // "VAR_POP", // "VAR_SAMP", // "STDDEV_POP", // "STDDEV_SAMP", // "DEFRAG", // "INCREMENT", // "TOCHAR", // "DATABASE", // "SCHEMA", // "ROLE", // "DOW", // "INITIAL" ); public boolean supportsInitiallyDeferrableColumns() { return false; } public String getCurrentDateTimeFunction() { if (currentDateTimeFunction != null) { return currentDateTimeFunction; } return "NOW()"; } }