Java tutorial
/* * Copyright (C) 2010 Trustees of the University of Pennsylvania * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS of ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package edu.upenn.cis.orchestra.repository.utils.dbConverter; import static edu.upenn.cis.orchestra.OrchestraUtil.newArrayList; import java.sql.DatabaseMetaData; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Types; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import javax.sql.DataSource; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.jdbc.support.DatabaseMetaDataCallback; import org.springframework.jdbc.support.JdbcUtils; import org.springframework.jdbc.support.MetaDataAccessException; import edu.upenn.cis.orchestra.Config; import edu.upenn.cis.orchestra.datalog.atom.Atom.AtomType; import edu.upenn.cis.orchestra.datamodel.ForeignKey; import edu.upenn.cis.orchestra.datamodel.IntegrityConstraint; import edu.upenn.cis.orchestra.datamodel.PrimaryKey; import edu.upenn.cis.orchestra.datamodel.Relation; import edu.upenn.cis.orchestra.datamodel.RelationField; import edu.upenn.cis.orchestra.datamodel.RelationIndexUnique; import edu.upenn.cis.orchestra.datamodel.Schema; import edu.upenn.cis.orchestra.dbms.SqlDb; import edu.upenn.cis.orchestra.dbms.sql.generation.ISqlStatementGen; import edu.upenn.cis.orchestra.exchange.sql.SqlEngine; import edu.upenn.cis.orchestra.sql.ISqlColumnDef; import edu.upenn.cis.orchestra.sql.ISqlFactory; import edu.upenn.cis.orchestra.sql.SqlFactories; public class SchemaConverterStatementsGen { public final static boolean INDEX_ALL_FIELDS = true; // public final static String ALTER_NO_LOGGING = // " ACTIVATE NOT LOGGED INITIALLY"; // TODO: Would be better to keep indexes tablespaces but is it possible via // JDBC ?? // TODO: It also loses deferrability and this kind of qttributes for foreign // keys // TODO: Views are not converted to deal with labeled nulls private DataSource _ds; private String _jdbcDriver; private Schema _sc; private final ISqlFactory _sqlFactory = SqlFactories.getSqlFactory(); private final List<String> _existingTableNames; // // TODO: CLEAN!!!!!!!! // public enum AtomType // { // NONE, // // OLD, // NEW, // INS, // DEL, // // AFF, // RCH, // // LOOPTEST, // INV, // ALLDEL // }; private Log _log = LogFactory.getLog(getClass()); public SchemaConverterStatementsGen(DataSource ds, String jdbcDriver, Schema sc) { _ds = ds; _sc = sc; _jdbcDriver = jdbcDriver; _existingTableNames = getExistingTableNames(); } /** * Create the SQL statements necessary to fill in any missing tables in the * schema * * @param statements The SQL statements will be added in this list. Order * must be respected for execution * @return List of relations */ public List<String> createNecessaryTables(boolean withNoLogging, String database, boolean stratified, boolean withLogging, SqlDb db) { List<String> statements = new ArrayList<String>(); for (Relation rel : _sc.getRelations()) { if (!_existingTableNames.contains(rel.getFullQualifiedDbId())) { String suffix = ""; statements.addAll(db.createSQLTableCode(rel.getDbRelName(), suffix, rel, false, false, false, false, withLogging)); // Create any key constraints /*if (!rel.isInternalRelation()) { // First of all, recreate primary keys. Necessary for // foreign // keys creation createPrimaryKey(rel, statements); // Create unique indexes createUniqueIndexes(rel, statements); // TODO: foreign keys??? }*/ } } return statements; } /** * Create the SQL statements necessary to convert the schema's relations so * that they can store labeled nulls. * * @param statements The SQL statements will be added in this list. Order * must be respected for execution * @param containsBidirectionalMappings * @return List of relations that already contain labeled null columns but * without the correct characteristics */ public Map<Relation, List<String>> createTableConversionStatements(List<String> statements, boolean withNoLogging, String database, boolean stratified, String NO_LOGGING, SqlDb db, boolean containsBidirectionalMappings) throws MetaDataAccessException { Map<Relation, List<String>> res; // First step : remove constraints that will be affected by // the addition of skolems => Foreign keys, primary keys, unique indexes // TODO // removeConstraints(statements); // Second step : add the labeled null columns res = addLabeledNullColumns(statements, withNoLogging, db); // Third step // TODO // recreateConstraints(statements); // 4th step: create the delta tables createDeltaTables(statements, withNoLogging, database, stratified, NO_LOGGING, db, containsBidirectionalMappings); return res; } /** * @deprecated * * @param statements * @param withNoLogging * @param database * @param stratified * @param NO_LOGGING * @param db */ private void OLDcreateDeltaTables(List<String> statements, boolean withNoLogging, String database, boolean stratified, String NO_LOGGING, SqlDb db) { // Modified by zives 12/20: don't create tables for _NONE. Instead // update the base table for (Relation rel : _sc.getRelations()) { for (AtomType type : AtomType.values()) { String relName = rel.getDbRelName(); // if((relName.endsWith("_L") || relName.endsWith("_R")) && // (type == AtomType.RCH)){ if (rel.isInternalRelation() && (type == AtomType.RCH)) { // We don't need to create _RCH tables for idbs } else if (!stratified || type != AtomType.ALLDEL) { if (type != AtomType.NONE) relName = relName + "_" + type.toString(); _log.debug("Delta table " + relName + ": prep statement"); relName = relName.replaceAll("_", "\\\\_"); if (type != AtomType.NONE) { if (_existingTableNames.contains(rel.getFullQualifiedDbId() + "_" + type.toString())) statements.add("DROP TABLE " + rel.getFullQualifiedDbId() + "_" + type.toString()); } // String tablename = rel.getFullQualifiedDbId() + "_" + // type.toString(); String tablename = rel.getFullQualifiedDbId(); // String stratum = ""; // if(!tablename.endsWith("_L") && !tablename.endsWith("_R") // && (type == AtomType.DEL)){ // stratum = "STRATUM INTEGER, "; // } String stratum = ""; List<ISqlColumnDef> cols = newArrayList(); if (stratified && (type == AtomType.DEL || type == AtomType.INS)) { // SqlColumnDef s = new SqlColumnDef("STRATUM", // "INTEGER", "0"); if (Config.isOracle()) stratum = "STRATUM INTEGER DEFAULT 0, "; else stratum = "STRATUM INTEGER, "; } if (type != AtomType.NONE) tablename = tablename + "_" + type.toString(); String typ; // if (database.contains("hsqldb")) // typ = "CACHED "; // else typ = ""; if (type != AtomType.NONE) { String stat = "CREATE " + typ + "TABLE " + tablename + "(" + stratum; for (int i = 0; i < rel.getFields().size(); i++) { if (i > 0) stat = stat + ","; stat = stat + rel.getField(i).getName() + " " + rel.getField(i).getSQLTypeName(); cols.add(_sqlFactory.newColumnDef(rel.getField(i).getName(), rel.getField(i).getSQLTypeName(), null)); } for (int i = 0; i < rel.getFields().size(); i++) { stat = stat + "," + rel.getField(i).getName() + RelationField.LABELED_NULL_EXT + " INTEGER";// + // rel.getField(i).getDbType(); if (tablename.endsWith("_L_INS") || tablename.endsWith("_R_INS") || tablename.endsWith("_L_DEL") || tablename.endsWith("_R_DEL")) cols.add(_sqlFactory.newColumnDef( rel.getField(i).getName() + RelationField.LABELED_NULL_EXT, "INTEGER", "1")); else cols.add(_sqlFactory.newColumnDef( rel.getField(i).getName() + RelationField.LABELED_NULL_EXT, "INTEGER", null)); } stat = stat + ") "; /* * + " AS (SELECT * FROM " + rel.getFullQualifiedDbId() * + ")"; if (_jdbcDriver.contains("db2")) { statement = * statement + " WITH NO DATA"; * * if (!withLogging) statement = statement + NO_LOGGING; * } */ // if (withNoLogging) // if(Config.isDB2() || Config.isOracle()) // statement = statement + NO_LOGGING; // statements.add(stat); List<String> stmts = db.getSqlTranslator().createTable(tablename, cols, withNoLogging); statements.addAll(stmts); } // statements.add("ALTER TABLE " + tablename + // " VOLATILE CARDINALITY"); if (tablename.endsWith("_L_INS") || tablename.endsWith("_R_INS") || tablename.endsWith("_L_DEL") || tablename.endsWith("_R_DEL")) { for (RelationField fld : rel.getFields()) { StringBuffer buf = new StringBuffer(); buf.append("ALTER TABLE " + tablename); // Add null column if this attribute is not the key // boolean fldInKey = // rel.getPrimaryKey().getFields().contains(fld); // if(!fldInKey){ if (database.contains("db2")) { buf.append(" ALTER COLUMN "); buf.append(fld.getName() + RelationField.LABELED_NULL_EXT); buf.append(" SET DEFAULT 1"); } else { buf.append(" modify ("); buf.append(fld.getName() + RelationField.LABELED_NULL_EXT); buf.append(" DEFAULT 1)"); } // } statements.add(buf.toString()); } } if (stratified) { if (tablename.endsWith("_L_DEL") || tablename.endsWith("_R_DEL") || tablename.endsWith("_L_INS") || tablename.endsWith("_R_INS")) { StringBuffer buf = new StringBuffer(); buf.append("ALTER TABLE " + tablename); if (database.contains("db2")) buf.append(" ALTER COLUMN STRATUM SET DEFAULT 0"); else buf.append(" modify (STRATUM DEFAULT 0)"); statements.add(buf.toString()); } } StringBuffer keybuf = new StringBuffer(); boolean first = true; for (RelationField fld : rel.getFields()) { boolean fldInKey = rel.getPrimaryKey().getFields().contains(fld); if (fldInKey) { if (first) { first = false; } else { keybuf.append(","); } keybuf.append(fld.getName()); } } String clusterSpec = ""; // if (!database.contains(("hsqldb"))) if (database.contains(("db2"))) clusterSpec = "CLUSTER"; // TODO: remove hack if (!INDEX_ALL_FIELDS) { // TEMPORARY HACK keybuf = new StringBuffer(); keybuf.append("KID"); String stmt = "CREATE INDEX " + tablename + "_IDX " + " ON " + tablename + "(" + keybuf.toString() + ") " + clusterSpec; statements.add(stmt); // if (rel.getField("KID")!=null){ // statements.add ("CREATE INDEX " + tablename + "_IDX " // + " ON " + tablename // + "(KID) CLUSTER"); // } } else { // boolean first = true; // StringBuffer buff = new StringBuffer (); // if (rel.getField("KID")!=null) // { // buff.append ("KID"); // first = false; // } StringBuffer buff = new StringBuffer(); if (keybuf.length() == 0) first = true; else first = false; buff.append(keybuf.toString()); for (RelationField fld : rel.getFields()) { // Add null column if this attribute is not the key // boolean fldInKey = // rel.getPrimaryKey().getFields().contains(fld); // if(!fldInKey){ buff.append(first ? "" : ","); buff.append(fld.getName() + RelationField.LABELED_NULL_EXT); // } first = false; } String stmt = "CREATE INDEX " + tablename + "_IDX " + " ON " + tablename + "(" + buff.toString() + ") " + clusterSpec; statements.add(stmt); // System.out.println("CREATE INDEX " + // (rel.getDbSchema()!=null?rel.getDbSchema() + ".":"") // + rel.getDbRelName() + "_" + type.toString() + // "_IDX " // + " ON " + rel.getFullQualifiedDbId() + "_" + // type.toString() // + "(" + buff.toString() + ") CLUSTER"); } // statements.add("ALTER TABLE " + tablename + // " VOLATILE CARDINALITY"); _log.debug("Delta table " + relName + ": statement done"); } } } } private void createDeltaTables(List<String> statements, boolean withNoLogging, String database, boolean stratified, String NO_LOGGING, SqlDb db, boolean containsBidirectionalMappings) { // Modified by zives 12/20: don't create tables for _NONE. Instead // update the base table for (Relation rel : _sc.getRelations()) { for (AtomType type : AtomType.values()) { String relName = rel.getDbRelName(); // if((relName.endsWith("_L") || relName.endsWith("_R")) && // (type == AtomType.RCH)){ if ((rel.isInternalRelation() && (type == AtomType.RCH)) || ((!containsBidirectionalMappings) && (type == AtomType.D))) // if ((rel.getName().endsWith(Relation.LOCAL) && (type == // AtomType.RCH)) || // (rel.getName().endsWith(Relation.REJECT) && (type != // AtomType.NONE))) { // We don't need to create _RCH tables for idbs } else if (!stratified || type != AtomType.ALLDEL) { if (type != AtomType.NONE) relName = relName + "_" + type.toString(); _log.debug("Delta table " + relName + ": prep statement"); relName = relName.replaceAll("_", "\\\\_"); if (type != AtomType.NONE) { if (_existingTableNames.contains(rel.getFullQualifiedDbId() + "_" + type.toString())) statements.add("DROP TABLE " + rel.getFullQualifiedDbId() + "_" + type.toString()); } // gregkar String tablename; // if(Config.getTempTables() // && type != AtomType.NONE // ){ // tablename = SqlStatementGen.sessionSchema + "." + // rel.getDbRelName(); // } else { tablename = rel.getFullQualifiedDbId(); // } List<ISqlColumnDef> cols = newArrayList(); if (stratified && (type == AtomType.DEL || type == AtomType.INS)) { if (tablename.endsWith("_L_DEL") || tablename.endsWith("_R_DEL") || tablename.endsWith("_L_INS") || tablename.endsWith("_R_INS")) { cols.add(_sqlFactory.newColumnDef("STRATUM", "INTEGER", "0")); } else { cols.add(_sqlFactory.newColumnDef("STRATUM", "INTEGER", null)); } } if (type != AtomType.NONE) tablename = tablename + "_" + type.toString(); if (type != AtomType.NONE) { for (int i = 0; i < rel.getFields().size(); i++) { cols.add(_sqlFactory.newColumnDef(rel.getField(i).getName(), rel.getField(i).getSQLTypeName(), null)); } if (rel.hasLabeledNulls()) { for (int i = 0; i < rel.getFields().size(); i++) { if (!Config.useCompactNulls() || rel.isNullable(i)) { if (tablename.endsWith("_L_INS") || tablename.endsWith("_R_INS") || tablename.endsWith("_L_DEL") || tablename.endsWith("_R_DEL")) cols.add(_sqlFactory.newColumnDef( rel.getField(i).getName() + RelationField.LABELED_NULL_EXT, "INTEGER", Integer.toString(SqlEngine.LABELED_NULL_NONVALUE))); else cols.add(_sqlFactory.newColumnDef( rel.getField(i).getName() + RelationField.LABELED_NULL_EXT, "INTEGER", null)); } } } // gregkar if (!Config.getTempTables() || !(tablename.startsWith(ISqlStatementGen.sessionSchema) // || // type // != // AtomType.NONE )) { System.out.println("* Adding non-temp table " + tablename); statements.addAll(db.getSqlTranslator().createTable(tablename, cols, withNoLogging)); } else { System.out.println("* Adding delta temp table " + tablename); statements.addAll(db.getSqlTranslator().createTempTable(tablename, cols)); } } List<ISqlColumnDef> v = newArrayList(); // boolean first = true; for (RelationField fld : rel.getFields()) { boolean fldInKey = rel.getPrimaryKey().getFields().contains(fld); if (fldInKey) { v.add(_sqlFactory.newColumnDef(fld.getName(), "", null)); } } // TODO: remove hack if (!INDEX_ALL_FIELDS) { // TEMPORARY HACK v.add(_sqlFactory.newColumnDef("KID", "", null)); } else { if (rel.hasLabeledNulls()) { int inx = 0; for (RelationField fld : rel.getFields()) { if (!Config.useCompactNulls() || rel.isNullable(inx)) v.add(_sqlFactory.newColumnDef(fld.getName() + RelationField.LABELED_NULL_EXT, "", null)); inx++; } } } String stmt; // gregkar stmt = db.getSqlTranslator().createIndex(tablename + "_IDX ", tablename, v, true, true); statements.add(stmt); _log.debug("Delta table " + relName + ": statement done"); } } } } /** * * Returns a list of the db names of any tables which already exist in the * {@code Schema}. * * @return a list of table names which exist in the {@code Schema} */ private List<String> getExistingTableNames() { _log.debug("Loading all the existing tables in memory"); List<String> existingTables = new ArrayList<String>(); Map<String, Set<String>> schemas = new HashMap<String, Set<String>>(); for (Relation rel : _sc.getRelations()) { if (!schemas.containsKey(rel.getDbCatalog())) schemas.put(rel.getDbCatalog(), new HashSet<String>()); Set<String> schemNames = schemas.get(rel.getDbCatalog()); schemNames.add(rel.getDbSchema()); } for (Map.Entry<String, Set<String>> entry : schemas.entrySet()) { for (String sc : entry.getValue()) { MetaExistTables loader = new MetaExistTables(entry.getKey(), sc, existingTables); try { JdbcUtils.extractDatabaseMetaData(_ds, loader); } catch (MetaDataAccessException ex) { // TODO: Actually deal with exceptions here! assert (false) : "not implemented yet"; } } } _log.debug("Existing tables loaded. " + existingTables.size() + " found"); return existingTables; } // TODO/ What about foreign key that were not loaded in the schema?? /** * First step is to remove constraints that will be affected by skolems * introduction. They will be recreated later * * @param statements List to which statements must be added */ private void removeConstraints(List<String> statements) { for (Relation rel : _sc.getRelations()) { for (ForeignKey fk : rel.getForeignKeys()) removeConstraint(rel, fk, statements); for (RelationIndexUnique idx : rel.getUniqueIndexes()) removeConstraint(rel, idx, statements); /* * Let's keep the non unique indexes. skolem nulls are not * discriminant enough */ /* * for (ScNonUniqueIndex idx : rel.getNonUniqueIndexes()) * removeIndex(rel, idx, statements); */ } for (Relation rel : _sc.getRelations()) if (rel.getPrimaryKey() != null) removeConstraint(rel, rel.getPrimaryKey(), statements); } /** * Remove a given constraint for relation rel * * @param rel Relation from which the constraint has to be removed * @param cst Constraint to be removed from the database * @param statements List to which statements must be added */ private void removeConstraint(Relation rel, IntegrityConstraint cst, List<String> statements) { statements.add("ALTER TABLE " + rel.getFullQualifiedDbId() + " DROP CONSTRAINT " + cst.getName()); } /** * For each table, add a labeled null column for each column that does not * have one already * * @param statements List to which statements must be added * @return For each relation having "errors": list of labeled null fields * already defined but incorrects */ private Map<Relation, List<String>> addLabeledNullColumns(List<String> statements, boolean withNoLogging, SqlDb db) throws MetaDataAccessException { Map<Relation, List<String>> tabErrors = new HashMap<Relation, List<String>>(); for (Relation rel : _sc.getRelations()) { if (rel.hasLabeledNulls()) { List<String> errors = addLabeledNullColumns(rel, statements, withNoLogging, db); if (errors.size() > 0) tabErrors.put(rel, errors); } } return tabErrors; } /** * For each column in the relation, add a new column to contain the labeled * null id. * * @param rel Relation for which labeled null columns must be added * @param statements List to which new statements must be added * @return List of labeled null columns already defined but incorrect */ @SuppressWarnings("unchecked") private List<String> addLabeledNullColumns(Relation rel, List<String> statements, boolean withNoLogging, SqlDb db) throws MetaDataAccessException { List<String> labNulls = new ArrayList<String>(); MetaLoadColumns loader = new MetaLoadColumns(rel.getDbCatalog(), rel.getDbSchema(), rel.getDbRelName(), labNulls); List<String> errors = null; // try // { errors = (List<String>) JdbcUtils.extractDatabaseMetaData(_ds, loader); /* * } catch (MetaDataAccessException ex) { //TODO: Actually deal with * exceptions here! assert (errors!=null) : "not implemented yet"; } */ // String extra = ""; // if (_jdbcDriver.contains("db2")) { // if (withNoLogging) // extra = ALTER_NO_LOGGING; // } List<ISqlColumnDef> cols = newArrayList(); int inx = 0; for (RelationField fld : rel.getFields()) { // Add null column if this attribute is not the key // boolean fldInKey = rel.getPrimaryKey().getFields().contains(fld); // if(!fldInKey){ if ((!Config.useCompactNulls() || rel.isNullable(inx)) && !labNulls.contains(fld.getName() + RelationField.LABELED_NULL_EXT)) cols.add(_sqlFactory.newColumnDef(fld.getName() + RelationField.LABELED_NULL_EXT, "INTEGER", Integer.toString(SqlEngine.LABELED_NULL_NONVALUE))); // statements.add ("ALTER TABLE " + rel.getFullQualifiedDbId() // + " ADD " + fld.getName() + ModelConstants.LABEL_NULL_EXT // //+ " NUMERIC (2, 0) WITH DEFAULT 1" + extra // + " INTEGER DEFAULT 1" + extra // //TODO: Fix pb: if create table as select it doesn't copy default // value!!! // /*+ " DEFAULT -1" // + " NOT NULL"*/); // // } inx++; } statements.addAll(db.getSqlTranslator().addColsToTable(rel.getFullQualifiedDbId(), cols, withNoLogging)); return errors; } /** * Recreate all the constraints, including the newly created labeled null * columns * * @param statements List to which SQL statements must be added * * @deprecated */ private void recreateConstraints(List<String> statements) { for (Relation rel : _sc.getRelations()) { // First of all, recreate primary keys. Necessary for foreign keys // creation createPrimaryKey(rel, statements); // Create unique indexes createUniqueIndexes(rel, statements); } for (Relation rel : _sc.getRelations()) { // Create foreign keys createForeignKeys(rel, statements); } } /** * Recreate the relation primary key (if any), including the new labeled * null columns * * @param rel Relation for which to create the primary key * @param statements List to which SQL statements must be added */ private void createPrimaryKey(Relation rel, List<String> statements) { PrimaryKey pk = rel.getPrimaryKey(); if (pk != null) { String fieldsList = getCommaSepFieldsList(rel, pk.getFields()); statements.add("ALTER TABLE " + rel.getFullQualifiedDbId() + " ADD CONSTRAINT " + pk.getName() + " PRIMARY KEY " + fieldsList); } } /** * Recreate the relation unique indexes including the new labeled null * columns * * @param rel Relation for which to create the unique index * @param statements List to which SQL statements must be added */ private void createUniqueIndexes(Relation rel, List<String> statements) { for (RelationIndexUnique idx : rel.getUniqueIndexes()) { String fieldsList = getCommaSepFieldsList(rel, idx.getFields()); statements.add("CREATE UNIQUE INDEX " + idx.getName() + " ON " + rel.getFullQualifiedDbId() + fieldsList + "CLUSTER"); // statements.add("ALTER TABLE " + idx.getName() + // " VOLATILE CARDINALITY"); } } /** * Recreate the relation foreign keys including the new labeled null columns * * @param rel Relation for which to recreate foreign keys * @param statements List to which SQL statements must be added */ private void createForeignKeys(Relation rel, List<String> statements) { for (ForeignKey fk : rel.getForeignKeys()) { String fkFieldsList = getCommaSepFieldsList(rel, fk.getFields()); String refFieldsLString = getCommaSepFieldsList(rel, fk.getRefFields()); statements.add("ALTER TABLE " + rel.getFullQualifiedDbId() + " ADD CONSTRAINT " + fk.getName() + " FOREIGN KEY " + fkFieldsList + " REFERENCES " + fk.getRefRelation().getName() + refFieldsLString); } } /** * Create a comma-separated list of the fields names, for each field include * the labeled null field name right after the associated field * * @param fields Fields to include in the list, should not contain labeled * null fields * @return comma-separated list of fields names */ private String getCommaSepFieldsList(Relation rel, List<RelationField> fields) { StringBuffer buff = new StringBuffer(); boolean first = true; buff.append("("); int inx = 0; for (RelationField fld : fields) { if (!first) buff.append(", "); else first = false; buff.append(fld.getName()); if (rel.hasLabeledNulls() && (!Config.useCompactNulls() || rel.isNullable(inx))) { buff.append(", "); buff.append(fld.getName() + RelationField.LABELED_NULL_EXT); } inx++; } buff.append(")"); return buff.toString(); } /** * Metadata callback class used to load a table's columns and store those * information in order to know what tables/columns are already prepared for * labeled nulls Based on the Spring framework / JDBC module * * @author Olivier Biton */ private class MetaLoadColumns implements DatabaseMetaDataCallback { String _catalog; String _schema; String _tableName; List<String> _fields; public MetaLoadColumns(String catalog, String schema, String tableName, List<String> fields) { _catalog = catalog; _schema = schema; _tableName = tableName; _fields = fields; } /** * @return If there are incorrect labeled null fields already defined in * the database: list of those fields names */ public Object processMetaData(DatabaseMetaData dbmd) throws SQLException, MetaDataAccessException { List<String> errorColumns = new ArrayList<String>(); ResultSet rs = dbmd.getColumns(_catalog, _schema, _tableName, null); while (rs.next()) { // Read column def first because of a bug in Oracle (column is // stored as a long and has to be read first!) String colDef = rs.getString("COLUMN_DEF"); // Ignore labeled null columns (if the database has already // been prepared for labeled null String colName = rs.getString("COLUMN_NAME"); if (colName.endsWith(RelationField.LABELED_NULL_EXT)) { if ((rs.getInt("DATA_TYPE") != Types.NUMERIC && rs.getInt("DATA_TYPE") != Types.DECIMAL) // TODO (cf. create table... pb with default copy_ /* * || rs.getString("IS_NULLABLE").equals("YES") || * !colDef.equals("-1") */ ) errorColumns.add(colName); _fields.add(colName); } } return errorColumns; } } /** * Metadata callback class used to load drop a table if it exists * * @author Olivier Biton */ private class MetaExistTables implements DatabaseMetaDataCallback { String _catalog; String _schema; List<String> _existingTables; public MetaExistTables(String catalog, String schema, List<String> existingTables) { _catalog = catalog; _schema = schema; _existingTables = existingTables; } /** * @return If there are incorrect labeled null fields already defined in * the database: list of those fields names */ public Object processMetaData(DatabaseMetaData dbmd) throws SQLException, MetaDataAccessException { ResultSet rs = dbmd.getTables(_catalog, _schema, null, null); while (rs.next()) { String tableName = ""; if (_catalog != null) tableName = _catalog + "."; if (_schema != null) tableName = tableName + _schema + "."; tableName = tableName + rs.getString("TABLE_NAME"); _existingTables.add(tableName); } return null; } } }