net.java.ao.db.NuoDBDatabaseProvider Maven / Gradle / Ivy
package net.java.ao.db;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import net.java.ao.ActiveObjectsException;
import net.java.ao.DatabaseProvider;
import net.java.ao.DisposableDataSource;
import net.java.ao.schema.IndexNameConverter;
import net.java.ao.schema.NameConverters;
import net.java.ao.schema.UniqueNameConverter;
import net.java.ao.schema.ddl.DDLField;
import net.java.ao.schema.ddl.DDLForeignKey;
import net.java.ao.schema.ddl.DDLIndex;
import net.java.ao.schema.ddl.DDLIndexField;
import net.java.ao.schema.ddl.DDLTable;
import net.java.ao.schema.ddl.SQLAction;
import net.java.ao.types.TypeManager;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import static java.sql.Connection.TRANSACTION_READ_COMMITTED;
import static java.sql.Types.OTHER;
import static net.java.ao.Common.closeQuietly;
/**
* @author Philip Stoev
*/
public class NuoDBDatabaseProvider extends DatabaseProvider {
public NuoDBDatabaseProvider(DisposableDataSource dataSource) {
this(dataSource, null);
}
public NuoDBDatabaseProvider(DisposableDataSource dataSource, String schema) {
super(dataSource, schema, TypeManager.nuodb());
}
/**
* Workaround for DB-7451 transaction isolation level SERIALIZABLE will not show tables created or altered with
* auto-commit off.
*
* @param conn The connection to use in retrieving the database tables.
* @return
* @throws SQLException
*/
@Override
public ResultSet getTables(Connection conn) throws SQLException {
int transactionIsolation = conn.getTransactionIsolation();
try {
conn.setTransactionIsolation(TRANSACTION_READ_COMMITTED);
return conn.getMetaData().getTables(null, getSchema(), null, new String[]{"TABLE"});
} finally {
conn.setTransactionIsolation(transactionIsolation);
}
}
@Override
protected String renderAutoIncrement() {
return "GENERATED BY DEFAULT AS IDENTITY";
}
@Override
protected RenderFieldOptions renderFieldOptionsInAlterColumn() {
return new RenderFieldOptions(false, false, true, true);
}
@Override
protected Iterable renderAlterTableChangeColumn(
NameConverters nameConverters, DDLTable table, DDLField oldField,
DDLField field) {
final ImmutableList.Builder back = ImmutableList.builder();
back.addAll(renderDropAccessoriesForField(nameConverters, table, oldField));
//Store if the field wants to be auto-increment, but turn it off. NuoDB does not support adding auto-increment on existing columns.
boolean needsAutoIncrement = field.isAutoIncrement();
field.setAutoIncrement(false);
//If we are changing the JDBC Type, we need to drop the primary index
if (oldField.isPrimaryKey() && oldField.getJdbcType() != field.getJdbcType()) {
back.add(findAndRenderDropUniqueIndex(nameConverters, table, field, true));
}
//Add alter statements for everything but unique.
back.add(renderAlterTableChangeColumnStatement(nameConverters, table, oldField, field, new RenderFieldOptions(false, true, true)));
//Need to drop default if necessary
if (oldField.getDefaultValue() != null && field.getDefaultValue() == null) {
back.add(SQLAction.of("ALTER TABLE " + withSchema(table.getName()) + " ALTER " + field.getName() + " DROP DEFAULT"));
}
//Need to drop null if necessary
if (oldField.isNotNull() && !field.isNotNull()) {
back.add(SQLAction.of("ALTER TABLE " + withSchema(table.getName()) + " ALTER " + field.getName() + " NULL"));
}
if (oldField.isUnique() && !field.isUnique()) {
// SchemaReader filters out unique indices; so use the expected name patterns to try to find it, and drop it
back.add(findAndRenderDropUniqueIndex(nameConverters, table, field, false));
}
if (!oldField.isUnique() && field.isUnique()) {
back.add(renderUniqueIndex(nameConverters.getIndexNameConverter(), table.getName(), field.getName()));
}
if (field.isPrimaryKey() && oldField.getJdbcType() != field.getJdbcType()) {
//If the type changed, and this field is the primary key, we need to redefine it.
//alter table AO_000000_ENTITY add primary key (id) ;
back.add(SQLAction.of("ALTER TABLE " + withSchema(table.getName()) + " ADD PRIMARY KEY (" + field.getName() + ")"));
}
back.addAll(renderAccessoriesForField(nameConverters, table, field));
return back.build();
}
/**
* Drop indices on field being altered.
*/
@Override
protected Iterable renderDropAccessoriesForField(
final NameConverters nameConverters,
final DDLTable table,
final DDLField field
) {
return Stream.of(table.getIndexes())
.filter(index -> index.containsFieldWithNameIgnoreCase(field.getName()))
.map(index -> renderDropIndex(nameConverters.getIndexNameConverter(), index))
.collect(Collectors.toList());
}
/**
* Create indices on field being altered.
*/
@Override
protected Iterable renderAccessoriesForField(
final NameConverters nameConverters,
final DDLTable table,
final DDLField field
) {
return Stream.of(table.getIndexes())
.filter(index -> index.containsFieldWithNameIgnoreCase(field.getName()))
.map(index -> renderCreateIndex(nameConverters.getIndexNameConverter(), index))
.collect(Collectors.toList());
}
private SQLAction findAndRenderDropUniqueIndex(
NameConverters nameConverters, DDLTable table, DDLField field,
boolean isPrimary) {
String indexName = findUniqueIndex(table.getName(), field.getName(), isPrimary);
if (indexName == null || indexName.isEmpty()) {
logger.error("Unable to find unique index for field {} in table {}", field.getName(), table.getName());
}
DDLIndex index = DDLIndex.builder()
.field(DDLIndexField.builder().fieldName(field.getName()).build())
.table(table.getName())
.indexName(indexName)
.build();
SQLAction renderDropIndex = renderDropIndex(nameConverters.getIndexNameConverter(), index);
return renderDropIndex;
}
private String findUniqueIndex(String table, String field, boolean returnPrimary) {
Connection connection = null;
try {
connection = getConnection();
ResultSet indexes = getIndexes(connection, table);
while (indexes.next()) {
if (field.equalsIgnoreCase(indexes.getString("COLUMN_NAME"))) {
String name = indexes.getString("INDEX_NAME");
if (returnPrimary == name.contains("PRIMARY")) {
return indexes.getString("INDEX_NAME");
}
}
}
return null;
} catch (SQLException e) {
throw new ActiveObjectsException(e);
} finally {
closeQuietly(connection);
}
}
@Override
protected SQLAction renderCreateIndex(IndexNameConverter indexNameConverter, DDLIndex index) {
String statement = "CREATE" + (index.isUnique() ? " UNIQUE" : "") + " INDEX " + index.getIndexName()
+ " ON " + withSchema(index.getTable()) +
Stream.of(index.getFields())
.map(DDLIndexField::getFieldName)
.map(this::processID)
.collect(Collectors.joining(",", "(", ")"));
return SQLAction.of(statement);
}
private SQLAction renderUniqueIndex(IndexNameConverter indexNameConverter, String table, String field) {
return SQLAction.of("CREATE UNIQUE INDEX " + indexNameConverter.getName(shorten(table), shorten(field))
+ " ON " + withSchema(table) + "(" + processID(field) + ")");
}
@Override
protected SQLAction renderDropIndex(IndexNameConverter indexNameConverter,
DDLIndex index) {
final String indexName = index.getIndexName();
final String tableName = index.getTable();
if (hasIndex(tableName, indexName)) {
return SQLAction.of("DROP INDEX " + withSchema("\"" + indexName + "\""));
} else {
return null;
}
}
@Override
protected String renderConstraintsForTable(UniqueNameConverter uniqueNameConverter, DDLTable table) {
StringBuilder back = new StringBuilder();
for (DDLForeignKey key : table.getForeignKeys()) {
back.append(" ").append(renderForeignKey(key));
if (!disableForeignKey(key)) {
back.append(",");
}
back.append("\n");
}
return back.toString();
}
/**
* Foreign keys which reference primary table aren't supported because of a open issue and are rendered in
* commentary
* block.
*
* @param key The database-agnostic foreign key representation.
* @return
*/
@Override
protected String renderForeignKey(DDLForeignKey key) {
StringBuilder back = new StringBuilder();
if (disableForeignKey(key)) {
back.append("/* DISABLE ");
}
back.append("FOREIGN KEY (").append(processID(key.getField())).append(") REFERENCES ");
back.append(withSchema(key.getTable())).append('(').append(processID(key.getForeignField())).append(")");
if (disableForeignKey(key)) {
back.append(" */");
}
return back.toString();
}
private boolean disableForeignKey(DDLForeignKey key) {
return key.getTable().equals(key.getDomesticTable());
}
@Override
protected SQLAction renderAlterTableDropKey(DDLForeignKey key) {
StringBuilder sql = new StringBuilder();
sql.append("ALTER TABLE ").append(withSchema(key.getDomesticTable()));
sql.append(" DROP FOREIGN KEY (").append(processID(key.getField())).append(") REFERENCES ");
sql.append(withSchema(key.getTable()));
return SQLAction.of(sql);
}
@Override
protected void loadQuoteString() {
quoteRef.set("`");
}
@Override
public Object parseValue(int type, String value) {
if (value != null) {
Matcher matcher = Pattern.compile("'(.*)'.*").matcher(value);
if (matcher.find()) {
value = matcher.group(1);
}
if (value.isEmpty()) value = null;
}
return super.parseValue(type, value);
}
@Override
protected Set getReservedWords() {
return RESERVED_WORDS;
}
/**
* The sql type argument is mandatory in JDBC for some reason, but is ignored by NuoDB JDBC,
* so any integer value can be used.
*
* @param stmt The statement in which to store the NULL
value.
* @param index The index of the parameter which should be assigned NULL
.
* @throws SQLException
*/
@Override
public void putNull(PreparedStatement stmt, int index) throws SQLException {
stmt.setNull(index, OTHER);
}
public static final Set RESERVED_WORDS = ImmutableSet.of(
"ACCESS", "ACCOUNT", "ACTIVATE", "ADD", "ADMIN", "ADVISE", "AFTER",
"ALL", "ALL_ROWS", "ALLOCATE", "ALTER", "ANALYZE", "AND", "ANY", "ARCHIVE",
"ARCHIVELOG", "ARRAY", "AS", "ASC", "AT", "AUDIT", "AUTHENTICATED", "AUTHORIZATION",
"AUTOEXTEND", "AUTOMATIC", "BACKUP", "BECOME", "BEFORE", "BEGIN", "BETWEEN",
"BFILE", "BITMAP", "BLOB", "BLOCK", "BODY", "BY", "CACHE", "CACHE_INSTANCES",
"CANCEL", "CASCADE", "CAST", "CFILE", "CHAINED", "CHANGE", "CHAR", "CHAR_CS",
"CHARACTER", "CHECK", "CHECKPOINT", "CHOOSE", "CHUNK", "CLEAR", "CLOB", "CLONE",
"CLOSE", "CLOSE_CACHED_OPEN_CURSORS", "CLUSTER", "COALESCE", "COLUMN", "COLUMNS",
"COMMENT", "COMMIT", "COMMITTED", "COMPATIBILITY", "COMPILE", "COMPLETE",
"COMPOSITE_LIMIT", "COMPRESS", "COMPUTE", "CONNECT", "CONNECT_TIME", "CONSTRAINT",
"CONSTRAINTS", "CONTENTS", "CONTINUE", "CONTROLFILE", "CONVERT", "COST",
"CPU_PER_CALL", "CPU_PER_SESSION", "CREATE", "CURRENT", "CURRENT_SCHEMA",
"CURREN_USER", "CURSOR", "CYCLE", "DANGLING", "DATABASE", "DATAFILE", "DATAFILES",
"DATAOBJNO", "DATE", "DBA", "DBHIGH", "DBLOW", "DBMAC", "DEALLOCATE", "DEBUG",
"DEC", "DECIMAL", "DECLARE", "DEFAULT", "DEFERRABLE", "DEFERRED", "DEGREE",
"DELETE", "DEREF", "DESC", "DIRECTORY", "DISABLE", "DISCONNECT", "DISMOUNT",
"DISTINCT", "DISTRIBUTED", "DML", "DOUBLE", "DROP", "DUMP", "EACH", "ELSE",
"ENABLE", "END", "ENFORCE", "ENTRY", "ESCAPE", "EXCEPT", "EXCEPTIONS", "EXCHANGE",
"EXCLUDING", "EXCLUSIVE", "EXECUTE", "EXISTS", "EXPIRE", "EXPLAIN", "EXTENT",
"EXTENTS", "EXTERNALLY", "FAILED_LOGIN_ATTEMPTS", "FALSE", "FAST", "FILE",
"FIRST_ROWS", "FLAGGER", "FLOAT", "FLOB", "FLUSH", "FOR", "FORCE", "FOREIGN",
"FREELIST", "FREELISTS", "FROM", "FULL", "FUNCTION", "GLOBAL", "GLOBALLY",
"GLOBAL_NAME", "GRANT", "GROUP", "GROUPS", "HASH", "HASHKEYS", "HAVING", "HEADER",
"HEAP", "IDENTIFIED", "IDGENERATORS", "IDLE_TIME", "IF", "IMMEDIATE", "IN",
"INCLUDING", "INCREMENT", "INDEX", "INDEXED", "INDEXES", "INDICATOR",
"IND_PARTITION", "INITIAL", "INITIALLY", "INITRANS", "INSERT", "INSTANCE",
"INSTANCES", "INSTEAD", "INT", "INTEGER", "INTERMEDIATE", "INTERSECT", "INTO",
"IS", "ISOLATION", "ISOLATION_LEVEL", "KEEP", "KEY", "KILL", "LABEL", "LAYER",
"LESS", "LEVEL", "LIBRARY", "LIKE", "LIMIT", "LINK", "LIST", "LOB", "LOCAL",
"LOCK", "LOCKED", "LOG", "LOGFILE", "LOGGING", "LOGICAL_READS_PER_CALL",
"LOGICAL_READS_PER_SESSION", "LONG", "MANAGE", "MASTER", "MAX", "MAXARCHLOGS",
"MAXDATAFILES", "MAXEXTENTS", "MAXINSTANCES", "MAXLOGFILES", "MAXLOGHISTORY",
"MAXLOGMEMBERS", "MAXSIZE", "MAXTRANS", "MAXVALUE", "MIN", "MEMBER", "MINIMUM",
"MINEXTENTS", "MINUS", "MINVALUE", "MLSLABEL", "MLS_LABEL_FORMAT", "MODE",
"MODIFY", "MOUNT", "MOVE", "MTS_DISPATCHERS", "MULTISET", "NATIONAL", "NCHAR",
"NCHAR_CS", "NCLOB", "NEEDED", "NESTED", "NETWORK", "NEW", "NEXT",
"NOARCHIVELOG", "NOAUDIT", "NOCACHE", "NOCOMPRESS", "NOCYCLE", "NOFORCE",
"NOLOGGING", "NOMAXVALUE", "NOMINVALUE", "NONE", "NOORDER", "NOOVERRIDE",
"NOPARALLEL", "NOPARALLEL", "NOREVERSE", "NORMAL", "NOSORT", "NOT", "NOTHING",
"NOWAIT", "NULL", "NUMBER", "NUMERIC", "NVARCHAR2", "OBJECT", "OBJNO",
"OBJNO_REUSE", "OF", "OFF", "OFFLINE", "OID", "OIDINDEX", "OLD", "ON", "ONLINE",
"ONLY", "OPCODE", "OPEN", "OPTIMAL", "OPTIMIZER_GOAL", "OPTION", "OR", "ORDER",
"ORGANIZATION", "OSLABEL", "OVERFLOW", "OWN", "PACKAGE", "PARALLEL", "PARTITION",
"PASSWORD", "PASSWORD_GRACE_TIME", "PASSWORD_LIFE_TIME", "PASSWORD_LOCK_TIME",
"PASSWORD_REUSE_MAX", "PASSWORD_REUSE_TIME", "PASSWORD_VERIFY_FUNCTION",
"PCTFREE", "PCTINCREASE", "PCTTHRESHOLD", "PCTUSED", "PCTVERSION", "PERCENT",
"PERMANENT", "PLAN", "PLSQL_DEBUG", "POST_TRANSACTION", "PRECISION", "PRESERVE",
"PRIMARY", "PRIOR", "PRIVATE", "PRIVATE_SGA", "PRIVILEGE", "PRIVILEGES",
"PROCEDURE", "PROFILE", "PUBLIC", "PURGE", "QUEUE", "QUOTA", "RANGE", "RAW",
"RBA", "READ", "READUP", "REAL", "REBUILD", "RECOVER", "RECOVERABLE", "RECOVERY",
"REF", "REFERENCES", "REFERENCING", "REFRESH", "RENAME", "REPLACE", "RESET",
"RESETLOGS", "RESIZE", "RESOURCE", "RESTRICTED", "RETURN", "RETURNING", "REUSE",
"REVERSE", "REVOKE", "ROLE", "ROLES", "ROLLBACK", "ROW", "ROWID", "ROWNUM",
"ROWS", "RULE", "SAMPLE", "SAVEPOINT", "SB4", "SCAN_INSTANCES", "SCHEMA", "SCN",
"SCOPE", "SD_ALL", "SD_INHIBIT", "SD_SHOW", "SEGMENT", "SEG_BLOCK", "SEG_FILE",
"SELECT", "SEQUENCE", "SERIALIZABLE", "SESSION", "SESSION_CACHED_CURSORS",
"SESSIONS_PER_USER", "SET", "SHARE", "SHARED", "SHARED_POOL", "SHRINK", "SIZE",
"SKIP", "SKIP_UNUSABLE_INDEXES", "SMALLINT", "SNAPSHOT", "SOME", "SORT",
"SPECIFICATION", "SPLIT", "SQL_TRACE", "STANDBY", "START", "STATEMENT_ID",
"STATISTICS", "STOP", "STORAGE", "STORE", "STRUCTURE", "SUCCESSFUL", "SWITCH",
"SYS_OP_ENFORCE_NOT_NULL$", "SYS_OP_NTCIMG$", "SYNONYM", "SYSDATE", "SYSDBA",
"SYSOPER", "SYSTEM", "TABLE", "TABLES", "TABLESPACE", "TABLESPACE_NO", "TABNO",
"TEMPORARY", "THAN", "THE", "THEN", "THREAD", "TIMESTAMP", "TIME", "TO",
"TOPLEVEL", "TRACE", "TRACING", "TRANSACTION", "TRANSITIONAL", "TRIGGER",
"TRIGGERS", "TRUE", "TRUNCATE", "TX", "TYPE", "UB2", "UBA", "UID", "UNARCHIVED",
"UNDO", "UNION", "UNIQUE", "UNLIMITED", "UNLOCK", "UNRECOVERABLE", "UNTIL",
"UNUSABLE", "UNUSED", "UPDATABLE", "UPDATE", "USAGE", "USE", "USER", "USING",
"VALIDATE", "VALIDATION", "VALUE", "VALUES", "VARCHAR", "VARCHAR2", "VARYING",
"VIEW", "WHEN", "WHENEVER", "WHERE", "WITH", "WITHOUT", "WORK", "WRITE",
"WRITEDOWN", "WRITEUP", "XID", "YEAR", "ZONE");
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy