All Downloads are FREE. Search and download functionalities are using the official Maven repository.

liquibase.database.core.HsqlDatabase Maven / Gradle / Ivy

There is a newer version: 4.30.0
Show newest version
package liquibase.database.core;

import liquibase.database.AbstractJdbcDatabase;
import liquibase.database.DatabaseConnection;
import liquibase.database.ObjectQuotingStrategy;
import liquibase.exception.DatabaseException;
import liquibase.exception.DateParseException;
import liquibase.structure.DatabaseObject;
import liquibase.structure.core.Table;
import liquibase.util.ISODateFormat;

import java.math.BigInteger;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;

import static liquibase.statement.DatabaseFunction.CURRENT_DATE_TIME_PLACE_HOLDER;

public class HsqlDatabase extends AbstractJdbcDatabase {

    private static final Map> SUPPORTED_DEFAULT_VALUE_COMPUTED_MAP;
    private static final String START_CONCAT = "CONCAT(";
    private static final String END_CONCAT = ")";
    private static final String SEP_CONCAT = ", ";
    private static final List keywords = Arrays.asList(
            "ADD",
            "ALL",
            "ALLOCATE",
            "ALTER",
            "AND",
            "ANY",
            "ARE",
            "ARRAY",
            "AS",
            "ASENSITIVE",
            "ASYMMETRIC",
            "AT",
            "ATOMIC",
            "AUTHORIZATION",
            "AVG",
            "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",
            "COUNT",
            "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",
            "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",
            "HAVING",
            "HOLD",
            "HOUR",
            "IDENTITY",
            "IF",
            "IMMEDIATE",
            "IN",
            "INDICATOR",
            "INNER",
            "INOUT",
            "INPUT",
            "INSENSITIVE",
            "INSERT",
            "INT",
            "INTEGER",
            "INTERSECT",
            "INTERVAL",
            "INTO",
            "IS",
            "ITERATE",
            "JOIN",
            "LANGUAGE",
            "LARGE",
            "LEADING",
            "LEAVE",
            "LEFT",
            "LIKE",
            "LOCAL",
            "LOCALTIME",
            "LOCALTIMESTAMP",
            "LOOP",
            "MATCH",
            "MAX",
            "MEMBER",
            "MERGE",
            "METHOD",
            "MIN",
            "MINUTE",
            "MODIFIES",
            "MODULE",
            "MONTH",
            "MULTISET",
            "NATIONAL",
            "NATURAL",
            "NCHAR",
            "NCLOB",
            "NEW",
            "NO",
            "NONE",
            "NOT",
            "NULL",
            "NUMERIC",
            "OF",
            "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",
            "SELECT",
            "SENSITIVE",
            "SESSION_USER",
            "SET",
            "SIGNAL",
            "SIMILAR",
            "SMALLINT",
            "SOME",
            "SPECIFIC",
            "SPECIFICTYPE",
            "SQL",
            "SQLEXCEPTION",
            "SQLSTATE",
            "SQLWARNING",
            "START",
            "STATIC",
            "SUBMULTISET",
            "SUM",
            "SYMMETRIC",
            "SYSTEM",
            "SYSTEM_USER",
            "TABLE",
            "TABLESAMPLE",
            "THEN",
            "TIME",
            "TIMESTAMP",
            "TIMEZONE_HOUR",
            "TIMEZONE_MINUTE",
            "TO",
            "TRAILING",
            "TREAT",
            "TRIGGER",
            "TRUE",
            "UNDO",
            "UNION",
            "UNIQUE",
            "UNNEST",
            "UNTIL",
            "UPDATE",
            "USER",
            "USING",
            "VALUE",
            "VALUES",
            "VARCHAR",
            "VARYING",
            "WHEN",
            "WHENEVER",
            "WHERE",
            "WHILE",
            "WINDOW",
            "WITHIN",
            "WITHOUT",
            "YEAR",
            "ALIAS",
            "AUTOCOMMIT",
            "CACHED",
            "CHECKPOINT",
            "EXPLAIN",
            "IGNORECASE",
            "INDEX",
            "LOGSIZE",
            "MATCHED",
            "MAXROWS",
            "MEMORY",
            "MINUS",
            "NEXT",
            "OPENBRACKET",
            "PLAN",
            "PROPERTY",
            "READONLY",
            "REFERENTIAL_INTEGRITY",
            "RENAME",
            "RESTART",
            "SCRIPT",
            "SCRIPTFORMAT",
            "SEMICOLON",
            "SHUTDOWN",
            "TEMP",
            "TEXT",
            "VIEW",
            "WRITE_DELAY",
            "VAR_POP",
            "VAR_SAMP",
            "STDDEV_POP",
            "STDDEV_SAMP",
            "DEFRAG",
            "INCREMENT",
            "TOCHAR",
            "DATABASE",
            "SCHEMA",
            "ROLE",
            "DOW",
            "INITIAL");

    static {
        Map> tempMap = new HashMap<>();
        tempMap.put("datetime", new HashSet<>(Arrays.asList("CURRENT_DATE", "CURRENT_TIME", "CURRENT_TIMESTAMP", "TODAY", "NOW", CURRENT_DATE_TIME_PLACE_HOLDER)));
        SUPPORTED_DEFAULT_VALUE_COMPUTED_MAP = Collections.unmodifiableMap(tempMap);
    }

