package liquibase.database.core; import liquibase.database.AbstractDatabase; import liquibase.database.DatabaseConnection; import liquibase.exception.DatabaseException; import liquibase.statement.DatabaseFunction; import java.lang.reflect.Method; import java.sql.Connection; import java.util.Arrays; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.logging.Level; import java.util.logging.Logger; import java.util.regex.Pattern; /** * Encapsulates Oracle database support. */ public class OracleDatabase extends AbstractDatabase { public static final String PRODUCT_NAME = "oracle"; private Logger log = Logger.getLogger(getClass().getName()); private Set<String> reservedWords = new HashSet<String>(); public OracleDatabase() { // Setting list of Oracle's native functions databaseFunctions.add(new DatabaseFunction("SYSDATE")); databaseFunctions.add(new DatabaseFunction("SYSTIMESTAMP")); databaseFunctions.add(new DatabaseFunction("CURRENT_TIMESTAMP")); } public int getPriority() { return PRIORITY_DEFAULT; } @Override public void setConnection(DatabaseConnection conn) { try { Method wrappedConn = conn.getClass().getMethod("getWrappedConnection"); wrappedConn.setAccessible(true); Connection sqlConn = (Connection)wrappedConn.invoke(conn); Method method = sqlConn.getClass().getMethod("setRemarksReporting", Boolean.TYPE); method.setAccessible(true); method.invoke(sqlConn, true); reservedWords.addAll(Arrays.asList(sqlConn.getMetaData().getSQLKeywords().toUpperCase().split(",\\s*"))); reservedWords.addAll(Arrays.asList("USER", "SESSION","RESOURCE")); //more reserved words not returned by driver } catch (Exception e) { log.info("Could not set remarks reporting on OracleDatabase: "+e.getMessage()); ; //cannot set it. That is OK } super.setConnection(conn); } public String getTypeName() { return "oracle"; } @Override public String generatePrimaryKeyName(String tableName) { if (tableName.length() > 27) { return "PK_" + tableName.toUpperCase().substring(0, 27); } else { return "PK_" + tableName.toUpperCase(); } } public boolean supportsInitiallyDeferrableColumns() { return true; } @Override public String escapeDatabaseObject(String objectName) { // escape the object name if it contains any non-word characters if (objectName != null && (Pattern.compile("\\W").matcher(objectName).find() || isReservedWord(objectName))) { return "\"" + objectName.trim().toUpperCase() + "\""; } else { return objectName; } } @Override public boolean isReservedWord(String objectName) { return reservedWords.contains(objectName.toUpperCase()); } @Override public boolean supportsSequences() { return true; } public boolean isCorrectDatabaseImplementation(DatabaseConnection conn) throws DatabaseException { return PRODUCT_NAME.equalsIgnoreCase(conn.getDatabaseProductName()); } public String getDefaultDriver(String url) { if (url.startsWith("jdbc:oracle")) { return "oracle.jdbc.OracleDriver"; } return null; } public String getCurrentDateTimeFunction() { if (currentDateTimeFunction != null) { return currentDateTimeFunction; } return "SYSTIMESTAMP"; } @Override protected String getDefaultDatabaseSchemaName() throws DatabaseException {//NOPMD return super.getDefaultDatabaseSchemaName().toUpperCase(); } @Override public String escapeIndexName(String schemaName, String indexName) { String escapedIndexName = indexName; if (schemaName != null) { escapedIndexName = schemaName + "." + escapedIndexName; } return escapedIndexName; } /** * Return an Oracle date literal with the same value as a string formatted using ISO 8601. * <p/> * Convert an ISO8601 date string to one of the following results: * to_date('1995-05-23', 'YYYY-MM-DD') * to_date('1995-05-23 09:23:59', 'YYYY-MM-DD HH24:MI:SS') * <p/> * Implementation restriction: * Currently, only the following subsets of ISO8601 are supported: * YYYY-MM-DD * YYYY-MM-DDThh:mm:ss */ @Override public String getDateLiteral(String isoDate) { String normalLiteral = super.getDateLiteral(isoDate); if (isDateOnly(isoDate)) { StringBuffer val = new StringBuffer(); val.append("to_date("); val.append(normalLiteral); val.append(", 'YYYY-MM-DD')"); return val.toString(); } else if (isTimeOnly(isoDate)) { StringBuffer val = new StringBuffer(); val.append("to_date("); val.append(normalLiteral); val.append(", 'HH24:MI:SS')"); return val.toString(); } else if (isDateTime(isoDate)) { normalLiteral = normalLiteral.substring(0, normalLiteral.lastIndexOf('.'))+"'"; StringBuffer val = new StringBuffer(26); val.append("to_date("); val.append(normalLiteral); val.append(", 'YYYY-MM-DD HH24:MI:SS')"); return val.toString(); } else { return "UNSUPPORTED:" + isoDate; } } @Override public boolean isSystemTable(String catalogName, String schemaName, String tableName) { if (super.isSystemTable(catalogName, schemaName, tableName)) { return true; } else if (tableName.startsWith("BIN$")) { //oracle deleted table return true; } else if (tableName.startsWith("AQ$")) { //oracle AQ tables return true; } else if (tableName.startsWith("DR$")) { //oracle index tables return true; } else if (tableName.startsWith("SYS_IOT_OVER")) { //oracle system table return true; } return false; } @Override public boolean shouldQuoteValue(String value) { return super.shouldQuoteValue(value) && !value.startsWith("to_date(") && !value.equalsIgnoreCase(getCurrentDateTimeFunction()); } public boolean supportsTablespaces() { return true; } @Override public boolean supportsAutoIncrement() { return false; } // public Set<UniqueConstraint> findUniqueConstraints(String schema) throws DatabaseException { // Set<UniqueConstraint> returnSet = new HashSet<UniqueConstraint>(); // // List<Map> maps = new Executor(this).queryForList(new RawSqlStatement("SELECT UC.CONSTRAINT_NAME, UCC.TABLE_NAME, UCC.COLUMN_NAME FROM USER_CONSTRAINTS UC, USER_CONS_COLUMNS UCC WHERE UC.CONSTRAINT_NAME=UCC.CONSTRAINT_NAME AND CONSTRAINT_TYPE='U' ORDER BY UC.CONSTRAINT_NAME")); // // UniqueConstraint constraint = null; // for (Map map : maps) { // if (constraint == null || !constraint.getName().equals(constraint.getName())) { // returnSet.add(constraint); // Table table = new Table((String) map.get("TABLE_NAME")); // constraint = new UniqueConstraint(map.get("CONSTRAINT_NAME").toString(), table); // } // } // if (constraint != null) { // returnSet.add(constraint); // } // // return returnSet; // } @Override public boolean supportsRestrictForeignKeys() { return false; } }