package com.eas.sqldrivers; import com.eas.client.ClientConstants; import com.eas.client.changes.JdbcChangeValue; import com.eas.client.metadata.DbTableIndexColumnSpec; import com.eas.client.metadata.DbTableIndexSpec; import com.eas.client.metadata.JdbcField; import com.eas.client.metadata.ForeignKeySpec; import com.eas.client.metadata.PrimaryKeySpec; import com.eas.client.sqldrivers.SqlDriver; import com.eas.sqldrivers.resolvers.H2TypesResolver; import com.eas.client.sqldrivers.resolvers.TypesResolver; import java.sql.Connection; import java.sql.SQLException; import java.sql.Statement; import java.sql.Wrapper; import java.util.ArrayList; import java.util.List; /** * * @author vv */ public class H2SqlDriver extends SqlDriver { // настройка экранирования наименования объектов БД private static final TwinString[] charsForWrap = {new TwinString("\"", "\""), new TwinString("`", "`")}; private static final char[] restrictedChars = {' ', ',', '\'', '"'}; protected TypesResolver resolver = new H2TypesResolver(); protected static final int[] h2ErrorCodes = {}; protected static final String[] platypusErrorMessages = {}; protected static final String SET_SCHEMA_CLAUSE = "SET SCHEMA %s"; protected static final String GET_SCHEMA_CLAUSE = "SELECT SCHEMA()"; protected static final String CREATE_SCHEMA_CLAUSE = "CREATE SCHEMA IF NOT EXISTS %s"; protected static final String SQL_CREATE_EMPTY_TABLE = "CREATE TABLE %s (%s DECIMAL(18,0) NOT NULL PRIMARY KEY)"; protected static final String SQL_CREATE_TABLE_COMMENT = "COMMENT ON TABLE %s IS '%s'"; protected static final String SQL_CREATE_COLUMN_COMMENT = "COMMENT ON COLUMN %s IS '%s'"; protected static final String SQL_DROP_TABLE = "DROP TABLE %s"; protected static final String SQL_CREATE_INDEX = "CREATE %s INDEX %s ON %s (%s)"; protected static final String SQL_DROP_INDEX = "DROP INDEX %s"; protected static final String SQL_ADD_PK = "ALTER TABLE %s ADD %s PRIMARY KEY (%s)"; protected static final String SQL_DROP_PK = "ALTER TABLE %s DROP PRIMARY KEY"; protected static final String SQL_ADD_FK = "ALTER TABLE %s ADD CONSTRAINT %s FOREIGN KEY (%s) REFERENCES %s (%s) %s"; protected static final String SQL_DROP_FK = "ALTER TABLE %s DROP CONSTRAINT %s"; protected static final String SQL_PARENTS_LIST = "" + "WITH RECURSIVE parents(mdent_id, mdent_parent_id) AS " + "( " + "SELECT m1.mdent_id, m1.mdent_parent_id FROM mtd_entities m1 WHERE m1.mdent_id = %s " + " UNION ALL " + "SELECT m2.mdent_id, m2.mdent_parent_id FROM parents p, mtd_entities m2 WHERE m2.mdent_id = p.mdent_parent_id " + ") " + "SELECT mdent_id, mdent_parent_id FROM parents"; protected static final String SQL_CHILDREN_LIST = "" + "WITH recursive children(mdent_id, mdent_name, mdent_parent_id, mdent_type, mdent_content_txt, mdent_content_txt_size, mdent_content_txt_crc32) AS" + "( " + "SELECT m1.mdent_id, m1.mdent_name, m1.mdent_parent_id, m1.mdent_type, m1.mdent_content_txt, m1.mdent_content_txt_size, m1.mdent_content_txt_crc32 FROM mtd_entities m1 WHERE m1.mdent_id = :%s " + " union all " + "SELECT m2.mdent_id, m2.mdent_name, m2.mdent_parent_id, m2.mdent_type, m2.mdent_content_txt, m2.mdent_content_txt_size, m2.mdent_content_txt_crc32 FROM children c, mtd_entities m2 WHERE c.mdent_id = m2.mdent_parent_id " + ") " + "SELECT mdent_id, mdent_name, mdent_parent_id, mdent_type, mdent_content_txt, mdent_content_txt_size, mdent_content_txt_crc32 FROM children"; protected static final String SQL_RENAME_COLUMN = "ALTER TABLE %s ALTER COLUMN %s RENAME TO %s"; protected static final String SQL_CHANGE_COLUMN_TYPE = "ALTER TABLE %s ALTER COLUMN %s %s"; protected static final String SQL_CHANGE_COLUMN_NULLABLE = "ALTER TABLE %s ALTER COLUMN %s SET %s NULL"; public H2SqlDriver() { super(); } @Override public boolean is(String aDialect) { return ClientConstants.SERVER_PROPERTY_H2_DIALECT.equals(aDialect); } /** * {@inheritDoc} */ @Override public boolean isConstraintsDeferrable() { return false; } /** * {@inheritDoc} */ @Override public TypesResolver getTypesResolver() { return resolver; } /** * {@inheritDoc} */ @Override public String getUsersSpaceInitResourceName() { return "/sqlscripts/H2InitUsersSpace.sql"; } /** * {@inheritDoc} */ @Override public String getVersionInitResourceName() { return "/sqlscripts/H2InitVersion.sql"; } /** * {@inheritDoc} */ @Override public void applyContextToConnection(Connection aConnection, String aSchema) throws Exception { if (aSchema != null && !aSchema.isEmpty()) { try (Statement stmt = aConnection.createStatement()) { stmt.execute(String.format(SET_SCHEMA_CLAUSE, wrapNameIfRequired(aSchema))); } } } @Override public String getSql4GetConnectionContext() { return GET_SCHEMA_CLAUSE; } /** * {@inheritDoc} */ @Override public String getSql4CreateSchema(String aSchemaName, String aPassword) { if (aSchemaName != null && !aSchemaName.isEmpty()) { return String.format(CREATE_SCHEMA_CLAUSE, aSchemaName); } throw new IllegalArgumentException("Schema name is null or empty."); } /** * {@inheritDoc} */ @Override public String[] getSql4CreateColumnComment(String aOwnerName, String aTableName, String aFieldName, String aDescription) { String fullName = wrapNameIfRequired(aTableName) + "." + wrapNameIfRequired(aFieldName); if (aOwnerName != null && !aOwnerName.isEmpty()) { fullName = wrapNameIfRequired(aOwnerName) + "." + fullName; } if (aDescription == null) { aDescription = ""; } return new String[]{String.format(SQL_CREATE_COLUMN_COMMENT, fullName, escapeSingleQuote(aDescription))}; } /** * {@inheritDoc} */ @Override public String getSql4CreateTableComment(String aOwnerName, String aTableName, String aDescription) { String fullName = makeFullName(aOwnerName, aTableName); if (aDescription == null) { aDescription = ""; } return String.format(SQL_CREATE_TABLE_COMMENT, fullName, escapeSingleQuote(aDescription)); } private String escapeSingleQuote(String str) { return str.replaceAll("'", "''"); //NOI18N } /** * {@inheritDoc} */ @Override public String getSql4DropTable(String aSchemaName, String aTableName) { if (aSchemaName != null && !aSchemaName.isEmpty()) { return String.format(SQL_DROP_TABLE, wrapNameIfRequired(aSchemaName) + "." + wrapNameIfRequired(aTableName)); } else { return String.format(SQL_DROP_TABLE, wrapNameIfRequired(aTableName)); } } /** * {@inheritDoc} */ @Override public String getSql4DropIndex(String aSchemaName, String aTableName, String aIndexName) { String indexName = makeFullName(aSchemaName, aIndexName); return String.format(SQL_DROP_INDEX, indexName); } /** * {@inheritDoc} */ @Override public String getSql4DropFkConstraint(String aSchemaName, ForeignKeySpec aFk) { String constraintName = wrapNameIfRequired(aFk.getCName()); String tableName = makeFullName(aSchemaName, aFk.getTable()); return String.format(SQL_DROP_FK, tableName, constraintName); } /** * {@inheritDoc} */ @Override public String[] getSql4CreatePkConstraint(String aSchemaName, List<PrimaryKeySpec> listPk) { if (listPk != null && listPk.size() > 0) { PrimaryKeySpec pk = listPk.get(0); String tableName = pk.getTable(); String pkTableName = makeFullName(aSchemaName, tableName); String pkName = wrapNameIfRequired(generatePkName(tableName, PKEY_NAME_SUFFIX)); String pkColumnName = wrapNameIfRequired(pk.getField()); for (int i = 1; i < listPk.size(); i++) { pk = listPk.get(i); pkColumnName += ", " + wrapNameIfRequired(pk.getField()); } return new String[]{ String.format(SQL_ADD_PK, pkTableName, "CONSTRAINT " + pkName, pkColumnName) }; } return null; } /** * {@inheritDoc} */ @Override public String getSql4DropPkConstraint(String aSchemaName, PrimaryKeySpec aPk) { String pkTableName = makeFullName(aSchemaName, aPk.getTable()); return String.format(SQL_DROP_PK, pkTableName); } /** * {@inheritDoc} */ @Override public String getSql4CreateFkConstraint(String aSchemaName, ForeignKeySpec aFk) { List<ForeignKeySpec> fkList = new ArrayList<>(); fkList.add(aFk); return getSql4CreateFkConstraint(aSchemaName, fkList); } /** * {@inheritDoc} */ @Override public String getSql4CreateFkConstraint(String aSchemaName, List<ForeignKeySpec> listFk) { if (listFk != null && listFk.size() > 0) { ForeignKeySpec fk = listFk.get(0); String fkTableName = makeFullName(aSchemaName, fk.getTable()); String fkName = fk.getCName(); String fkColumnName = wrapNameIfRequired(fk.getField()); PrimaryKeySpec pk = fk.getReferee(); String pkSchemaName = pk.getSchema(); String pkTableName = makeFullName(aSchemaName, pk.getTable()); String pkColumnName = wrapNameIfRequired(pk.getField()); for (int i = 1; i < listFk.size(); i++) { fk = listFk.get(i); pk = fk.getReferee(); fkColumnName += ", " + wrapNameIfRequired(fk.getField()); pkColumnName += ", " + wrapNameIfRequired(pk.getField()); } String fkRule = ""; switch (fk.getFkUpdateRule()) { case CASCADE: fkRule += " ON UPDATE CASCADE"; break; case NOACTION: // case SETDEFAULT: fkRule += " ON UPDATE NO ACTION"; break; case SETDEFAULT: fkRule += " ON UPDATE SET DEFAULT"; break; case SETNULL: fkRule += " ON UPDATE SET NULL"; break; } switch (fk.getFkDeleteRule()) { case CASCADE: fkRule += " ON DELETE CASCADE"; break; case NOACTION: // case SETDEFAULT: fkRule += " ON DELETE NO ACTION"; break; case SETDEFAULT: fkRule += " ON DELETE SET DEFAULT"; break; case SETNULL: fkRule += " ON DELETE SET NULL"; break; } return String.format(SQL_ADD_FK, fkTableName, fkName.isEmpty() ? "" : wrapNameIfRequired(fkName), fkColumnName, pkTableName, pkColumnName, fkRule); } return null; } /** * {@inheritDoc} */ @Override public String getSql4CreateIndex(String aSchemaName, String aTableName, DbTableIndexSpec aIndex) { assert aIndex.getColumns().size() > 0 : "index definition must consist of at least 1 column"; String tableName = makeFullName(aSchemaName, aTableName); String fieldsList = ""; for (int i = 0; i < aIndex.getColumns().size(); i++) { DbTableIndexColumnSpec column = aIndex.getColumns().get(i); fieldsList += wrapNameIfRequired(column.getColumnName()); if (!column.isAscending()) { fieldsList += " DESC"; } if (i != aIndex.getColumns().size() - 1) { fieldsList += ", "; } } return String.format(SQL_CREATE_INDEX, (aIndex.isUnique() ? "UNIQUE " : "") + (aIndex.isHashed() ? "HASH " : ""), wrapNameIfRequired(aIndex.getName()), tableName, fieldsList); } /** * {@inheritDoc} */ @Override public String getSql4EmptyTableCreation(String aSchemaName, String aTableName, String aPkFieldName) { String fullName = makeFullName(aSchemaName, aTableName); return String.format(SQL_CREATE_EMPTY_TABLE, fullName, wrapNameIfRequired(aPkFieldName)); } /** * {@inheritDoc} */ @Override public String parseException(Exception ex) { if (ex != null && ex instanceof SQLException) { SQLException sqlEx = (SQLException) ex; int errorCode = sqlEx.getErrorCode(); for (int i = 0; i < h2ErrorCodes.length; i++) { if (errorCode == h2ErrorCodes[i]) { return platypusErrorMessages[i]; } } } return ex.getLocalizedMessage(); } private String getFieldTypeDefinition(JdbcField aField) { String typeDefine = ""; String sqlTypeName = aField.getType().toLowerCase(); typeDefine += sqlTypeName; // field length int size = aField.getSize(); int scale = aField.getScale(); if (resolver.isScaled(sqlTypeName) && size > 0) { typeDefine += "(" + String.valueOf(size) + "," + String.valueOf(scale) + ")"; } else { if (resolver.isSized(sqlTypeName) && size > 0) { typeDefine += "(" + String.valueOf(size) + ")"; } } return typeDefine; } /** * {@inheritDoc} */ @Override public String getSql4FieldDefinition(JdbcField aField) { String fieldDefinition = wrapNameIfRequired(aField.getName()) + " " + getFieldTypeDefinition(aField); if (!aField.isNullable()) { fieldDefinition += " NOT NULL"; } else { fieldDefinition += " NULL"; } if (aField.isPk()) { fieldDefinition += " PRIMARY KEY"; } return fieldDefinition; } /** * {@inheritDoc} */ @Override public String[] getSqls4ModifyingField(String aSchemaName, String aTableName, JdbcField aOldFieldMd, JdbcField aNewFieldMd) { assert aOldFieldMd.getName().toLowerCase().equals(aNewFieldMd.getName().toLowerCase()); List<String> sql = new ArrayList<>(); //Change data type String lOldTypeName = aOldFieldMd.getType(); if (lOldTypeName == null) { lOldTypeName = ""; } String lNewTypeName = aNewFieldMd.getType(); if (lNewTypeName == null) { lNewTypeName = ""; } String fullTableName = makeFullName(aSchemaName, aTableName); if (!lOldTypeName.equalsIgnoreCase(lNewTypeName) || aOldFieldMd.getSize() != aNewFieldMd.getSize() || aOldFieldMd.getScale() != aNewFieldMd.getScale()) { sql.add(String.format( SQL_CHANGE_COLUMN_TYPE, fullTableName, wrapNameIfRequired(aOldFieldMd.getName()), getFieldTypeDefinition(aNewFieldMd))); } //Change nullable String not = ""; if (aOldFieldMd.isNullable() != aNewFieldMd.isNullable()) { if (!aNewFieldMd.isNullable()) { not = "NOT"; } sql.add(String.format( SQL_CHANGE_COLUMN_NULLABLE, fullTableName, wrapNameIfRequired(aOldFieldMd.getName()), not)); } return sql.toArray(new String[0]); } /** * {@inheritDoc} */ @Override public String[] getSqls4RenamingField(String aSchemaName, String aTableName, String aOldFieldName, JdbcField aNewFieldMd) { String fullTableName = makeFullName(aSchemaName, aTableName); String renameSQL = String.format(SQL_RENAME_COLUMN, fullTableName, wrapNameIfRequired(aOldFieldName), wrapNameIfRequired(aNewFieldMd.getName())); return new String[]{renameSQL}; } @Override public String[] getSqls4AddingField(String aSchemaName, String aTableName, JdbcField aField) { String fullTableName = makeFullName(aSchemaName, aTableName); return new String[]{ String.format(SqlDriver.ADD_FIELD_SQL_PREFIX, fullTableName) + getSql4FieldDefinition(aField) }; } @Override public TwinString[] getCharsForWrap() { return charsForWrap; } @Override public char[] getRestrictedChars() { return restrictedChars; } @Override public boolean isHadWrapped(String aName) { return isHaveLowerCase(aName); } private String prepareName(String aName) { return (isWrappedName(aName) ? unwrapName(aName) : aName.toUpperCase()); } @Override public JdbcChangeValue convertGeometry(String aValue, Connection aConnection) throws SQLException { return null; } @Override public String readGeometry(Wrapper aRs, int aColumnIndex, Connection aConnection) throws SQLException { return null; } }