    private Boolean oracleSyntax;

    public HsqlDatabase() {
        super.unquotedObjectsAreUppercased = true;
        super.setCurrentDateTimeFunction("NOW");
        super.sequenceNextValueFunction = "NEXT VALUE FOR %s";
        super.defaultAutoIncrementStartWith = BigInteger.ZERO;
        super.sequenceCurrentValueFunction = "CURRVAL('%s')";
    }

    /**
     * Checks to see if the string is an acceptable computed value for HSQL
     * "datetime" columns are the only columns for which HSQL supports computer values.
     *
     * @param columnType   String of the column's data type
     * @param defaultValue String to be checked for valid valueComputed in HSQL
     * @return boolean True if the string represents a function supported by HSQL for default values
     */
    public static boolean supportsDefaultValueComputed(String columnType, String defaultValue) {
        HashSet possibleComputedValues = SUPPORTED_DEFAULT_VALUE_COMPUTED_MAP.get(columnType);
        return (possibleComputedValues != null) && possibleComputedValues.contains(defaultValue.toLowerCase());
    }

    @Override
    public boolean isCorrectDatabaseImplementation(DatabaseConnection conn) throws DatabaseException {
        return "HSQL Database Engine".equalsIgnoreCase(conn.getDatabaseProductName());
    }

    @Override
    public String getDefaultDriver(String url) {
        if (url.startsWith("jdbc:hsqldb:")) {
            return "org.hsqldb.jdbcDriver";
        }
        return null;
    }

    @Override
    public Integer getDefaultPort() {
        return 9001;
    }

    @Override
    protected String getDefaultDatabaseProductName() {
        return "HyperSQL";
    }

    @Override
    public int getPriority() {
        return PRIORITY_DEFAULT;
    }

    @Override
    public String getShortName() {
        return "hsqldb";
    }

    @Override
    public boolean supportsSequences() {
        return true;
    }

    @Override
    public boolean supportsInitiallyDeferrableColumns() {
        return false;
    }

    @Override
    public boolean supportsCatalogs() {
        try {
            return getDatabaseMajorVersion() >= 2;
        } catch (DatabaseException e) {
            return true;
        }
    }

    @Override
    protected String getConnectionCatalogName() throws DatabaseException {
        if (supportsCatalogs()) {
            return "PUBLIC";
        } else {
            return null;
        }

    }

    @Override
    protected String getConnectionSchemaName() {
        return "PUBLIC";
    }

    @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 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.SSS");
                returnString = dbTimestampFormat.format(isoTimestampFormat.parse(isoDate));
            }
        } catch (ParseException e) {
            throw new RuntimeException("Unexpected date format: " + isoDate, e);
        }
        return "'" + returnString + "'";
    }

    @Override
    public Date parseDate(String dateAsString) throws DateParseException {
        try {
            if (dateAsString.indexOf(" ") > 0) {
                return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").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 supportsTablespaces() {
        return false;
    }

    @Override
    public boolean isReservedWord(String value) {
        return keywords.contains(value.toUpperCase());
    }

    @Override
    public boolean isCaseSensitive() {
        return false;
    }

    @Override
    public void setConnection(DatabaseConnection conn) {
        oracleSyntax = null;
        super.setConnection(conn);
    }

    public boolean isUsingOracleSyntax() {
        if (oracleSyntax == null) {
            oracleSyntax = Boolean.FALSE;
            if ((getConnection() != null) && (getConnection().getURL() != null)) {
                for (String str : getConnection().getURL().split(";")) {
                    if (str.contains("sql.syntax_ora") && str.contains("=")) {
                        oracleSyntax = Boolean.valueOf(str.split("=")[1].trim());
                        break;
                    }
                }
            }
        }
        return oracleSyntax;
    }


    @Override
    public String escapeObjectName(String objectName, Class objectType) {
        if (quotingStrategy == ObjectQuotingStrategy.QUOTE_ALL_OBJECTS) {
            return super.escapeObjectName(objectName, objectType);
        }
        if ((objectName != null) && (quotingStrategy != ObjectQuotingStrategy.QUOTE_ALL_OBJECTS) && isReservedWord
            (objectName.toUpperCase(Locale.US))) {
                return "\""+objectName.toUpperCase(Locale.US)+"\"";
        }
        return objectName;
    }

    @Override
    public int getMaxFractionalDigitsForTimestamp() {
        // HyperSQL does not seem to specify the exact number of possible fractional digits in its documentation,
        // this value is derived from tests.
        return 9;
    }

    @Override
    public String getAutoIncrementClause(BigInteger startWith, BigInteger incrementBy, String generationType, Boolean defaultOnNull) {
        final String clause = super.getAutoIncrementClause(startWith, incrementBy, generationType, defaultOnNull);
        return clause.replace(",", ""); //sql doesn't use commas between the values
    }

    @Override
    public boolean supportsCreateIfNotExists(Class type) {
        return type.isAssignableFrom(Table.class);
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy