blob: a9e416299cdea3c9e4db2e06fcd0e84fd52f659c [file] [log] [blame]
/*
* Copyright (c) 2008-2015 Eike Stepper (Berlin, Germany) and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Eike Stepper - initial API and implementation
* Stefan Winkler - Bug 289445
*/
package org.eclipse.net4j.spi.db;
import org.eclipse.net4j.db.DBException;
import org.eclipse.net4j.db.DBType;
import org.eclipse.net4j.db.DBUtil;
import org.eclipse.net4j.db.IDBAdapter;
import org.eclipse.net4j.db.IDBConnectionProvider;
import org.eclipse.net4j.db.ddl.IDBField;
import org.eclipse.net4j.db.ddl.IDBIndex;
import org.eclipse.net4j.db.ddl.IDBSchema;
import org.eclipse.net4j.db.ddl.IDBTable;
import org.eclipse.net4j.db.ddl.delta.IDBDelta.ChangeKind;
import org.eclipse.net4j.db.ddl.delta.IDBDeltaVisitor;
import org.eclipse.net4j.db.ddl.delta.IDBIndexDelta;
import org.eclipse.net4j.db.ddl.delta.IDBSchemaDelta;
import org.eclipse.net4j.db.ddl.delta.IDBTableDelta;
import org.eclipse.net4j.internal.db.bundle.OM;
import org.eclipse.net4j.internal.db.ddl.DBField;
import org.eclipse.net4j.util.CheckUtil;
import org.eclipse.net4j.util.om.trace.ContextTracer;
import javax.sql.DataSource;
import java.io.IOException;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.Driver;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
* A useful base class for implementing custom {@link IDBAdapter DB adapters}.
*
* @author Eike Stepper
*/
public abstract class DBAdapter implements IDBAdapter
{
private static final ContextTracer TRACER = new ContextTracer(OM.DEBUG_SQL, DBAdapter.class);
private static final String[] SQL92_RESERVED_WORDS = { "ABSOLUTE", "ACTION", "ADD", "AFTER", "ALL", "ALLOCATE", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$
"ALTER", "AND", "ANY", "ARE", "ARRAY", "AS", "ASC", "ASENSITIVE", "ASSERTION", "ASYMMETRIC", "AT", "ATOMIC", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$ //$NON-NLS-9$ //$NON-NLS-10$ //$NON-NLS-11$ //$NON-NLS-12$
"AUTHORIZATION", "AVG", "BEFORE", "BEGIN", "BETWEEN", "BIGINT", "BINARY", "BIT", "BIT_LENGTH", "BLOB", "BOOLEAN", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$ //$NON-NLS-9$ //$NON-NLS-10$ //$NON-NLS-11$
"BOTH", "BREADTH", "BY", "CALL", "CALLED", "CASCADE", "CASCADED", "CASE", "CAST", "CATALOG", "CHAR", "CHARACTER", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$ //$NON-NLS-9$ //$NON-NLS-10$ //$NON-NLS-11$ //$NON-NLS-12$
"CHARACTER_LENGTH", "CHAR_LENGTH", "CHECK", "CLOB", "CLOSE", "COALESCE", "COLLATE", "COLLATION", "COLUMN", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$ //$NON-NLS-9$
"COMMIT", "CONDITION", "CONNECT", "CONNECTION", "CONSTRAINT", "CONSTRAINTS", "CONSTRUCTOR", "CONTAINS", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$
"CONTINUE", "CONVERT", "CORRESPONDING", "COUNT", "CREATE", "CROSS", "CUBE", "CURRENT", "CURRENT_DATE", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$ //$NON-NLS-9$
"CURRENT_DEFAULT_TRANSFORM_GROUP", "CURRENT_PATH", "CURRENT_ROLE", "CURRENT_TIME", "CURRENT_TIMESTAMP", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$
"CURRENT_TRANSFORM_GROUP_FOR_TYPE", "CURRENT_USER", "CURSOR", "CYCLE", "DATA", "DATE", "DAY", "DEALLOCATE", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$
"DEC", "DECIMAL", "DECLARE", "DEFAULT", "DEFERRABLE", "DEFERRED", "DELETE", "DEPTH", "DEREF", "DESC", "DESCRIBE", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$ //$NON-NLS-9$ //$NON-NLS-10$ //$NON-NLS-11$
"DESCRIPTOR", "DETERMINISTIC", "DIAGNOSTICS", "DISCONNECT", "DISTINCT", "DO", "DOMAIN", "DOUBLE", "DROP", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$ //$NON-NLS-9$
"DYNAMIC", "EACH", "ELEMENT", "ELSE", "ELSEIF", "END", "EQUALS", "ESCAPE", "EXCEPT", "EXCEPTION", "EXEC", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$ //$NON-NLS-9$ //$NON-NLS-10$ //$NON-NLS-11$
"EXECUTE", "EXISTS", "EXIT", "EXTERNAL", "EXTRACT", "FALSE", "FETCH", "FILTER", "FIRST", "FLOAT", "FOR", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$ //$NON-NLS-9$ //$NON-NLS-10$ //$NON-NLS-11$
"FOREIGN", "FOUND", "FREE", "FROM", "FULL", "FUNCTION", "GENERAL", "GET", "GLOBAL", "GO", "GOTO", "GRANT", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$ //$NON-NLS-9$ //$NON-NLS-10$ //$NON-NLS-11$ //$NON-NLS-12$
"GROUP", "GROUPING", "HANDLER", "HAVING", "HOLD", "HOUR", "IDENTITY", "IF", "IMMEDIATE", "IN", "INDICATOR", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$ //$NON-NLS-9$ //$NON-NLS-10$ //$NON-NLS-11$
"INITIALLY", "INNER", "INOUT", "INPUT", "INSENSITIVE", "INSERT", "INT", "INTEGER", "INTERSECT", "INTERVAL", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$ //$NON-NLS-9$ //$NON-NLS-10$
"INTO", "IS", "ISOLATION", "ITERATE", "JOIN", "KEY", "LANGUAGE", "LARGE", "LAST", "LATERAL", "LEADING", "LEAVE", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$ //$NON-NLS-9$ //$NON-NLS-10$ //$NON-NLS-11$ //$NON-NLS-12$
"LEFT", "LEVEL", "LIKE", "LOCAL", "LOCALTIME", "LOCALTIMESTAMP", "LOCATOR", "LOOP", "LOWER", "MAP", "MATCH", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$ //$NON-NLS-9$ //$NON-NLS-10$ //$NON-NLS-11$
"MAX", "MEMBER", "MERGE", "METHOD", "MIN", "MINUTE", "MODIFIES", "MODULE", "MONTH", "MULTISET", "NAMES", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$ //$NON-NLS-9$ //$NON-NLS-10$ //$NON-NLS-11$
"NATIONAL", "NATURAL", "NCHAR", "NCLOB", "NEW", "NEXT", "NO", "NONE", "NOT", "NULL", "NULLIF", "NUMERIC", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$ //$NON-NLS-9$ //$NON-NLS-10$ //$NON-NLS-11$ //$NON-NLS-12$
"OBJECT", "OCTET_LENGTH", "OF", "OLD", "ON", "ONLY", "OPEN", "OPTION", "OR", "ORDER", "ORDINALITY", "OUT", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$ //$NON-NLS-9$ //$NON-NLS-10$ //$NON-NLS-11$ //$NON-NLS-12$
"OUTER", "OUTPUT", "OVER", "OVERLAPS", "PAD", "PARAMETER", "PARTIAL", "PARTITION", "PATH", "POSITION", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$ //$NON-NLS-9$ //$NON-NLS-10$
"PRECISION", "PREPARE", "PRESERVE", "PRIMARY", "PRIOR", "PRIVILEGES", "PROCEDURE", "PUBLIC", "RANGE", "READ", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$ //$NON-NLS-9$ //$NON-NLS-10$
"READS", "REAL", "RECURSIVE", "REF", "REFERENCES", "REFERENCING", "RELATIVE", "RELEASE", "REPEAT", "RESIGNAL", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$ //$NON-NLS-9$ //$NON-NLS-10$
"RESTRICT", "RESULT", "RETURN", "RETURNS", "REVOKE", "RIGHT", "ROLE", "ROLLBACK", "ROLLUP", "ROUTINE", "ROW", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$ //$NON-NLS-9$ //$NON-NLS-10$ //$NON-NLS-11$
"ROWS", "SAVEPOINT", "SCHEMA", "SCOPE", "SCROLL", "SEARCH", "SECOND", "SECTION", "SELECT", "SENSITIVE", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$ //$NON-NLS-9$ //$NON-NLS-10$
"SESSION", "SESSION_USER", "SET", "SETS", "SIGNAL", "SIMILAR", "SIZE", "SMALLINT", "SOME", "SPACE", "SPECIFIC", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$ //$NON-NLS-9$ //$NON-NLS-10$ //$NON-NLS-11$
"SPECIFICTYPE", "SQL", "SQLCODE", "SQLERROR", "SQLEXCEPTION", "SQLSTATE", "SQLWARNING", "START", "STATE", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$ //$NON-NLS-9$
"STATIC", "SUBMULTISET", "SUBSTRING", "SUM", "SYMMETRIC", "SYSTEM", "SYSTEM_USER", "TABLE", "TABLESAMPLE", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$ //$NON-NLS-9$
"TEMPORARY", "THEN", "TIME", "TIMESTAMP", "TIMEZONE_HOUR", "TIMEZONE_MINUTE", "TO", "TRAILING", "TRANSACTION", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$ //$NON-NLS-9$
"TRANSLATE", "TRANSLATION", "TREAT", "TRIGGER", "TRIM", "TRUE", "UNDER", "UNDO", "UNION", "UNIQUE", "UNKNOWN", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$ //$NON-NLS-9$ //$NON-NLS-10$ //$NON-NLS-11$
"UNNEST", "UNTIL", "UPDATE", "UPPER", "USAGE", "USER", "USING", "VALUE", "VALUES", "VARCHAR", "VARYING", "VIEW", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$ //$NON-NLS-9$ //$NON-NLS-10$ //$NON-NLS-11$ //$NON-NLS-12$
"WHEN", "WHENEVER", "WHERE", "WHILE", "WINDOW", "WITH", "WITHIN", "WITHOUT", "WORK", "WRITE", "YEAR", "ZONE" }; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$ //$NON-NLS-9$ //$NON-NLS-10$ //$NON-NLS-11$ //$NON-NLS-12$
private String name;
private String version;
private Set<String> reservedWords;
public DBAdapter(String name, String version)
{
this.name = name;
this.version = version;
}
public String getName()
{
return name;
}
public String getVersion()
{
return version;
}
/**
* @since 4.2
* @deprecated As of 4.2 no longer supported because of IP issues for external build dependencies (the vendor driver libs).
*/
@Deprecated
public Driver getJDBCDriver()
{
throw new UnsupportedOperationException();
}
/**
* @since 4.2
* @deprecated As of 4.2 no longer supported because of IP issues for external build dependencies (the vendor driver libs).
*/
@Deprecated
public DataSource createJDBCDataSource()
{
throw new UnsupportedOperationException();
}
/**
* @since 4.3
*/
public IDBConnectionProvider createConnectionProvider(DataSource dataSource)
{
return DBUtil.createConnectionProvider(dataSource);
}
/**
* @since 4.2
*/
public IDBSchema readSchema(Connection connection, String name)
{
return DBUtil.readSchema(this, connection, name);
}
/**
* @since 4.2
*/
public void readSchema(Connection connection, IDBSchema schema)
{
boolean isTrackConstruction = DBField.isTrackConstruction();
try
{
DBField.trackConstruction(false);
String schemaName = schema.getName();
DatabaseMetaData metaData = connection.getMetaData();
Set<String> schemaNames = DBUtil.getAllSchemaNames(metaData);
if (!schemaNames.contains(schemaName))
{
schemaName = null;
}
ResultSet tables = readTables(connection, metaData, schemaName);
while (tables.next())
{
String tableName = tables.getString(3);
IDBTable table = schema.addTable(tableName);
readFields(connection, table);
readIndices(connection, metaData, table, schemaName);
}
}
catch (SQLException ex)
{
throw new DBException(ex);
}
finally
{
DBField.trackConstruction(isTrackConstruction);
}
}
/**
* @since 4.3
*/
protected ResultSet readTables(Connection connection, DatabaseMetaData metaData, String schemaName)
throws SQLException
{
return metaData.getTables(null, schemaName, null, new String[] { "TABLE" });
}
/**
* @since 4.2
*/
protected void readFields(Connection connection, IDBTable table) throws SQLException
{
Statement statement = null;
ResultSet resultSet = null;
try
{
statement = connection.createStatement();
statement.setMaxRows(1);
resultSet = statement.executeQuery("SELECT * FROM " + table);
ResultSetMetaData metaData = resultSet.getMetaData();
for (int i = 0; i < metaData.getColumnCount(); i++)
{
int column = i + 1;
String name = metaData.getColumnName(column);
if (name == null)
{
// Bug 405924: Just to be sure in case this happens with Oracle.
continue;
}
DBType type = DBType.getTypeByCode(metaData.getColumnType(column));
int precision = metaData.getPrecision(column);
int scale = metaData.getScale(column);
boolean notNull = metaData.isNullable(column) == ResultSetMetaData.columnNoNulls;
table.addField(name, type, precision, scale, notNull);
}
}
finally
{
DBUtil.close(resultSet);
DBUtil.close(statement);
}
}
/**
* @since 4.2
*/
protected void readIndices(Connection connection, DatabaseMetaData metaData, IDBTable table, String schemaName)
throws SQLException
{
String tableName = table.getName();
ResultSet primaryKeys = metaData.getPrimaryKeys(null, schemaName, tableName);
readIndices(connection, primaryKeys, table, 6, 0, 4, 5);
ResultSet indexInfo = metaData.getIndexInfo(null, schemaName, tableName, false, false);
readIndices(connection, indexInfo, table, 6, 4, 9, 8);
}
/**
* @since 4.2
*/
protected void readIndices(Connection connection, ResultSet resultSet, IDBTable table, int indexNameColumn,
int indexTypeColumn, int fieldNameColumn, int fieldPositionColumn) throws SQLException
{
try
{
String indexName = null;
IDBIndex.Type indexType = null;
List<FieldInfo> fieldInfos = new ArrayList<FieldInfo>();
while (resultSet.next())
{
String name = resultSet.getString(indexNameColumn);
if (name == null)
{
// Bug 405924: It seems that this can happen with Oracle.
continue;
}
if (indexName != null && !indexName.equals(name))
{
addIndex(connection, table, indexName, indexType, fieldInfos);
fieldInfos.clear();
}
indexName = name;
if (indexTypeColumn == 0)
{
indexType = IDBIndex.Type.PRIMARY_KEY;
}
else
{
boolean nonUnique = resultSet.getBoolean(indexTypeColumn);
indexType = nonUnique ? IDBIndex.Type.NON_UNIQUE : IDBIndex.Type.UNIQUE;
}
FieldInfo fieldInfo = new FieldInfo();
fieldInfo.name = resultSet.getString(fieldNameColumn);
fieldInfo.position = resultSet.getShort(fieldPositionColumn);
fieldInfos.add(fieldInfo);
}
if (indexName != null)
{
addIndex(connection, table, indexName, indexType, fieldInfos);
}
}
finally
{
DBUtil.close(resultSet);
}
}
/**
* @since 4.2
*/
protected void addIndex(Connection connection, IDBTable table, String name, IDBIndex.Type type,
List<FieldInfo> fieldInfos)
{
IDBField[] fields = new IDBField[fieldInfos.size()];
Collections.sort(fieldInfos);
for (int i = 0; i < fieldInfos.size(); i++)
{
FieldInfo fieldInfo = fieldInfos.get(i);
IDBField field = table.getField(fieldInfo.name);
if (field == null)
{
throw new IllegalStateException("Field not found: " + fieldInfo.name);
}
fields[i] = field;
}
if (!isPrimaryKeyShadow(connection, table, name, type, fields))
{
table.addIndex(name, type, fields);
}
}
/**
* @since 4.2
*/
protected boolean isPrimaryKeyShadow(Connection connection, IDBTable table, String name, IDBIndex.Type type,
IDBField[] fields)
{
if (type != IDBIndex.Type.UNIQUE)
{
return false;
}
IDBIndex primaryKey = table.getPrimaryKeyIndex();
if (primaryKey == null)
{
return false;
}
IDBField[] primaryKeyFields = primaryKey.getFields();
return Arrays.equals(primaryKeyFields, fields);
}
/**
* @since 4.2
*/
public void updateSchema(final Connection connection, final IDBSchema schema, IDBSchemaDelta delta) throws DBException
{
// Apply delta to in-memory representation of the schema
delta.applyTo(schema);
// Call DDL methods to update the database schema
IDBDeltaVisitor schemaUpdater = new IDBDeltaVisitor.Default()
{
@Override
public void visit(IDBTableDelta delta)
{
IDBTable table = delta.getSchemaElement(schema);
ChangeKind changeKind = delta.getChangeKind();
switch (changeKind)
{
case ADD:
createTable(connection, table, delta);
break;
case REMOVE:
dropTable(connection, table, delta);
break;
case CHANGE:
alterTable(connection, table, delta);
break;
default:
throw new IllegalStateException("Illegal change kind: " + changeKind);
}
}
@Override
public void visit(IDBIndexDelta delta)
{
IDBIndex element = delta.getSchemaElement(schema);
ChangeKind changeKind = delta.getChangeKind();
switch (changeKind)
{
case ADD:
createIndex(connection, element, delta);
break;
case REMOVE:
dropIndex(connection, element, delta);
break;
case CHANGE:
dropIndex(connection, element, delta);
createIndex(connection, element, delta);
break;
default:
throw new IllegalStateException("Illegal change kind: " + changeKind);
}
}
};
delta.accept(schemaUpdater);
}
/**
* @since 4.2
*/
protected void createTable(Connection connection, IDBTable table, IDBTableDelta delta)
{
CheckUtil.checkArg(delta.getChangeKind() == ChangeKind.ADD, "Not added: " + delta.getName());
StringBuilder builder = new StringBuilder();
builder.append("CREATE TABLE "); //$NON-NLS-1$
builder.append(delta.getName());
builder.append(" ("); //$NON-NLS-1$
appendFieldDefs(builder, table, createFieldDefinitions(table));
builder.append(")"); //$NON-NLS-1$
DBUtil.execute(connection, builder);
}
/**
* @since 4.2
*/
protected void dropTable(Connection connection, IDBTable table, IDBTableDelta delta)
{
String sql = getDropTableSQL(table);
DBUtil.execute(connection, sql);
}
/**
* @since 4.2
*/
protected void alterTable(Connection connection, IDBTable table, IDBTableDelta delta)
{
}
/**
* @since 4.2
*/
protected void createIndex(Connection connection, IDBIndex index, IDBIndexDelta delta)
{
StringBuilder builder = new StringBuilder();
if (index.getType() == IDBIndex.Type.PRIMARY_KEY)
{
createPrimaryKey(index, builder);
}
else
{
createIndex(index, builder);
}
createIndexFields(index, builder);
DBUtil.execute(connection, builder);
}
/**
* @since 4.2
*/
protected void createPrimaryKey(IDBIndex index, StringBuilder builder)
{
builder.append("ALTER TABLE "); //$NON-NLS-1$
builder.append(index.getTable());
builder.append(" ADD CONSTRAINT "); //$NON-NLS-1$
builder.append(index);
builder.append(" PRIMARY KEY"); //$NON-NLS-1$
}
/**
* @since 4.2
*/
protected void createIndex(IDBIndex index, StringBuilder builder)
{
builder.append("CREATE "); //$NON-NLS-1$
if (index.getType() == IDBIndex.Type.UNIQUE)
{
builder.append("UNIQUE "); //$NON-NLS-1$
}
builder.append("INDEX "); //$NON-NLS-1$
builder.append(index);
builder.append(" ON "); //$NON-NLS-1$
builder.append(index.getTable());
}
/**
* @since 4.2
*/
protected void createIndexFields(IDBIndex index, StringBuilder builder)
{
builder.append(" ("); //$NON-NLS-1$
IDBField[] fields = index.getFields();
for (int i = 0; i < fields.length; i++)
{
if (i != 0)
{
builder.append(", "); //$NON-NLS-1$
}
addIndexField(builder, fields[i]);
}
builder.append(")"); //$NON-NLS-1$
}
/**
* @since 4.2
*/
protected void dropIndex(Connection connection, IDBIndex index, IDBIndexDelta delta)
{
StringBuilder builder = new StringBuilder();
if (index.getType() == IDBIndex.Type.PRIMARY_KEY)
{
dropPrimaryKey(index, builder);
}
else
{
dropIndex(index, builder);
}
DBUtil.execute(connection, builder);
}
/**
* @since 4.2
*/
protected void dropPrimaryKey(IDBIndex index, StringBuilder builder)
{
builder.append("ALTER TABLE "); //$NON-NLS-1$
builder.append(index.getTable());
builder.append(" DROP CONSTRAINT "); //$NON-NLS-1$
builder.append(index);
}
/**
* @since 4.2
*/
protected void dropIndex(IDBIndex index, StringBuilder builder)
{
}
public Set<IDBTable> createTables(Iterable<? extends IDBTable> tables, Connection connection) throws DBException
{
Set<IDBTable> createdTables = new HashSet<IDBTable>();
for (IDBTable table : tables)
{
Statement statement = null;
try
{
statement = connection.createStatement();
if (createTable(table, statement))
{
createdTables.add(table);
}
}
catch (SQLException ex)
{
throw new DBException(ex);
}
finally
{
DBUtil.close(statement);
}
}
return createdTables;
}
public boolean createTable(IDBTable table, Statement statement) throws DBException
{
boolean created = true;
try
{
doCreateTable(table, statement);
}
catch (SQLException ex)
{
created = false;
if (TRACER.isEnabled())
{
TRACER.trace("-- " + ex.getMessage()); //$NON-NLS-1$
}
}
validateTable(table, statement);
return created;
}
public Collection<IDBTable> dropTables(Iterable<? extends IDBTable> tables, Connection connection) throws DBException
{
List<IDBTable> droppedTables = new ArrayList<IDBTable>();
for (IDBTable table : tables)
{
Statement statement = null;
try
{
statement = connection.createStatement();
if (dropTable(table, statement))
{
droppedTables.add(table);
}
}
catch (SQLException ex)
{
OM.LOG.error(ex);
}
finally
{
DBUtil.close(statement);
}
}
return droppedTables;
}
public boolean dropTable(IDBTable table, Statement statement)
{
try
{
String sql = getDropTableSQL(table);
if (TRACER.isEnabled())
{
TRACER.trace(sql);
}
statement.execute(sql);
return true;
}
catch (SQLException ex)
{
if (TRACER.isEnabled())
{
TRACER.trace(ex.getMessage());
}
return false;
}
}
protected String getDropTableSQL(IDBTable table)
{
return "DROP TABLE " + table; //$NON-NLS-1$
}
/**
* @since 2.0
*/
public int getMaxTableNameLength()
{
// Ansi SQL 92 default value
return 128;
}
/**
* @since 2.0
*/
public int getMaxFieldNameLength()
{
// Ansi SQL 92 default value
return 128;
}
/**
* @since 4.2
*/
public int getFieldLength(DBType type)
{
return getDefaultDBLength(type);
}
public boolean isTypeIndexable(DBType type)
{
switch (type)
{
case CLOB:
case BLOB:
case LONGVARCHAR:
case LONGVARBINARY:
case VARBINARY:
case BINARY:
return false;
default:
return true;
}
}
@Override
public String toString()
{
return getName() + "-" + getVersion(); //$NON-NLS-1$
}
/**
* @since 4.3
*/
public String convertString(PreparedStatement preparedStatement, int parameterIndex, String value)
{
return value;
}
/**
* @since 4.3
*/
public String convertString(ResultSet resultSet, int columnIndex, String value)
{
return value;
}
/**
* @since 4.3
*/
public String convertString(ResultSet resultSet, String columnLabel, String value)
{
return value;
}
/**
* @since 2.0
*/
protected void doCreateTable(IDBTable table, Statement statement) throws SQLException
{
StringBuilder builder = new StringBuilder();
builder.append("CREATE TABLE "); //$NON-NLS-1$
builder.append(table);
builder.append(" ("); //$NON-NLS-1$
appendFieldDefs(builder, table, createFieldDefinitions(table));
String constraints = createConstraints(table);
if (constraints != null)
{
builder.append(", "); //$NON-NLS-1$
builder.append(constraints);
}
builder.append(")"); //$NON-NLS-1$
String sql = builder.toString();
if (TRACER.isEnabled())
{
TRACER.trace(sql);
}
statement.execute(sql);
IDBIndex[] indices = table.getIndices();
for (int i = 0; i < indices.length; i++)
{
createIndex(indices[i], statement, i);
}
}
/**
* @since 2.0
*/
protected void createIndex(IDBIndex index, Statement statement, int num) throws SQLException
{
IDBTable table = index.getTable();
StringBuilder builder = new StringBuilder();
builder.append("CREATE "); //$NON-NLS-1$
if (index.getType() == IDBIndex.Type.UNIQUE || index.getType() == IDBIndex.Type.PRIMARY_KEY)
{
builder.append("UNIQUE "); //$NON-NLS-1$
}
builder.append("INDEX "); //$NON-NLS-1$
builder.append(index);
builder.append(" ON "); //$NON-NLS-1$
builder.append(table);
createIndexFields(index, builder);
String sql = builder.toString();
if (TRACER.isEnabled())
{
TRACER.trace(sql);
}
statement.execute(sql);
}
protected void addIndexField(StringBuilder builder, IDBField field)
{
builder.append(field);
}
/**
* @since 2.0
*/
protected String createConstraints(IDBTable table)
{
return null;
}
/**
* @since 2.0
*/
protected String createFieldDefinition(IDBField field)
{
return getTypeName(field) + (field.isNotNull() ? " NOT NULL" : ""); //$NON-NLS-1$ //$NON-NLS-2$
}
// protected String getTypeName(DBType type)
// {
// new DBField(null,null)
// return getTypeName(field);
// }
protected String getTypeName(IDBField field)
{
DBType type = field.getType();
switch (type)
{
case BOOLEAN:
case BIT:
case TINYINT:
case SMALLINT:
case INTEGER:
case BIGINT:
case FLOAT:
case REAL:
case DOUBLE:
case DATE:
case TIME:
case TIMESTAMP:
case LONGVARCHAR:
case LONGVARBINARY:
case BLOB:
case CLOB:
return type.toString();
case CHAR:
case VARCHAR:
case BINARY:
case VARBINARY:
return type.toString() + field.formatPrecision();
case NUMERIC:
case DECIMAL:
return type.toString() + field.formatPrecisionAndScale();
}
throw new IllegalArgumentException("Unknown type: " + type); //$NON-NLS-1$
}
public String[] getSQL92ReservedWords()
{
return SQL92_RESERVED_WORDS;
}
public boolean isReservedWord(String word)
{
if (reservedWords == null)
{
reservedWords = new HashSet<String>();
for (String reservedWord : getReservedWords())
{
reservedWords.add(reservedWord.toUpperCase());
}
}
word = word.toUpperCase();
return reservedWords.contains(word);
}
/**
* @since 2.0
*/
protected void validateTable(IDBTable table, Statement statement) throws DBException
{
int maxRows = 1;
try
{
maxRows = statement.getMaxRows();
statement.setMaxRows(1);
String sql = null;
try
{
StringBuilder builder = new StringBuilder();
builder.append("SELECT "); //$NON-NLS-1$
appendFieldNames(builder, table);
builder.append(" FROM "); //$NON-NLS-1$
builder.append(table);
sql = builder.toString();
if (TRACER.isEnabled())
{
TRACER.format("{0}", sql); //$NON-NLS-1$
}
ResultSet resultSet = statement.executeQuery(sql);
try
{
ResultSetMetaData metaData = resultSet.getMetaData();
int columnCount = metaData.getColumnCount();
if (columnCount != table.getFieldCount())
{
throw new DBException("DBTable " + table + " has " + columnCount + " columns instead of " //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ table.getFieldCount());
}
}
finally
{
DBUtil.close(resultSet);
}
}
catch (SQLException ex)
{
throw new DBException("Problem with table " + table, ex, sql);
}
finally
{
if (maxRows != 1)
{
statement.setMaxRows(maxRows);
}
}
}
catch (SQLException ex)
{
throw new DBException(ex);
}
}
/**
* @since 4.2
*/
protected String[] createFieldDefinitions(IDBTable table)
{
IDBField[] fields = table.getFields();
int fieldCount = fields.length;
String[] result = new String[fieldCount];
for (int i = 0; i < fieldCount; i++)
{
IDBField field = fields[i];
result[i] = createFieldDefinition(field);
}
return result;
}
public void appendFieldNames(Appendable appendable, IDBTable table)
{
try
{
IDBField[] fields = table.getFields();
for (int i = 0; i < fields.length; i++)
{
IDBField field = fields[i];
if (i != 0)
{
appendable.append(", "); //$NON-NLS-1$
}
String fieldName = field.getName();
appendable.append(fieldName);
}
}
catch (IOException canNotHappen)
{
}
}
/**
* @since 4.2
*/
protected void appendFieldDefs(Appendable appendable, IDBTable table, String[] defs)
{
try
{
IDBField[] fields = table.getFields();
for (int i = 0; i < fields.length; i++)
{
IDBField field = fields[i];
if (i != 0)
{
appendable.append(", "); //$NON-NLS-1$
}
String fieldName = field.getName();
appendable.append(fieldName);
appendable.append(" "); //$NON-NLS-1$
appendable.append(defs[i]);
}
}
catch (IOException canNotHappen)
{
}
}
/**
* @since 3.0
*/
public DBType adaptType(DBType type)
{
return type;
}
/**
* @since 4.0
*/
public boolean isValidFirstChar(char ch)
{
return true;
}
/**
* @since 4.0
*/
public boolean isDuplicateKeyException(SQLException ex)
{
String sqlState = ex.getSQLState();
return "23001".equals(sqlState);
}
/**
* @since 4.2
*/
public boolean isTableNotFoundException(SQLException ex)
{
String sqlState = ex.getSQLState();
return "42S02".equals(sqlState);
}
/**
* @since 4.2
*/
public boolean isColumnNotFoundException(SQLException ex)
{
String sqlState = ex.getSQLState();
return "42S22".equals(sqlState);
}
/**
* @since 4.2
*/
public String sqlRenameField(IDBField field, String oldName)
{
return "ALTER TABLE " + field.getTable() + " RENAME COLUMN " + oldName + " TO " + field;
}
/**
* @since 4.2
*/
public String sqlModifyField(IDBField field)
{
String tableName = field.getTable().getName();
String fieldName = field.getName();
String definition = createFieldDefinition(field);
return sqlModifyField(tableName, fieldName, definition);
}
/**
* @since 4.2
*/
protected String sqlModifyField(String tableName, String fieldName, String definition)
{
return "ALTER TABLE " + tableName + " ALTER COLUMN " + fieldName + " " + definition;
}
/**
* @since 4.2
*/
public String format(PreparedStatement stmt)
{
return stmt.toString();
}
/**
* @since 4.2
*/
public String format(ResultSet resultSet)
{
try
{
StringBuilder builder = new StringBuilder();
ResultSetMetaData metaData = resultSet.getMetaData();
int columnCount = metaData.getColumnCount();
for (int i = 0; i < columnCount; i++)
{
if (i != 0)
{
builder.append(", ");
}
builder.append(metaData.getColumnName(i + 1).toLowerCase());
builder.append("=");
builder.append(resultSet.getObject(i + 1));
}
return builder.toString();
}
catch (SQLException ex)
{
throw new DBException(ex);
}
}
/**
* @since 4.2
*/
public static int getDefaultDBLength(DBType type)
{
return type == DBType.VARCHAR ? 32672 : IDBField.DEFAULT;
}
/**
* @since 4.2
* @author Eike Stepper
*/
protected static final class FieldInfo implements Comparable<FieldInfo>
{
public String name;
public int position;
public int compareTo(FieldInfo o)
{
return position - o.position;
}
}
}