You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@openjpa.apache.org by pc...@apache.org on 2006/07/19 23:35:07 UTC
svn commit: r423615 [27/44] - in /incubator/openjpa/trunk: ./
openjpa-jdbc-5/ openjpa-jdbc-5/src/ openjpa-jdbc-5/src/main/
openjpa-jdbc-5/src/main/java/ openjpa-jdbc-5/src/main/java/org/
openjpa-jdbc-5/src/main/java/org/apache/ openjpa-jdbc-5/src/main/...
Added: incubator/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/schema/SchemaTool.java
URL: http://svn.apache.org/viewvc/incubator/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/schema/SchemaTool.java?rev=423615&view=auto
==============================================================================
--- incubator/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/schema/SchemaTool.java (added)
+++ incubator/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/schema/SchemaTool.java Wed Jul 19 14:34:44 2006
@@ -0,0 +1,1523 @@
+/*
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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 org.apache.openjpa.jdbc.schema;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.Writer;
+import java.sql.Connection;
+import java.sql.DatabaseMetaData;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.Set;
+import javax.sql.DataSource;
+
+import org.apache.openjpa.conf.OpenJPAConfiguration;
+import org.apache.openjpa.jdbc.conf.JDBCConfiguration;
+import org.apache.openjpa.jdbc.conf.JDBCConfigurationImpl;
+import org.apache.openjpa.jdbc.sql.DBDictionary;
+import org.apache.openjpa.jdbc.sql.SQLExceptions;
+import org.apache.openjpa.lib.conf.Configurations;
+import org.apache.openjpa.lib.log.Log;
+import org.apache.openjpa.lib.util.Files;
+import org.apache.openjpa.lib.util.Localizer;
+import org.apache.openjpa.lib.util.Options;
+import org.apache.openjpa.util.InvalidStateException;
+
+/**
+ * The SchemaTool is used to manage the database schema. Note that the
+ * tool never adds or drops unique constraints from existing tables, because
+ * JDBC {@link DatabaseMetaData} does not include information on these
+ * constraints.
+ *
+ * @author Abe White
+ * @author Patrick Linskey
+ */
+public class SchemaTool {
+
+ public static final String ACTION_ADD = "add";
+ public static final String ACTION_DROP = "drop";
+ public static final String ACTION_RETAIN = "retain";
+ public static final String ACTION_REFRESH = "refresh";
+ public static final String ACTION_BUILD = "build";
+ public static final String ACTION_REFLECT = "reflect";
+ public static final String ACTION_CREATEDB = "createDB";
+ public static final String ACTION_DROPDB = "dropDB";
+ public static final String ACTION_IMPORT = "import";
+ public static final String ACTION_EXPORT = "export";
+
+ public static final String[] ACTIONS = new String[]{
+ ACTION_ADD,
+ ACTION_DROP,
+ ACTION_RETAIN,
+ ACTION_REFRESH,
+ ACTION_BUILD,
+ ACTION_REFLECT,
+ ACTION_CREATEDB,
+ ACTION_DROPDB,
+ ACTION_IMPORT,
+ ACTION_EXPORT,
+ };
+
+ private static final Localizer _loc = Localizer.forPackage
+ (SchemaTool.class);
+
+ private final JDBCConfiguration _conf;
+ private final DataSource _ds;
+ private final Log _log;
+ private final DBDictionary _dict;
+ private final String _action;
+ private boolean _ignoreErrs = false;
+ private boolean _openjpaTables = false;
+ private boolean _dropTables = true;
+ private boolean _dropSeqs = true;
+ private boolean _pks = true;
+ private boolean _fks = true;
+ private boolean _indexes = true;
+ private boolean _seqs = true;
+ private PrintWriter _writer = null;
+ private SchemaGroup _group = null;
+ private SchemaGroup _db = null;
+ private boolean _fullDB = false;
+
+ /**
+ * Default constructor. Tools constructed this way will not have an
+ * action, so the {@link #run()} method will be a no-op.
+ */
+ public SchemaTool(JDBCConfiguration conf) {
+ this(conf, null);
+ }
+
+ /**
+ * Construct a tool to perform the given action.
+ */
+ public SchemaTool(JDBCConfiguration conf, String action) {
+ if (action != null && !Arrays.asList(ACTIONS).contains(action))
+ throw new IllegalArgumentException("action == " + action);
+
+ _conf = conf;
+ _action = action;
+ _ds = conf.getDataSource2(null);
+ _log = conf.getLog(JDBCConfiguration.LOG_SCHEMA);
+
+ // initialize this up-front; otherwise the dbdictionaryfactory might
+ // try to take a connection to initialize when we've already got one:
+ // bad news if the max pool is 1
+ _dict = _conf.getDBDictionaryInstance();
+ }
+
+ /**
+ * The action supplied on construction.
+ */
+ public String getAction() {
+ return _action;
+ }
+
+ /**
+ * If true, SQLExceptions thrown during schema manipulation will be
+ * printed but ignored.
+ */
+ public boolean getIgnoreErrors() {
+ return _ignoreErrs;
+ }
+
+ /**
+ * If true, SQLExceptions thrown during schema manipulation will be
+ * printed but ignored.
+ */
+ public void setIgnoreErrors(boolean ignoreErrs) {
+ _ignoreErrs = ignoreErrs;
+ }
+
+ /**
+ * Whether to act on special tables used by OpenJPA components
+ * for bookkeeping.
+ */
+ public boolean getOpenJPATables() {
+ return _openjpaTables;
+ }
+
+ /**
+ * Whether to act on special tables used by OpenJPA components
+ * for bookkeeping.
+ */
+ public void setOpenJPATables(boolean openjpaTables) {
+ _openjpaTables = openjpaTables;
+ }
+
+ /**
+ * If true, tables that appear to be unused will be dropped. Defaults to
+ * true.
+ */
+ public boolean getDropTables() {
+ return _dropTables;
+ }
+
+ /**
+ * If true, tables that appear to be unused will be dropped. Defaults to
+ * true.
+ */
+ public void setDropTables(boolean dropTables) {
+ _dropTables = dropTables;
+ }
+
+ /**
+ * If true, sequences that appear to be unused will be dropped. Defaults
+ * to true.
+ */
+ public boolean getDropSequences() {
+ return _dropSeqs;
+ }
+
+ /**
+ * If true, sequences that appear to be unused will be dropped. Defaults
+ * to true.
+ */
+ public void setDropSequences(boolean dropSeqs) {
+ _dropSeqs = dropSeqs;
+ if (dropSeqs)
+ setSequences(true);
+ }
+
+ /**
+ * Whether sequences should be manipulated. Defaults to true.
+ */
+ public boolean getSequences() {
+ return _seqs;
+ }
+
+ /**
+ * Whether sequences should be manipulated. Defaults to true.
+ */
+ public void setSequences(boolean seqs) {
+ _seqs = seqs;
+ }
+
+ /**
+ * Whether indexes on existing tables should be manipulated.
+ * Defaults to true.
+ */
+ public boolean getIndexes() {
+ return _indexes;
+ }
+
+ /**
+ * Whether indexes on existing tables should be manipulated.
+ * Defaults to true.
+ */
+ public void setIndexes(boolean indexes) {
+ _indexes = indexes;
+ }
+
+ /**
+ * Whether foreign keys on existing tables should be manipulated.
+ * Defaults to true.
+ */
+ public boolean getForeignKeys() {
+ return _fks;
+ }
+
+ /**
+ * Whether foreign keys on existing tables should be manipulated.
+ * Defaults to true.
+ */
+ public void setForeignKeys(boolean fks) {
+ _fks = fks;
+ }
+
+ /**
+ * Whether primary keys on existing tables should be manipulated.
+ * Defaults to true.
+ */
+ public boolean getPrimaryKeys() {
+ return _pks;
+ }
+
+ /**
+ * Whether primary keys on existing tables should be manipulated.
+ * Defaults to true.
+ */
+ public void setPrimaryKeys(boolean pks) {
+ _pks = pks;
+ }
+
+ /**
+ * The stream to write to for the creation of SQL scripts. If the
+ * stream is non-null, all SQL will be written to this stream rather than
+ * executed against the database.
+ */
+ public Writer getWriter() {
+ return _writer;
+ }
+
+ /**
+ * The stream to write to for the creation of SQL scripts. If the
+ * stream is non-null, all SQL will be written to this stream rather than
+ * executed against the database.
+ */
+ public void setWriter(Writer writer) {
+ if (writer == null)
+ _writer = null;
+ else if (writer instanceof PrintWriter)
+ _writer = (PrintWriter) writer;
+ else
+ _writer = new PrintWriter(writer);
+ }
+
+ /**
+ * Return the schema group the tool will act on.
+ */
+ public SchemaGroup getSchemaGroup() {
+ return _group;
+ }
+
+ /**
+ * Set the schema group the tool will act on.
+ */
+ public void setSchemaGroup(SchemaGroup group) {
+ _group = group;
+ }
+
+ ///////////
+ // Actions
+ ///////////
+
+ /**
+ * Run the tool action.
+ */
+ public void run()
+ throws SQLException {
+ if (_action == null)
+ return;
+
+ if (ACTION_ADD.equals(_action))
+ add();
+ else if (ACTION_DROP.equals(_action))
+ drop();
+ else if (ACTION_RETAIN.equals(_action))
+ retain();
+ else if (ACTION_REFRESH.equals(_action))
+ refresh();
+ else if (ACTION_BUILD.equals(_action))
+ build();
+ else if (ACTION_CREATEDB.equals(_action))
+ createDB();
+ else if (ACTION_DROPDB.equals(_action))
+ dropDB();
+ }
+
+ /**
+ * Adds any components present in the schema repository but absent from
+ * the database. Package-private for testing.
+ */
+ void add()
+ throws SQLException {
+ add(getDBSchemaGroup(false), assertSchemaGroup());
+ }
+
+ /**
+ * Drops all schema components in the schema repository that also exist
+ * in the database. Package-private for testing.
+ */
+ void drop()
+ throws SQLException {
+ drop(getDBSchemaGroup(false), assertSchemaGroup());
+ }
+
+ /**
+ * Drops database components that are not mentioned in the schema
+ * repository. Package-private for testing.
+ */
+ void retain()
+ throws SQLException {
+ retain(getDBSchemaGroup(true), assertSchemaGroup(),
+ getDropTables(), getDropSequences());
+ }
+
+ /**
+ * Adds any components present in the schema repository but absent from
+ * the database, and drops unused database components.
+ * Package-private for testing.
+ */
+ void refresh()
+ throws SQLException {
+ SchemaGroup local = assertSchemaGroup();
+ SchemaGroup db = getDBSchemaGroup(true);
+ retain(db, local, getDropTables(), getDropSequences());
+ add(db, local);
+ }
+
+ /**
+ * Re-execute all SQL used for the creation of the current database;
+ * this action is usually used when creating SQL scripts.
+ * Package-private for testing.
+ */
+ void createDB()
+ throws SQLException {
+ SchemaGroup group = new SchemaGroup();
+ group.addSchema();
+ add(group, getDBSchemaGroup(true));
+ }
+
+ /**
+ * Re-execute all SQL used for the creation of the current database;
+ * this action is usually used when creating SQL scripts.
+ * Package-private for testing.
+ */
+ void build()
+ throws SQLException {
+ SchemaGroup group = new SchemaGroup();
+ group.addSchema();
+ add(group, assertSchemaGroup());
+ }
+
+ /**
+ * Drop the current database. Package-private for testing.
+ */
+ void dropDB()
+ throws SQLException {
+ retain(getDBSchemaGroup(true), new SchemaGroup(), true, true);
+ }
+
+ /**
+ * Record the changes made to the DB in the current {@link SchemaFactory}.
+ */
+ public void record() {
+ if (_db != null && _writer == null)
+ _conf.getSchemaFactoryInstance().storeSchema(_db);
+ }
+
+ /**
+ * Adds all database components in the repository schema that are not
+ * present in the given database schema to the database.
+ */
+ private void add(SchemaGroup db, SchemaGroup repos)
+ throws SQLException {
+ // add sequences
+ Schema[] schemas = repos.getSchemas();
+ Schema schema;
+ if (_seqs) {
+ Sequence[] seqs;
+ for (int i = 0; i < schemas.length; i++) {
+ seqs = schemas[i].getSequences();
+ for (int j = 0; j < seqs.length; j++) {
+ if (db.findSequence(seqs[j]) != null)
+ continue;
+
+ if (createSequence(seqs[j])) {
+ schema = db.getSchema(seqs[j].getSchemaName());
+ if (schema == null)
+ schema = db.addSchema(seqs[j].getSchemaName());
+ schema.importSequence(seqs[j]);
+ } else
+ _log.warn(_loc.get("add-seq", seqs[j]));
+ }
+ }
+ }
+
+ // order is important in this method; start with columns
+ Table[] tabs;
+ Table dbTable;
+ Column[] cols;
+ Column col;
+ for (int i = 0; i < schemas.length; i++) {
+ tabs = schemas[i].getTables();
+ for (int j = 0; j < tabs.length; j++) {
+ cols = tabs[j].getColumns();
+ dbTable = db.findTable(tabs[j]);
+ for (int k = 0; k < cols.length; k++) {
+ if (dbTable != null) {
+ col = dbTable.getColumn(cols[k].getName());
+ if (col == null) {
+ if (addColumn(cols[k]))
+ dbTable.importColumn(cols[k]);
+ else
+ _log.warn(_loc.get("add-col", cols[k],
+ tabs[j]));
+ } else if (!cols[k].equalsColumn(col)) {
+ _log.warn(_loc.get("bad-col", new Object[]{
+ col, dbTable, col.getDescription(),
+ cols[k].getDescription() }));
+ }
+ }
+ }
+ }
+ }
+
+ // primary keys
+ if (_pks) {
+ PrimaryKey pk;
+ for (int i = 0; i < schemas.length; i++) {
+ tabs = schemas[i].getTables();
+ for (int j = 0; j < tabs.length; j++) {
+ pk = tabs[j].getPrimaryKey();
+ dbTable = db.findTable(tabs[j]);
+ if (pk != null && !pk.isLogical() && dbTable != null) {
+ if (dbTable.getPrimaryKey() == null
+ && addPrimaryKey(pk))
+ dbTable.importPrimaryKey(pk);
+ else if (dbTable.getPrimaryKey() == null)
+ _log.warn(_loc.get("add-pk", pk, tabs[j]));
+ else if (!pk.equalsPrimaryKey(dbTable.getPrimaryKey()))
+ _log.warn(_loc.get("bad-pk",
+ dbTable.getPrimaryKey(), dbTable));
+ }
+ }
+ }
+ }
+
+ // tables
+ Set newTables = new HashSet();
+ for (int i = 0; i < schemas.length; i++) {
+ tabs = schemas[i].getTables();
+ for (int j = 0; j < tabs.length; j++) {
+ if (db.findTable(tabs[j]) != null)
+ continue;
+
+ if (createTable(tabs[j])) {
+ newTables.add(tabs[j]);
+ schema = db.getSchema(tabs[j].getSchemaName());
+ if (schema == null)
+ schema = db.addSchema(tabs[j].getSchemaName());
+ schema.importTable(tabs[j]);
+ } else
+ _log.warn(_loc.get("add-table", tabs[j]));
+ }
+ }
+
+ // indexes
+ Index[] idxs;
+ Index idx;
+ for (int i = 0; i < schemas.length; i++) {
+ tabs = schemas[i].getTables();
+ for (int j = 0; j < tabs.length; j++) {
+ // create indexes on new tables even if indexes
+ // have been turned off
+ if (!_indexes && !newTables.contains(tabs[j]))
+ continue;
+
+ idxs = tabs[j].getIndexes();
+ dbTable = db.findTable(tabs[j]);
+ for (int k = 0; k < idxs.length; k++) {
+ if (dbTable != null) {
+ idx = findIndex(dbTable, idxs[k]);
+ if (idx == null) {
+ if (createIndex(idxs[k], dbTable))
+ dbTable.importIndex(idxs[k]);
+ else
+ _log.warn(_loc.get("add-index", idxs[k],
+ tabs[j]));
+ } else if (!idxs[k].equalsIndex(idx))
+ _log.warn(_loc.get("bad-index", idx, dbTable));
+ }
+ }
+ }
+ }
+
+ // foreign keys
+ ForeignKey[] fks;
+ ForeignKey fk;
+ for (int i = 0; i < schemas.length; i++) {
+ tabs = schemas[i].getTables();
+ for (int j = 0; j < tabs.length; j++) {
+ // create foreign keys on new tables even if fks
+ // have been turned off
+ if (!_fks && !newTables.contains(tabs[j]))
+ continue;
+
+ fks = tabs[j].getForeignKeys();
+ dbTable = db.findTable(tabs[j]);
+ for (int k = 0; k < fks.length; k++) {
+ if (!fks[k].isLogical() && dbTable != null) {
+ fk = findForeignKey(dbTable, fks[k]);
+ if (fk == null) {
+ if (addForeignKey(fks[k]))
+ dbTable.importForeignKey(fks[k]);
+ else
+ _log.warn(_loc.get("add-fk",
+ fks[k], tabs[j]));
+ } else if (!fks[k].equalsForeignKey(fk))
+ _log.warn(_loc.get("bad-fk", fk, dbTable));
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Drops all database components that are in the given database schema
+ * but not in the repository schema.
+ */
+ private void retain(SchemaGroup db, SchemaGroup repos, boolean tables,
+ boolean sequences)
+ throws SQLException {
+ Schema[] schemas = db.getSchemas();
+ if (_seqs && sequences) {
+ Sequence[] seqs;
+ for (int i = 0; i < schemas.length; i++) {
+ seqs = schemas[i].getSequences();
+ for (int j = 0; j < seqs.length; j++) {
+ if (!isDroppable(seqs[j]))
+ continue;
+ if (repos.findSequence(seqs[j]) == null) {
+ if (dropSequence(seqs[j]))
+ schemas[i].removeSequence(seqs[j]);
+ else
+ _log.warn(_loc.get("drop-seq", seqs[j]));
+ }
+ }
+ }
+ }
+
+ // order is important in this method; start with foreign keys
+ Table[] tabs;
+ Table reposTable;
+ if (_fks) {
+ ForeignKey[] fks;
+ ForeignKey fk;
+ for (int i = 0; i < schemas.length; i++) {
+ tabs = schemas[i].getTables();
+ for (int j = 0; j < tabs.length; j++) {
+ if (!isDroppable(tabs[j]))
+ continue;
+ fks = tabs[j].getForeignKeys();
+ reposTable = repos.findTable(tabs[j]);
+ if (!tables && reposTable == null)
+ continue;
+
+ for (int k = 0; k < fks.length; k++) {
+ if (fks[k].isLogical())
+ continue;
+
+ fk = null;
+ if (reposTable != null)
+ fk = findForeignKey(reposTable, fks[k]);
+ if (reposTable == null || fk == null
+ || !fks[k].equalsForeignKey(fk)) {
+ if (dropForeignKey(fks[k]))
+ tabs[j].removeForeignKey(fks[k]);
+ else
+ _log.warn(_loc.get("drop-fk", fks[k],
+ tabs[j]));
+ }
+ }
+ }
+ }
+ }
+
+ // indexes
+ /*
+ if (_indexes)
+ {
+ Index[] idxs;
+ Index idx;
+ for (int i = 0; i < schemas.length; i++)
+ {
+ tabs = schemas[i].getTables ();
+ for (int j = 0; j < tabs.length; j++)
+ {
+ if (!isDroppable (tabs[j]))
+ continue;
+ idxs = tabs[j].getIndexes ();
+ reposTable = repos.findTable (tabs[j]);
+ if (!tables && reposTable == null)
+ continue;
+
+ for (int k = 0; k < idxs.length; k++)
+ {
+ idx = null;
+ if (reposTable != null)
+ idx = reposTable.getIndex (idxs[k].getName ());
+ if (reposTable == null || idx == null
+ || !idxs[k].equalsIndex (idx))
+ {
+ if (dropIndex (idxs[k]))
+ tabs[j].removeIndex (idxs[k]);
+ else
+ _log.warn (_loc.get ("drop-index", idxs[k],
+ tabs[j]));
+ }
+ }
+ }
+ }
+ }
+ */
+
+ // primary keys
+ if (_pks) {
+ PrimaryKey pk;
+ for (int i = 0; i < schemas.length; i++) {
+ tabs = schemas[i].getTables();
+ for (int j = 0; j < tabs.length; j++) {
+ if (!isDroppable(tabs[j]))
+ continue;
+ pk = tabs[j].getPrimaryKey();
+ if (pk != null && pk.isLogical())
+ continue;
+
+ reposTable = repos.findTable(tabs[j]);
+ if (pk != null && reposTable != null
+ && (reposTable.getPrimaryKey() == null
+ || !pk.equalsPrimaryKey(reposTable.getPrimaryKey()))) {
+ if (dropPrimaryKey(pk))
+ tabs[j].removePrimaryKey();
+ else
+ _log.warn(_loc.get("drop-pk", pk, tabs[j]));
+ }
+ }
+ }
+ }
+
+ // columns
+ Column[] cols;
+ Column col;
+ Collection drops = new LinkedList();
+ for (int i = 0; i < schemas.length; i++) {
+ tabs = schemas[i].getTables();
+ for (int j = 0; j < tabs.length; j++) {
+ if (!isDroppable(tabs[j]))
+ continue;
+ cols = tabs[j].getColumns();
+ reposTable = repos.findTable(tabs[j]);
+ if (reposTable != null) {
+ for (int k = 0; k < cols.length; k++) {
+ col = reposTable.getColumn(cols[k].getName());
+ if (col == null || !cols[k].equalsColumn(col)) {
+ if (tabs[j].getColumns().length == 1)
+ drops.add(tabs[j]);
+ else if (dropColumn(cols[k]))
+ tabs[j].removeColumn(cols[k]);
+ else
+ _log.warn(_loc.get("drop-col", cols[k],
+ tabs[j]));
+ }
+ }
+ }
+ }
+ }
+
+ // now tables
+ if (tables) {
+ for (int i = 0; i < schemas.length; i++) {
+ tabs = schemas[i].getTables();
+ for (int j = 0; j < tabs.length; j++)
+ if (!!isDroppable(tabs[j])
+ && repos.findTable(tabs[j]) == null)
+ drops.add(tabs[j]);
+ }
+ }
+ dropTables(drops, db);
+ }
+
+ /**
+ * Drops all database components in the given repository schema.
+ */
+ private void drop(SchemaGroup db, SchemaGroup repos)
+ throws SQLException {
+ // drop sequences
+ Schema[] schemas = repos.getSchemas();
+ if (_seqs) {
+ Sequence[] seqs;
+ Sequence dbSeq;
+ for (int i = 0; i < schemas.length; i++) {
+ seqs = schemas[i].getSequences();
+ for (int j = 0; j < seqs.length; j++) {
+ if (!isDroppable(seqs[j]))
+ continue;
+ dbSeq = db.findSequence(seqs[j]);
+ if (dbSeq != null) {
+ if (dropSequence(seqs[j]))
+ dbSeq.getSchema().removeSequence(dbSeq);
+ else
+ _log.warn(_loc.get("drop-seq", seqs[j]));
+ }
+ }
+ }
+ }
+
+ // calculate tables to drop; we can only drop tables if we're sure
+ // the user listed the entire table definition in the stuff they want
+ // dropped; else they might just want to drop a few columns
+ Collection drops = new LinkedList();
+ Table[] tabs;
+ Table dbTable;
+ Column[] dbCols;
+ for (int i = 0; i < schemas.length; i++) {
+ tabs = schemas[i].getTables();
+ tables:
+ for (int j = 0; j < tabs.length; j++) {
+ if (!isDroppable(tabs[j]))
+ continue;
+ dbTable = db.findTable(tabs[j]);
+ if (dbTable == null)
+ continue;
+
+ dbCols = dbTable.getColumns();
+ for (int k = 0; k < dbCols.length; k++)
+ if (tabs[j].getColumn(dbCols[k].getName()) == null)
+ continue tables;
+
+ drops.add(tabs[j]);
+ }
+ }
+
+ // order is important in this method; start with foreign keys mentioned
+ // in the drop schema
+ if (_fks) {
+ ForeignKey[] fks;
+ ForeignKey fk;
+ for (int i = 0; i < schemas.length; i++) {
+ tabs = schemas[i].getTables();
+ for (int j = 0; j < tabs.length; j++) {
+ if (!isDroppable(tabs[j]))
+ continue;
+ fks = tabs[j].getForeignKeys();
+ dbTable = db.findTable(tabs[j]);
+ for (int k = 0; k < fks.length; k++) {
+ if (fks[k].isLogical())
+ continue;
+
+ fk = null;
+ if (dbTable != null)
+ fk = findForeignKey(dbTable, fks[k]);
+ if (dbTable == null || fk == null)
+ continue;
+
+ if (dropForeignKey(fks[k]))
+ if (dbTable != null)
+ dbTable.removeForeignKey(fk);
+ else
+ _log.warn(_loc.get("drop-fk", fks[k], tabs[j]));
+ }
+ }
+ }
+
+ // also drop imported foreign keys for tables that will be dropped
+ Table tab;
+ for (Iterator itr = drops.iterator(); itr.hasNext();) {
+ tab = (Table) itr.next();
+ dbTable = db.findTable(tab);
+ if (dbTable == null)
+ continue;
+
+ fks = db.findExportedForeignKeys(dbTable.getPrimaryKey());
+ for (int i = 0; i < fks.length; i++) {
+ if (dropForeignKey(fks[i]))
+ dbTable.removeForeignKey(fks[i]);
+ else
+ _log.warn(_loc.get("drop-fk", fks[i], dbTable));
+ }
+ }
+ }
+
+ // indexes
+ /*
+ if (_indexes)
+ {
+ Index[] idxs;
+ Index idx;
+ for (int i = 0; i < schemas.length; i++)
+ {
+ tabs = schemas[i].getTables ();
+ for (int j = 0; j < tabs.length; j++)
+ {
+ if (!isDroppable (tabs[j]))
+ continue;
+ idxs = tabs[j].getIndexes ();
+ dbTable = db.findTable (tabs[j]);
+ for (int k = 0; k < idxs.length; k++)
+ {
+ idx = null;
+ if (dbTable != null)
+ idx = dbTable.getIndex (idxs[k].getName ());
+ if (dbTable == null || idx == null)
+ continue;
+
+ if (dropIndex (idxs[k]))
+ if (dbTable != null)
+ dbTable.removeIndex (idx);
+ else
+ _log.warn (_loc.get ("drop-index", idxs[k],
+ tabs[j]));
+ }
+ }
+ }
+ }
+ */
+
+ // drop the tables we calculated above
+ dropTables(drops, db);
+
+ // columns
+ Column[] cols;
+ Column col;
+ for (int i = 0; i < schemas.length; i++) {
+ tabs = schemas[i].getTables();
+ for (int j = 0; j < tabs.length; j++) {
+ if (!isDroppable(tabs[j]))
+ continue;
+ cols = tabs[j].getColumns();
+ dbTable = db.findTable(tabs[j]);
+ for (int k = 0; k < cols.length; k++) {
+ col = null;
+ if (dbTable != null)
+ col = dbTable.getColumn(cols[k].getName());
+ if (dbTable == null || col == null)
+ continue;
+
+ if (dropColumn(cols[k]))
+ if (dbTable != null)
+ dbTable.removeColumn(col);
+ else
+ _log.warn(_loc.get("drop-col", cols[k], tabs[j]));
+ }
+ }
+ }
+ }
+
+ /**
+ * Return true if the table is droppable.
+ */
+ private boolean isDroppable(Table table) {
+ return _openjpaTables
+ || (!table.getName().toUpperCase().startsWith("OPENJPA_")
+ && !table.getName().toUpperCase().startsWith("JDO_")); // legacy
+ }
+
+ /**
+ * Return true if the sequence is droppable.
+ */
+ private boolean isDroppable(Sequence seq) {
+ return _openjpaTables
+ || (!seq.getName().toUpperCase().startsWith("OPENJPA_")
+ && !seq.getName().toUpperCase().startsWith("JDO_")); // legacy
+ }
+
+ /**
+ * Find an index in the given table that matches the given one.
+ */
+ private Index findIndex(Table dbTable, Index idx) {
+ Index[] idxs = dbTable.getIndexes();
+ for (int i = 0; i < idxs.length; i++)
+ if (idx.columnsMatch(idxs[i].getColumns()))
+ return idxs[i];
+ return null;
+ }
+
+ /**
+ * Find a foreign key in the given table that matches the given one.
+ */
+ private ForeignKey findForeignKey(Table dbTable, ForeignKey fk) {
+ if (fk.getConstantColumns().length > 0
+ || fk.getConstantPrimaryKeyColumns().length > 0)
+ return null;
+ ForeignKey[] fks = dbTable.getForeignKeys();
+ for (int i = 0; i < fks.length; i++)
+ if (fk.columnsMatch(fks[i].getColumns(),
+ fks[i].getPrimaryKeyColumns()))
+ return fks[i];
+ return null;
+ }
+
+ /**
+ * Remove the given collection of tables from the database schema. Orders
+ * the removals according to foreign key constraints on the tables.
+ */
+ private void dropTables(Collection tables, SchemaGroup change)
+ throws SQLException {
+ if (tables.isEmpty())
+ return;
+
+ Collection nodes = tables;
+
+ Table table;
+ Table changeTable;
+ for (Iterator itr = nodes.iterator(); itr.hasNext();) {
+ table = (Table) itr.next();
+ if (dropTable(table)) {
+ changeTable = change.findTable(table);
+ if (changeTable != null)
+ changeTable.getSchema().removeTable(changeTable);
+ } else
+ _log.warn(_loc.get("drop-table", table));
+ }
+ }
+
+ /**
+ * Add the given table to the database schema.
+ *
+ * @return true if the operation was successful, false otherwise
+ */
+ public boolean createTable(Table table)
+ throws SQLException {
+ return executeSQL(_dict.getCreateTableSQL(table));
+ }
+
+ /**
+ * Drop the given table from the database schema.
+ *
+ * @return true if the operation was successful, false otherwise
+ */
+ public boolean dropTable(Table table)
+ throws SQLException {
+ return executeSQL(_dict.getDropTableSQL(table));
+ }
+
+ /**
+ * Add the given sequence to the database schema.
+ *
+ * @return true if the operation was successful, false otherwise
+ */
+ public boolean createSequence(Sequence seq)
+ throws SQLException {
+ return executeSQL(_dict.getCreateSequenceSQL(seq));
+ }
+
+ /**
+ * Drop the given sequence from the database schema.
+ *
+ * @return true if the operation was successful, false otherwise
+ */
+ public boolean dropSequence(Sequence seq)
+ throws SQLException {
+ return executeSQL(_dict.getDropSequenceSQL(seq));
+ }
+
+ /**
+ * Add the given index to the database schema.
+ *
+ * @return true if the operation was successful, false otherwise
+ */
+ public boolean createIndex(Index idx, Table table)
+ throws SQLException {
+ int max = _dict.maxIndexesPerTable;
+
+ int len = table.getIndexes().length;
+ if (table.getPrimaryKey() != null)
+ len += table.getPrimaryKey().getColumns().length;
+
+ if (len >= max) {
+ _log.warn(_loc.get("too-many-indexes", idx, table, max + ""));
+ return false;
+ }
+
+ return executeSQL(_dict.getCreateIndexSQL(idx));
+ }
+
+ /**
+ * Drop the given index from the database schema.
+ *
+ * @return true if the operation was successful, false otherwise
+ */
+ public boolean dropIndex(Index idx)
+ throws SQLException {
+ return executeSQL(_dict.getDropIndexSQL(idx));
+ }
+
+ /**
+ * Add the given column to the database schema.
+ *
+ * @return true if the operation was successful, false otherwise
+ */
+ public boolean addColumn(Column col)
+ throws SQLException {
+ return executeSQL(_dict.getAddColumnSQL(col));
+ }
+
+ /**
+ * Drop the given column from the database schema.
+ *
+ * @return true if the operation was successful, false otherwise
+ */
+ public boolean dropColumn(Column col)
+ throws SQLException {
+ return executeSQL(_dict.getDropColumnSQL(col));
+ }
+
+ /**
+ * Add the given primary key to the database schema.
+ *
+ * @return true if the operation was successful, false otherwise
+ */
+ public boolean addPrimaryKey(PrimaryKey pk)
+ throws SQLException {
+ return executeSQL(_dict.getAddPrimaryKeySQL(pk));
+ }
+
+ /**
+ * Drop the given primary key from the database schema.
+ *
+ * @return true if the operation was successful, false otherwise
+ */
+ public boolean dropPrimaryKey(PrimaryKey pk)
+ throws SQLException {
+ return executeSQL(_dict.getDropPrimaryKeySQL(pk));
+ }
+
+ /**
+ * Add the given foreign key to the database schema.
+ *
+ * @return true if the operation was successful, false otherwise
+ */
+ public boolean addForeignKey(ForeignKey fk)
+ throws SQLException {
+ return executeSQL(_dict.getAddForeignKeySQL(fk));
+ }
+
+ /**
+ * Drop the given foreign key from the database schema.
+ *
+ * @return true if the operation was successful, false otherwise
+ */
+ public boolean dropForeignKey(ForeignKey fk)
+ throws SQLException {
+ return executeSQL(_dict.getDropForeignKeySQL(fk));
+ }
+
+ /**
+ * Return the database schema.
+ */
+ public SchemaGroup getDBSchemaGroup() {
+ try {
+ return getDBSchemaGroup(true);
+ } catch (SQLException se) {
+ throw SQLExceptions.getStore(se, _dict);
+ }
+ }
+
+ /**
+ * Set the database schema.
+ */
+ public void setDBSchemaGroup(SchemaGroup db) {
+ _db = db;
+ if (db != null)
+ _fullDB = true;
+ }
+
+ /**
+ * Return the database schema.
+ *
+ * @param full if false, only the tables named in the set schema
+ * repository will be generated
+ */
+ private SchemaGroup getDBSchemaGroup(boolean full)
+ throws SQLException {
+ if (_db == null || (full && !_fullDB)) {
+ SchemaGenerator gen = new SchemaGenerator(_conf);
+ gen.setPrimaryKeys(_pks);
+ gen.setForeignKeys(_fks);
+ gen.setIndexes(_indexes);
+ if (full)
+ gen.generateSchemas();
+ else {
+ // generate only the tables in the given repository
+ // group; some may not exist yet, which is OK; we just need
+ // to make sure we can detect the changes to the ones that
+ // do exist
+ Collection tables = new LinkedList();
+ SchemaGroup group = assertSchemaGroup();
+ Schema[] schemas = group.getSchemas();
+ Table[] tabs;
+ for (int i = 0; i < schemas.length; i++) {
+ tabs = schemas[i].getTables();
+ for (int j = 0; j < tabs.length; j++) {
+ if (tabs[j].getSchemaName() == null)
+ tables.add("." + tabs[j].getName());
+ else
+ tables.add(tabs[j].getFullName());
+ }
+ }
+ if (!tables.isEmpty())
+ gen.generateSchemas((String[]) tables.toArray
+ (new String[tables.size()]));
+ }
+ _db = gen.getSchemaGroup();
+ }
+ return _db;
+ }
+
+ private SchemaGroup assertSchemaGroup() {
+ SchemaGroup local = getSchemaGroup();
+ if (local == null)
+ throw new InvalidStateException(_loc.get("tool-norepos"));
+ return local;
+ }
+
+ /////////////
+ // Utilities
+ /////////////
+
+ /**
+ * Executes the given array of non-selecting SQL statements, correctly
+ * logging the SQL calls and optionally ignoring errors.
+ *
+ * @return true if there was SQL to execute and the calls were
+ * successful, false otherwise
+ */
+ private boolean executeSQL(String[] sql)
+ throws SQLException {
+ // if no sql, probably b/c dictionary doesn't support operation
+ if (sql.length == 0)
+ return false;
+
+ boolean err = false;
+ if (_writer == null) {
+ // this is outside the try-catch because a failure here is
+ // really bad, and should not be ignored.
+ Connection conn = _ds.getConnection();
+ Statement statement = null;
+ boolean wasAuto = true;
+ try {
+ wasAuto = conn.getAutoCommit();
+ if (!wasAuto)
+ conn.setAutoCommit(true);
+ for (int i = 0; i < sql.length; i++) {
+ try {
+ // some connections require that rollback be
+ // called on the connection before any DDL statements
+ // can be run on it, even when autocommit is on.
+ // This is sometimes because the connection does not
+ // allow DDL statements when there are multiple
+ // commands issued on the connection, and the
+ // connection pool may have issued some validation SQL.
+ try {
+ conn.rollback();
+ } catch (Exception e) {
+ }
+
+ statement = conn.createStatement();
+ statement.executeUpdate(sql[i]);
+
+ // some connections seem to require an explicit
+ // commit for DDL statements, even when autocommit
+ // is on. The DataDirect drivers seem to suffer from
+ // this limitation.
+ try {
+ conn.commit();
+ } catch (Exception e) {
+ }
+ }
+ catch (SQLException se) {
+ err = true;
+ handleException(se);
+ } finally {
+ if (statement != null)
+ try {
+ statement.close();
+ } catch (SQLException se) {
+ }
+ }
+ }
+ }
+ finally {
+ if (!wasAuto)
+ conn.setAutoCommit(false);
+ try {
+ conn.close();
+ } catch (SQLException se) {
+ }
+ }
+ } else {
+ for (int i = 0; i < sql.length; i++)
+ _writer.println(sql[i] + ";");
+ _writer.flush();
+ }
+
+ return !err;
+ }
+
+ /**
+ * Handle the given exception, logging it and optionally ignoring it,
+ * depending on the flags this SchemaTool was created with.
+ */
+ private void handleException(SQLException sql)
+ throws SQLException {
+ if (!_ignoreErrs)
+ throw sql;
+ _log.warn(sql.getMessage(), sql);
+ }
+
+ ////////
+ // Main
+ ////////
+
+ /**
+ * Usage: java org.apache.openjpa.jdbc.schema.SchemaTool [option]*
+ * [-action/-a <add | retain | drop | refresh | createDB | dropDB
+ * | build | reflect | import | export>]
+ * <.schema file or resource>*
+ * Where the following options are recognized.
+ * <ul>
+ * <li><i>-properties/-p <properties file or resource></i>: The
+ * path or resource name of a OpenJPA properties file containing
+ * information such as the license key and connection data as
+ * outlined in {@link JDBCConfiguration}. Optional.</li>
+ * <li><i>-<property name> <property value></i>: All bean
+ * properties of the OpenJPA {@link JDBCConfiguration} can be set by
+ * using their names and supplying a value. For example:
+ * <code>-licenseKey adslfja83r3lkadf</code></li>
+ * <li><i>-ignoreErrors/-i <true/t | false/f></i>: If false, an
+ * exception will will be thrown if the tool encounters any database
+ * exceptions; defaults to <code>false</code>.</li>
+ * <li><i>-file/-f <stdout | output file or resource></i>: Use this
+ * option to write a SQL script for the planned schema modifications,
+ * rather than committing them to the database. This option also
+ * applies to the <code>export</code> and <code>reflect</code> actions.</li>
+ * <li><i>-openjpaTables/-kt <true/t | false/f></i>: Under the
+ * <code>reflect</code> action, whether to reflect on tables with
+ * the name <code>OPENJPA_*</code>. Under other actions, whether to
+ * drop such tables. Defaults to <code>false</code>.</li>
+ * <li><i>-dropTables/-dt <true/t | false/f></i>: Set this option to
+ * true to drop tables that appear to be unused during
+ * <code>retain</code> and <code>refresh</code> actions. Defaults to
+ * <code>true</code>.</li>
+ * <li><i>-dropSequences/-dsq <true/t | false/f></i>: Set this option
+ * to true to drop sequences that appear to be unused during
+ * <code>retain</code> and <code>refresh</code> actions. Defaults to
+ * <code>true</code>.</li>
+ * <li><i>-primaryKeys/-pk <true/t | false/f></i>: Whether primary
+ * keys on existing tables are manipulated. Defaults to true.</li>
+ * <li><i>-foreignKeys/-fk <true/t | false/f></i>: Whether foreign
+ * keys on existing tables are manipulated. Defaults to true.</li>
+ * <li><i>-indexes/-ix <true/t | false/f></i>: Whether indexes
+ * on existing tables are manipulated. Defaults to true.</li>
+ * <li><i>-sequences/-sq <true/t | false/f></i>: Whether to
+ * manipulate sequences. Defaults to true.</li>
+ * <li><i>-record/-r <true/t | false/f></i>: Set this option to
+ * <code>false</code> to prevent writing the schema changes to the
+ * current {@link SchemaFactory}.</li>
+ * </ul>
+ * The various actions are as follows.
+ * <ul>
+ * <li><i>add</i>: Bring the schema up-to-date with the latest
+ * changes to the schema XML data by adding tables, columns,
+ * indexes, etc. This action never drops any data. This is the
+ * default action.</li>
+ * <li><i>retain</i>: Keep all schema components in the schema XML, but
+ * drop the rest from the database. This action never adds any data.</li>
+ * <li><i>drop</i>: Drop all the schema components in the schema XML.</li>
+ * <li><i>refresh</i>: Equivalent to retain, then add.</li>
+ * <li><i>createDB</i>: Execute SQL to re-create the current database.
+ * This action is typically used in conjuction with the
+ * <code>file</code> option.</li>
+ * <li><i>build</i>: Execute SQL to build the schema defined in the XML.
+ * Because it doesn't take the current database schema into account,
+ * this action is typically used in conjuction with the
+ * <code>file</code> option.</li>
+ * <li><i>reflect</i>: Reflect on the current database schema. Write the
+ * schema's XML representation to the file specified with the
+ * <code>file</code> option, or to stdout if no file is given.</li>
+ * <li><i>dropDB</i>: Execute SQL to drop the current database. This
+ * action implies <code>dropTables</code>.</li>
+ * <li><i>import</i>: Import the given XML schema definition into the
+ * current {@link SchemaFactory}.</li>
+ * <li><i>export</i>: Export the current {@link SchemaFactory}'s recorded
+ * schema to an XML schema definition file.</li>
+ * </ul>
+ * Examples:
+ * <ul>
+ * <li>Write a script to stdout to re-create the current database
+ * schema:<br />
+ * <code>java org.apache.openjpa.jdbc.schema.SchemaTool -f stdout -a createDB</code>
+ * <li>Drop the current database schema:<br />
+ * <code>java org.apache.openjpa.jdbc.schema.SchemaTool -a dropDB</code></li>
+ * <li>Create a schema based on an XML schema definition file:<br />
+ * <code>java org.apache.openjpa.jdbc.schema.SchemaTool myschema.xml</code></li>
+ * </ul>
+ */
+ public static void main(String[] args)
+ throws IOException, SQLException {
+ Options opts = new Options();
+ args = opts.setFromCmdLine(args);
+ JDBCConfiguration conf = new JDBCConfigurationImpl();
+ try {
+ if (!run(conf, args, opts))
+ System.out.println(_loc.get("tool-usage"));
+ } finally {
+ conf.close();
+ }
+ }
+
+ /**
+ * Run the tool. Returns false if any invalid options were given.
+ *
+ * @see #main
+ */
+ public static boolean run(JDBCConfiguration conf, String[] args,
+ Options opts)
+ throws IOException, SQLException {
+ if (opts.containsKey("help") || opts.containsKey("-help"))
+ return false;
+
+ Flags flags = new Flags();
+ flags.dropTables = opts.removeBooleanProperty
+ ("dropTables", "dt", flags.dropTables);
+ flags.dropSequences = opts.removeBooleanProperty
+ ("dropSequences", "dsq", flags.dropSequences);
+ flags.ignoreErrors = opts.removeBooleanProperty
+ ("ignoreErrors", "i", flags.ignoreErrors);
+ flags.openjpaTables = opts.removeBooleanProperty
+ ("openjpaTables", "kt", flags.openjpaTables);
+ flags.primaryKeys = opts.removeBooleanProperty
+ ("primaryKeys", "pk", flags.primaryKeys);
+ flags.foreignKeys = opts.removeBooleanProperty
+ ("foreignKeys", "fks", flags.foreignKeys);
+ flags.indexes = opts.removeBooleanProperty
+ ("indexes", "ix", flags.indexes);
+ flags.sequences = opts.removeBooleanProperty
+ ("sequences", "sq", flags.sequences);
+ flags.record = opts.removeBooleanProperty("record", "r", flags.record);
+ flags.action = opts.removeProperty("action", "a", flags.action);
+ String fileName = opts.removeProperty("file", "f", null);
+ String schemas = opts.removeProperty("s");
+ if (schemas != null)
+ opts.setProperty("schemas", schemas);
+
+ // setup a configuration instance with cmd-line info
+ Configurations.populateConfiguration(conf, opts);
+
+ // create script writer
+ ClassLoader loader = conf.getClassResolverInstance().
+ getClassLoader(SchemaTool.class, null);
+ flags.writer = Files.getWriter(fileName, loader);
+
+ return run(conf, args, flags, loader);
+ }
+
+ /**
+ * Run the tool. Return false if invalid options were given.
+ */
+ public static boolean run(JDBCConfiguration conf, String[] args,
+ Flags flags, ClassLoader loader)
+ throws IOException, SQLException {
+ Log log = conf.getLog(OpenJPAConfiguration.LOG_TOOL);
+ if (ACTION_REFLECT.equals(flags.action)) {
+ if (args.length > 0)
+ return false;
+ if (flags.writer == null)
+ flags.writer = new PrintWriter(System.out);
+
+ SchemaGenerator gen = new SchemaGenerator(conf);
+ gen.setPrimaryKeys(flags.primaryKeys);
+ gen.setIndexes(flags.indexes);
+ gen.setForeignKeys(flags.foreignKeys);
+ gen.setSequences(flags.sequences);
+ gen.setOpenJPATables(flags.openjpaTables);
+
+ String schemas = conf.getSchemas();
+ if (schemas == null || schemas.length() == 0)
+ schemas = "all";
+ log.info(_loc.get("sch-reflect", schemas));
+ gen.generateSchemas();
+
+ // record the schema
+ log.info(_loc.get("sch-reflect-write"));
+ SchemaSerializer ser = new XMLSchemaSerializer(conf);
+ ser.addAll(gen.getSchemaGroup());
+ ser.serialize(flags.writer, ser.PRETTY);
+ return true;
+ }
+
+ if (args.length == 0
+ && !ACTION_CREATEDB.equals(flags.action)
+ && !ACTION_DROPDB.equals(flags.action)
+ && !ACTION_EXPORT.equals(flags.action))
+ return false;
+
+ // parse in the arguments
+ SchemaParser parser = new XMLSchemaParser(conf);
+ parser.setDelayConstraintResolve(true);
+ File file;
+ for (int i = 0; i < args.length; i++) {
+ file = Files.getFile(args[i], loader);
+ log.info(_loc.get("tool-running", file));
+ parser.parse(file);
+ }
+ parser.resolveConstraints();
+
+ if (ACTION_IMPORT.equals(flags.action)) {
+ log.info(_loc.get("tool-import-store"));
+ SchemaGroup schema = parser.getSchemaGroup();
+ conf.getSchemaFactoryInstance().storeSchema(schema);
+ return true;
+ }
+ if (ACTION_EXPORT.equals(flags.action)) {
+ if (flags.writer == null)
+ flags.writer = new PrintWriter(System.out);
+
+ log.info(_loc.get("tool-export-gen"));
+ SchemaGroup schema = conf.getSchemaFactoryInstance().readSchema();
+
+ log.info(_loc.get("tool-export-write"));
+ SchemaSerializer ser = new XMLSchemaSerializer(conf);
+ ser.addAll(schema);
+ ser.serialize(flags.writer, ser.PRETTY);
+ return true;
+ }
+
+ SchemaTool tool = new SchemaTool(conf, flags.action);
+ tool.setIgnoreErrors(flags.ignoreErrors);
+ tool.setDropTables(flags.dropTables);
+ tool.setSequences(flags.sequences); // set before dropseqs
+ tool.setDropSequences(flags.dropSequences);
+ tool.setPrimaryKeys(flags.primaryKeys);
+ tool.setForeignKeys(flags.foreignKeys);
+ tool.setIndexes(flags.indexes);
+ tool.setOpenJPATables(flags.openjpaTables);
+ if (args.length > 0)
+ tool.setSchemaGroup(parser.getSchemaGroup());
+ if (flags.writer != null)
+ tool.setWriter(flags.writer);
+
+ log.info(_loc.get("tool-action", flags.action));
+ try {
+ tool.run();
+ } finally {
+ if (flags.record) {
+ log.info(_loc.get("tool-record"));
+ tool.record();
+ }
+ }
+ if (flags.writer != null)
+ flags.writer.flush();
+
+ return true;
+ }
+
+ /**
+ * Run flags.
+ */
+ public static class Flags {
+
+ public String action = ACTION_ADD;
+ public Writer writer = null;
+ public boolean dropTables = true;
+ public boolean dropSequences = true;
+ public boolean ignoreErrors = false;
+ public boolean openjpaTables = false;
+ public boolean primaryKeys = true;
+ public boolean foreignKeys = true;
+ public boolean indexes = true;
+ public boolean sequences = true;
+ public boolean record = true;
+ }
+}
Propchange: incubator/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/schema/SchemaTool.java
------------------------------------------------------------------------------
svn:executable = *
Added: incubator/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/schema/Schemas.java
URL: http://svn.apache.org/viewvc/incubator/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/schema/Schemas.java?rev=423615&view=auto
==============================================================================
--- incubator/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/schema/Schemas.java (added)
+++ incubator/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/schema/Schemas.java Wed Jul 19 14:34:44 2006
@@ -0,0 +1,232 @@
+/*
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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 org.apache.openjpa.jdbc.schema;
+
+import java.sql.Types;
+import java.util.Date;
+
+import org.apache.openjpa.jdbc.conf.JDBCConfiguration;
+
+/**
+ * Helper class to deal with schemas.
+ *
+ * @author Abe White
+ * @nojavadoc
+ */
+public class Schemas {
+
+ public static final Column[] EMPTY_COLUMNS = new Column[0];
+ public static final ForeignKey[] EMPTY_FOREIGN_KEYS = new ForeignKey[0];
+ public static final Index[] EMPTY_INDEXES = new Index[0];
+ public static final Unique[] EMPTY_UNIQUES = new Unique[0];
+ public static final Object[] EMPTY_VALUES = new Object[0];
+
+ /**
+ * Return the schema name that should be used for new tables, or null if
+ * none.
+ */
+ public static String getNewTableSchema(JDBCConfiguration conf) {
+ if (conf.getSchema() != null)
+ return conf.getSchema();
+
+ String[] schemas = conf.getSchemasList();
+ if (schemas.length == 0)
+ return null;
+ int dotIdx = schemas[0].lastIndexOf('.');
+ if (dotIdx == 0)
+ return null;
+ if (dotIdx == -1)
+ return schemas[0];
+ return schemas[0].substring(0, dotIdx);
+ }
+
+ /**
+ * Return the SQL type name for the given {@link Types} constant.
+ */
+ public static String getJDBCName(int type) {
+ switch (type) {
+ case Types.ARRAY:
+ return "array";
+ case Types.BIGINT:
+ return "bigint";
+ case Types.BINARY:
+ return "binary";
+ case Types.BIT:
+ return "bit";
+ case Types.BLOB:
+ return "blob";
+ case Types.CHAR:
+ return "char";
+ case Types.CLOB:
+ return "clob";
+ case Types.DATE:
+ return "date";
+ case Types.DECIMAL:
+ return "decimal";
+ case Types.DISTINCT:
+ return "distinct";
+ case Types.DOUBLE:
+ return "double";
+ case Types.FLOAT:
+ return "float";
+ case Types.INTEGER:
+ return "integer";
+ case Types.JAVA_OBJECT:
+ return "java_object";
+ case Types.LONGVARBINARY:
+ return "longvarbinary";
+ case Types.LONGVARCHAR:
+ return "longvarchar";
+ case Types.NULL:
+ return "null";
+ case Types.NUMERIC:
+ return "numeric";
+ case Types.OTHER:
+ return "other";
+ case Types.REAL:
+ return "real";
+ case Types.REF:
+ return "ref";
+ case Types.SMALLINT:
+ return "smallint";
+ case Types.STRUCT:
+ return "struct";
+ case Types.TIME:
+ return "time";
+ case Types.TIMESTAMP:
+ return "timestamp";
+ case Types.TINYINT:
+ return "tinyint";
+ case Types.VARBINARY:
+ return "varbinary";
+ case Types.VARCHAR:
+ return "varchar";
+ default:
+ return "unknown(" + type + ")";
+ }
+ }
+
+ /**
+ * Return the {@link Types} constant for the given SQL type name.
+ */
+ public static int getJDBCType(String name) {
+ if ("array".equalsIgnoreCase(name))
+ return Types.ARRAY;
+ if ("bigint".equalsIgnoreCase(name))
+ return Types.BIGINT;
+ if ("binary".equalsIgnoreCase(name))
+ return Types.BINARY;
+ if ("bit".equalsIgnoreCase(name))
+ return Types.BIT;
+ if ("blob".equalsIgnoreCase(name))
+ return Types.BLOB;
+ if ("char".equalsIgnoreCase(name))
+ return Types.CHAR;
+ if ("clob".equalsIgnoreCase(name))
+ return Types.CLOB;
+ if ("date".equalsIgnoreCase(name))
+ return Types.DATE;
+ if ("decimal".equalsIgnoreCase(name))
+ return Types.DECIMAL;
+ if ("distinct".equalsIgnoreCase(name))
+ return Types.DISTINCT;
+ if ("double".equalsIgnoreCase(name))
+ return Types.DOUBLE;
+ if ("float".equalsIgnoreCase(name))
+ return Types.FLOAT;
+ if ("integer".equalsIgnoreCase(name))
+ return Types.INTEGER;
+ if ("java_object".equalsIgnoreCase(name))
+ return Types.JAVA_OBJECT;
+ if ("longvarbinary".equalsIgnoreCase(name))
+ return Types.LONGVARBINARY;
+ if ("longvarchar".equalsIgnoreCase(name))
+ return Types.LONGVARCHAR;
+ if ("null".equalsIgnoreCase(name))
+ return Types.NULL;
+ if ("numeric".equalsIgnoreCase(name))
+ return Types.NUMERIC;
+ if ("other".equalsIgnoreCase(name))
+ return Types.OTHER;
+ if ("real".equalsIgnoreCase(name))
+ return Types.REAL;
+ if ("ref".equalsIgnoreCase(name))
+ return Types.REF;
+ if ("smallint".equalsIgnoreCase(name))
+ return Types.SMALLINT;
+ if ("struct".equalsIgnoreCase(name))
+ return Types.STRUCT;
+ if ("time".equalsIgnoreCase(name))
+ return Types.TIME;
+ if ("timestamp".equalsIgnoreCase(name))
+ return Types.TIMESTAMP;
+ if ("tinyint".equalsIgnoreCase(name))
+ return Types.TINYINT;
+ if ("varbinary".equalsIgnoreCase(name))
+ return Types.VARBINARY;
+ if ("varchar".equalsIgnoreCase(name))
+ return Types.VARCHAR;
+ if (name == null || name.toLowerCase().startsWith("unknown"))
+ return Types.OTHER;
+ throw new IllegalArgumentException("name = " + name);
+ }
+
+ /**
+ * Return the java type for the given SQL type from {@link Types}.
+ */
+ public static Class getJavaType(int type, int size, int decimals) {
+ switch (type) {
+ case Types.CHAR:
+ if (size == 1)
+ return char.class;
+ // no break
+ case Types.VARCHAR:
+ case Types.LONGVARCHAR:
+ case Types.CLOB:
+ return String.class;
+ case Types.BIT:
+ return boolean.class;
+ case Types.TINYINT:
+ return byte.class;
+ case Types.SMALLINT:
+ return short.class;
+ case Types.INTEGER:
+ return int.class;
+ case Types.BIGINT:
+ return long.class;
+ case Types.REAL:
+ case Types.FLOAT:
+ return float.class;
+ case Types.DOUBLE:
+ case Types.NUMERIC:
+ return double.class;
+ case Types.DECIMAL:
+ // oracle uses this for everything, so look at size and decimals
+ if (decimals == 0 && size < 10)
+ return int.class;
+ else if (decimals == 0)
+ return long.class;
+ return double.class;
+ // ### return a BigDecimal if the size if out of double range?
+ case Types.DATE:
+ case Types.TIME:
+ case Types.TIMESTAMP:
+ return Date.class;
+ default:
+ return Object.class;
+ }
+ }
+}
Propchange: incubator/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/schema/Schemas.java
------------------------------------------------------------------------------
svn:executable = *
Added: incubator/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/schema/Sequence.java
URL: http://svn.apache.org/viewvc/incubator/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/schema/Sequence.java?rev=423615&view=auto
==============================================================================
--- incubator/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/schema/Sequence.java (added)
+++ incubator/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/schema/Sequence.java Wed Jul 19 14:34:44 2006
@@ -0,0 +1,209 @@
+/*
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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 org.apache.openjpa.jdbc.schema;
+
+import java.io.File;
+
+import org.apache.openjpa.lib.meta.SourceTracker;
+import org.apache.openjpa.lib.util.Localizer;
+
+/**
+ * Represents a database sequence.
+ *
+ * @author Abe White
+ */
+public class Sequence
+ extends ReferenceCounter
+ implements Comparable, SourceTracker {
+
+ private static final Localizer _loc = Localizer.forPackage(Sequence.class);
+
+ private String _name = null;
+ private String _fullName = null;
+ private Schema _schema = null;
+ private String _schemaName = null;
+ private int _initial = 1;
+ private int _increment = 1;
+ private int _cache = 0;
+
+ // keep track of source
+ private File _source = null;
+ private int _srcType = SRC_OTHER;
+
+ /**
+ * Default constructor.
+ */
+ public Sequence() {
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param name the sequence name
+ * @param schema the sequence schema
+ */
+ public Sequence(String name, Schema schema) {
+ setName(name);
+ if (schema != null)
+ setSchemaName(schema.getName());
+ _schema = schema;
+ }
+
+ /**
+ * Called when the sequence is removed from its schema.
+ */
+ void remove() {
+ _schema = null;
+ _fullName = null;
+ }
+
+ /**
+ * Return the schema for the sequence.
+ */
+ public Schema getSchema() {
+ return _schema;
+ }
+
+ /**
+ * The sequence's schema name.
+ */
+ public String getSchemaName() {
+ return _schemaName;
+ }
+
+ /**
+ * The sequence's schema name. You can only call this method on sequences
+ * whose schema object is not set.
+ */
+ public void setSchemaName(String name) {
+ if (getSchema() != null)
+ throw new IllegalStateException();
+ _schemaName = name;
+ _fullName = null;
+ }
+
+ /**
+ * Return the name of the sequence.
+ */
+ public String getName() {
+ return _name;
+ }
+
+ /**
+ * Set the name of the sequence. This method can only be called on
+ * sequences that are not part of a schema.
+ */
+ public void setName(String name) {
+ if (getSchema() != null)
+ throw new IllegalStateException();
+ _name = name;
+ _fullName = null;
+ }
+
+ /**
+ * Return the sequence name, including schema, using '.' as the
+ * catalog separator.
+ */
+ public String getFullName() {
+ if (_fullName == null) {
+ Schema schema = getSchema();
+ if (schema == null || schema.getName() == null)
+ _fullName = getName();
+ else
+ _fullName = schema.getName() + "." + getName();
+ }
+ return _fullName;
+ }
+
+ /**
+ * The sequence's initial value.
+ */
+ public int getInitialValue() {
+ return _initial;
+ }
+
+ /**
+ * The sequence's initial value.
+ */
+ public void setInitialValue(int initial) {
+ _initial = initial;
+ }
+
+ /**
+ * The sequence's increment.
+ */
+ public int getIncrement() {
+ return _increment;
+ }
+
+ /**
+ * The sequence's increment.
+ */
+ public void setIncrement(int increment) {
+ _increment = increment;
+ }
+
+ /**
+ * The sequence's cache size.
+ */
+ public int getAllocate() {
+ return _cache;
+ }
+
+ /**
+ * The sequence's cache size.
+ */
+ public void setAllocate(int cache) {
+ _cache = cache;
+ }
+
+ public File getSourceFile() {
+ return _source;
+ }
+
+ public Object getSourceScope() {
+ return null;
+ }
+
+ public int getSourceType() {
+ return _srcType;
+ }
+
+ public void setSource(File source, int srcType) {
+ _source = source;
+ _srcType = srcType;
+ }
+
+ public String getResourceName() {
+ return getFullName();
+ }
+
+ public int compareTo(Object other) {
+ String name = getFullName();
+ String otherName = ((Sequence) other).getFullName();
+ if (name == null && otherName == null)
+ return 0;
+ if (name == null)
+ return 1;
+ if (otherName == null)
+ return -1;
+ return name.compareTo(otherName);
+ }
+
+ public String toString() {
+ return getFullName();
+ }
+}
Propchange: incubator/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/schema/Sequence.java
------------------------------------------------------------------------------
svn:executable = *
Added: incubator/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/schema/SimpleDriverDataSource.java
URL: http://svn.apache.org/viewvc/incubator/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/schema/SimpleDriverDataSource.java?rev=423615&view=auto
==============================================================================
--- incubator/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/schema/SimpleDriverDataSource.java (added)
+++ incubator/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/schema/SimpleDriverDataSource.java Wed Jul 19 14:34:44 2006
@@ -0,0 +1,180 @@
+/*
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * 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 org.apache.openjpa.jdbc.schema;
+
+import java.io.PrintWriter;
+import java.sql.Connection;
+import java.sql.Driver;
+import java.sql.DriverManager;
+import java.sql.SQLException;
+import java.util.List;
+import java.util.Properties;
+
+import org.apache.openjpa.jdbc.conf.JDBCConfiguration;
+import org.apache.openjpa.jdbc.sql.DBDictionary;
+import org.apache.openjpa.lib.conf.Configuration;
+
+public class SimpleDriverDataSource
+ implements DriverDataSource {
+
+ private String _connectionDriverName;
+ private String _connectionURL;
+ private String _connectionUserName;
+ private String _connectionPassword;
+ private Properties _connectionProperties;
+ private Properties _connectionFactoryProperties;
+ private JDBCConfiguration _conf;
+ private Driver _driver;
+ private ClassLoader _classLoader;
+
+ private synchronized Driver getDriver()
+ throws SQLException {
+ if (_driver == null)
+ _driver = DriverManager.getDriver(_connectionURL);
+
+ if (_driver == null) {
+ try {
+ Class.forName(_connectionDriverName, true, _classLoader);
+ } catch (Exception e) {
+ }
+
+ _driver = DriverManager.getDriver(_connectionURL);
+ }
+
+ if (_driver == null) {
+ try {
+ _driver = (Driver) Class.forName(_connectionDriverName,
+ true, _classLoader).newInstance();
+ } catch (Exception e) {
+ if (e instanceof SQLException)
+ throw(SQLException) e;
+
+ if (e instanceof RuntimeException)
+ throw(RuntimeException) e;
+
+ throw new SQLException(e.getClass() + ": " + e.getMessage());
+ }
+ }
+
+ return _driver;
+ }
+
+ public Connection getConnection()
+ throws SQLException {
+ return getConnection(null);
+ }
+
+ public Connection getConnection(String username, String password)
+ throws SQLException {
+ Properties props = new Properties();
+
+ props.put("user",
+ username != null ? username : _connectionUserName);
+ props.put("password",
+ password != null ? password : _connectionPassword);
+
+ return getConnection(props);
+ }
+
+ public Connection getConnection(Properties props)
+ throws SQLException {
+ return getDriver().connect(_conf.getConnectionURL(), props);
+ }
+
+ public int getLoginTimeout() {
+ return 0;
+ }
+
+ public void setLoginTimeout(int seconds) {
+ }
+
+ public PrintWriter getLogWriter() {
+ return DriverManager.getLogWriter();
+ }
+
+ public void setLogWriter(PrintWriter out) {
+ }
+
+ public void startConfiguration() {
+ }
+
+ public void endConfiguration() {
+ }
+
+ public void setConfiguration(Configuration conf) {
+ _conf = (JDBCConfiguration) conf;
+ }
+
+ public void initDBDictionary(DBDictionary dict) {
+ }
+
+ public void setConnectionURL(String connectionURL) {
+ _connectionURL = connectionURL;
+ }
+
+ public String getConnectionURL() {
+ return _connectionURL;
+ }
+
+ public void setConnectionUserName(String connectionUserName) {
+ _connectionUserName = connectionUserName;
+ }
+
+ public String getConnectionUserName() {
+ return _connectionUserName;
+ }
+
+ public void setConnectionPassword(String connectionPassword) {
+ _connectionPassword = connectionPassword;
+ }
+
+ public void setConnectionProperties(Properties props) {
+ _connectionProperties = props;
+ }
+
+ public Properties getConnectionProperties() {
+ return _connectionProperties;
+ }
+
+ public void setConnectionFactoryProperties(Properties props) {
+ _connectionFactoryProperties = props;
+ }
+
+ public Properties getConnectionFactoryProperties() {
+ return _connectionFactoryProperties;
+ }
+
+ public List createConnectionDecorators() {
+ return null;
+ }
+
+ public void setClassLoader(ClassLoader classLoader) {
+ _classLoader = classLoader;
+ }
+
+ public ClassLoader getClassLoader() {
+ return _classLoader;
+ }
+
+ public void setConnectionDriverName(String connectionDriverName) {
+ _connectionDriverName = connectionDriverName;
+ }
+
+ public String getConnectionDriverName() {
+ return _connectionDriverName;
+ }
+}
+
Propchange: incubator/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/schema/SimpleDriverDataSource.java
------------------------------------------------------------------------------
svn:executable = *