0001: /*
0002: * Copyright (c) 1998 - 2005 Versant Corporation
0003: * All rights reserved. This program and the accompanying materials
0004: * are made available under the terms of the Eclipse Public License v1.0
0005: * which accompanies this distribution, and is available at
0006: * http://www.eclipse.org/legal/epl-v10.html
0007: *
0008: * Contributors:
0009: * Versant Corporation - initial API and implementation
0010: */
0011: package com.versant.core.jdbc.sql;
0012:
0013: import com.versant.core.jdbc.metadata.*;
0014: import com.versant.core.jdbc.sql.exp.*;
0015: import com.versant.core.jdbc.sql.conv.*;
0016: import com.versant.core.jdbc.sql.diff.*;
0017: import com.versant.core.jdbc.JdbcConverterFactory;
0018: import com.versant.core.jdbc.JdbcMetaDataBuilder;
0019: import com.versant.core.jdbc.JdbcUtils;
0020: import com.versant.core.jdbc.conn.StatementWithLastSQL;
0021: import com.versant.core.util.CharBuf;
0022: import com.versant.core.util.classhelper.ClassHelper;
0023: import com.versant.core.jdo.query.AggregateNode;
0024: import com.versant.core.metadata.MDStatics;
0025: import com.versant.core.metadata.MDStaticUtils;
0026: import com.versant.core.metadata.ClassMetaData;
0027:
0028: import java.util.*;
0029: import java.util.Date;
0030: import java.sql.*;
0031: import java.math.BigDecimal;
0032: import java.math.BigInteger;
0033: import java.io.PrintWriter;
0034: import java.io.File;
0035: import java.net.URL;
0036: import java.text.DecimalFormatSymbols;
0037: import java.text.DecimalFormat;
0038: import java.text.NumberFormat;
0039:
0040: import com.versant.core.common.BindingSupportImpl;
0041: import com.versant.core.common.Debug;
0042:
0043: /**
0044: * This is the base class for the classes responsible for generating SQL
0045: * for different databases and interfacing to JDBC drivers. There is normally
0046: * one shared instance per Store. This class is also responsible for creating
0047: * columns for fields and so on during meta data generation.<p>
0048: */
0049: public abstract class SqlDriver {
0050:
0051: public static final char[] DEFAULT_PARAM_CHARS = new char[] { '?' };
0052:
0053: /**
0054: * These are all the JDBC type codes that we care about. All SqlDriver
0055: * subclasses must provide a mapping for each of these.
0056: */
0057: public static final int[] JDBC_TYPES = new int[] { Types.BIT,
0058: Types.TINYINT, Types.SMALLINT, Types.INTEGER, Types.BIGINT,
0059: Types.FLOAT, Types.REAL, Types.DOUBLE, Types.NUMERIC,
0060: Types.DECIMAL, Types.CHAR, Types.VARCHAR,
0061: Types.LONGVARCHAR, Types.DATE, Types.TIME, Types.TIMESTAMP,
0062: Types.BINARY, Types.VARBINARY, Types.LONGVARBINARY,
0063: Types.BLOB, Types.CLOB, };
0064:
0065: protected BytesConverter.Factory bytesConverterFactory = new BytesConverter.Factory();
0066: protected NullBytesAsBinaryConverter.Factory nullBytesAsBinaryConverterFactory = new NullBytesAsBinaryConverter.Factory();
0067:
0068: protected static final int COMMENT_COL = 40;
0069:
0070: private static char[] FOR_UPDATE = " FOR UPDATE".toCharArray();
0071:
0072: private ArrayList allTableList = null;
0073:
0074: protected static DecimalFormat doubleFormat;
0075:
0076: public SqlDriver() {
0077: doubleFormat = new DecimalFormat("#.#",
0078: new DecimalFormatSymbols(Locale.US));
0079: }
0080:
0081: /**
0082: * Get the name of this driver.
0083: */
0084: public abstract String getName();
0085:
0086: public int getMajorVersion() {
0087: return -1;
0088: }
0089:
0090: public int getMinorVersion() {
0091: return -1;
0092: }
0093:
0094: public String getMinorVersionPatchLevel() {
0095: return "NOT_SET";
0096: }
0097:
0098: public String getVersion() {
0099: return "NOT_SET";
0100: }
0101:
0102: /**
0103: * Create a SqlDriver instance by name.
0104: *
0105: * @param jdbcDriver Optional JDBC driver for more accurate feature use
0106: * @throws javax.jdo.JDOFatalUserException
0107: * if name is invalid
0108: * @see #customizeForServer
0109: */
0110: public static SqlDriver createSqlDriver(String name,
0111: Driver jdbcDriver) {
0112: SqlDriver ans = null;
0113:
0114: if (name.equals("informix")) {
0115: ans = new InformixSqlDriver();
0116: } else if (name.equals("informixse")) {
0117: ans = new InformixSESqlDriver();
0118: } else if (name.equals("sybase")) {
0119: ans = new SybaseSqlDriver();
0120: } else if (name.equals("db2")) {
0121: ans = new DB2SqlDriver();
0122: } else if (name.equals("mssql")) {
0123: ans = new MsSqlDriver();
0124:
0125: } else if (name.equals("postgres")) {
0126: ans = new PostgresSqlDriver();
0127: } else if (name.equals("oracle")) {
0128: ans = new OracleSqlDriver();
0129:
0130: } else if (name.equals("hypersonic")) {
0131: ans = new HypersonicSqlDriver();
0132: } else if (name.equals("instantdb")) {
0133: ans = new InstantDbSqlDriver();
0134: } else if (name.equals("mckoi")) {
0135: ans = new MckoiSqlDriver();
0136: } else if (name.equals("sapdb")) {
0137: ans = new SapDbSqlDriver();
0138: } else if (name.equals("interbase")) {
0139: ans = new InterbaseSqlDriver();
0140: } else if (name.equals("pointbase")) {
0141: ans = new PointbaseSqlDriver();
0142: } else if (name.equals("firebird")) {
0143: ans = new FirebirdSqlDriver();
0144: } else if (name.equals("mysql")) {
0145: ans = new MySqlSqlDriver();
0146: } else if (name.equals("daffodil")) {
0147: ans = new DaffodilSqlDriver();
0148: } else if (name.equals("cache")) {
0149: ans = new CacheSqlDriver();
0150:
0151: } else {
0152: throw BindingSupportImpl.getInstance().runtime(
0153: "Unknown db: " + name);
0154: }
0155: if (jdbcDriver != null)
0156: ans.init(jdbcDriver);
0157: return ans;
0158: }
0159:
0160: /**
0161: * Try and guess an appropriate SqlDriver name from a JDBC URL. Returns
0162: * null if no match.
0163: */
0164: public static String getNameFromURL(String url) {
0165: url = url.toLowerCase();
0166: int i = url.indexOf(':');
0167: if (i < 0)
0168: return null;
0169: int j = ++i + 1;
0170: int n = url.length();
0171: for (; j < n; j++) {
0172: char c = url.charAt(j);
0173: if (c < 'a' || c > 'z')
0174: break;
0175: }
0176: String key = url.substring(i, j);
0177: String[] a = new String[] { "cache", "cache", "db2", "db2",
0178: "firebirdsql", "firebird", "hsqldb", "hypersonic",
0179: "informix", "informix", "interbase", "interbase",
0180: "microsoft", "mssql", "mysql", "mysql", "oracle",
0181: "oracle", "pointbase", "pointbase", "postgresql",
0182: "postgres", "sapdb", "sapdb", "sybase", "sybase", };
0183: for (i = 0; i < a.length; i += 2) {
0184: if (a[i].equals(key))
0185: return a[i + 1];
0186: }
0187: return null;
0188: }
0189:
0190: /**
0191: * Try and guess an appropriate SqlDriver name from a JDBC URL. Returns
0192: * null if no match.
0193: */
0194: public static String getDriverFromURL(String url) {
0195: if (url == null)
0196: return null;
0197: url = url.toLowerCase();
0198: int i = url.indexOf(':');
0199: if (i < 0)
0200: return null;
0201: int j = ++i + 1;
0202: int n = url.length();
0203: for (; j < n; j++) {
0204: char c = url.charAt(j);
0205: if (c < 'a' || c > 'z')
0206: break;
0207: }
0208: String key = url.substring(i, j);
0209: String[] a = new String[] { "cache",
0210: "com.intersys.jdbc.CacheDriver", "db2",
0211: "com.ibm.db2.jcc.DB2Driver", "firebirdsql",
0212: "org.firebirdsql.jdbc.FBDriver", "hsqldb",
0213: "org.hsqldb.jdbcDriver", "informix",
0214: "com.informix.jdbc.IfxDriver", "interbase",
0215: "interbase.interclient.Driver", "microsoft",
0216: "com.microsoft.jdbc.sqlserver.SQLServerDriver",
0217: "mysql", "com.mysql.jdbc.Driver", "oracle",
0218: "oracle.jdbc.driver.OracleDriver", "pointbase",
0219: "com.pointbase.jdbc.jdbcUniversalDriver", "postgresql",
0220: "org.postgresql.Driver", "sapdb",
0221: "com.sap.dbtech.jdbc.DriverSapDB", "sybase",
0222: "com.sybase.jdbc2.jdbc.SybDriver", };
0223: for (i = 0; i < a.length; i += 2) {
0224: if (a[i].equals(key))
0225: return a[i + 1];
0226: }
0227: return null;
0228: }
0229:
0230: /**
0231: * Load a JDBC driver class and create an instance of it. The driver
0232: * class is unregistered if possible.
0233: */
0234: public static Driver createJdbcDriver(String name, ClassLoader cl) {
0235: Class cls = null;
0236: try {
0237: cls = ClassHelper.get().classForName(name, true, cl);
0238: } catch (ClassNotFoundException e) {
0239: throw BindingSupportImpl.getInstance().invalidOperation(
0240: "JDBC Driver class '" + name
0241: + "' is not available in the classpath");
0242: }
0243: Driver driver = null;
0244: try {
0245: java.lang.Object drv = cls.newInstance();
0246: driver = (Driver) drv;
0247: } catch (Exception e) {
0248: BindingSupportImpl.getInstance().runtime(
0249: "Unable to create "
0250: + "instance of JDBC Driver class '" + name
0251: + "': " + e, e);
0252: }
0253: try {
0254: DriverManager.deregisterDriver(driver);
0255: } catch (Exception x) {
0256: // ignore
0257: }
0258: return driver;
0259: }
0260:
0261: /**
0262: * Get the default type mappings for this driver. These map JDBC type
0263: * codes from java.sql.Types to column properties.
0264: *
0265: * @see #getTypeMapping
0266: */
0267: public final JdbcTypeMapping[] getTypeMappings() {
0268: int n = JDBC_TYPES.length;
0269: JdbcTypeMapping[] a = new JdbcTypeMapping[n];
0270: String database = getName();
0271: for (int i = 0; i < n; i++) {
0272: int jdbcType = JDBC_TYPES[i];
0273: JdbcTypeMapping m = getTypeMapping(jdbcType);
0274: if (m != null) {
0275: m.setDatabase(database);
0276: m.setJdbcType(jdbcType);
0277: }
0278: a[i] = m;
0279: }
0280: return a;
0281: }
0282:
0283: /**
0284: * Get the default type mapping for the supplied JDBC type code from
0285: * java.sql.Types or null if the type is not supported. There is no
0286: * need to set the database or jdbcType on the mapping as this is done
0287: * after this call returns. Subclasses should override this and to
0288: * customize type mappings.
0289: */
0290: protected JdbcTypeMapping getTypeMapping(int jdbcType) {
0291: switch (jdbcType) {
0292: case Types.BIT:
0293: case Types.TINYINT:
0294: case Types.SMALLINT:
0295: case Types.INTEGER:
0296: case Types.BIGINT:
0297: return new JdbcTypeMapping(JdbcTypes.toString(jdbcType), 0,
0298: 0, JdbcTypeMapping.TRUE, JdbcTypeMapping.TRUE, null);
0299: case Types.FLOAT:
0300: case Types.REAL:
0301: case Types.DOUBLE:
0302: case Types.DATE:
0303: case Types.TIME:
0304: case Types.TIMESTAMP:
0305: case Types.BLOB:
0306: case Types.CLOB:
0307: case Types.LONGVARCHAR:
0308: case Types.LONGVARBINARY:
0309: return new JdbcTypeMapping(JdbcTypes.toString(jdbcType), 0,
0310: 0, JdbcTypeMapping.TRUE, JdbcTypeMapping.FALSE,
0311: null);
0312: case Types.DECIMAL:
0313:
0314: case Types.NUMERIC:
0315: return new JdbcTypeMapping(JdbcTypes.toString(jdbcType),
0316: 20, 10, JdbcTypeMapping.TRUE, JdbcTypeMapping.TRUE,
0317: null);
0318: case Types.CHAR:
0319: case Types.VARCHAR:
0320: return new JdbcTypeMapping(JdbcTypes.toString(jdbcType),
0321: 255, 0, JdbcTypeMapping.TRUE, JdbcTypeMapping.TRUE,
0322: null);
0323: case Types.BINARY:
0324: case Types.VARBINARY:
0325: return new JdbcTypeMapping(JdbcTypes.toString(jdbcType),
0326: 255, 0, JdbcTypeMapping.TRUE, JdbcTypeMapping.TRUE,
0327: bytesConverterFactory);
0328: }
0329: throw BindingSupportImpl.getInstance().internal(
0330: "Invalid type in getTypeMapping: " + jdbcType);
0331: }
0332:
0333: /**
0334: * Get the default field mappings for this driver. These map java classes
0335: * to column properties. Subclasses should override this, call super() and
0336: * replace mappings as needed.
0337: */
0338: public HashMap getJavaTypeMappings() {
0339: HashMap ans = new HashMap(61);
0340:
0341: add(ans,
0342: new JdbcJavaTypeMapping(Boolean.TYPE, Types.BIT, false));
0343: add(ans,
0344: new JdbcJavaTypeMapping(Boolean.class, Types.BIT, true));
0345: add(ans, new JdbcJavaTypeMapping(Byte.TYPE, Types.TINYINT,
0346: false));
0347: add(ans, new JdbcJavaTypeMapping(Byte.class, Types.TINYINT,
0348: true));
0349: add(ans, new JdbcJavaTypeMapping(Short.TYPE, Types.SMALLINT,
0350: false));
0351: add(ans, new JdbcJavaTypeMapping(Short.class, Types.SMALLINT,
0352: true));
0353: add(ans, new JdbcJavaTypeMapping(Integer.TYPE, Types.INTEGER,
0354: false));
0355: add(ans, new JdbcJavaTypeMapping(Integer.class, Types.INTEGER,
0356: true));
0357: add(ans, new JdbcJavaTypeMapping(Character.TYPE, Types.CHAR,
0358: false));
0359: add(ans, new JdbcJavaTypeMapping(Character.class, Types.CHAR,
0360: true));
0361: add(ans,
0362: new JdbcJavaTypeMapping(Long.TYPE, Types.BIGINT, false));
0363: add(ans,
0364: new JdbcJavaTypeMapping(Long.class, Types.BIGINT, true));
0365: add(ans, new JdbcJavaTypeMapping(Float.TYPE, Types.REAL, false,
0366: false));
0367: add(ans, new JdbcJavaTypeMapping(Float.class, Types.REAL, true,
0368: false));
0369: add(ans, new JdbcJavaTypeMapping(Double.TYPE, Types.DOUBLE,
0370: false, false));
0371: add(ans, new JdbcJavaTypeMapping(Double.class, Types.DOUBLE,
0372: true, false));
0373:
0374: add(ans, new JdbcJavaTypeMapping(String.class, Types.VARCHAR,
0375: true));
0376: add(ans, new JdbcJavaTypeMapping(Date.class, Types.TIMESTAMP,
0377: true, false));
0378: add(ans, new JdbcJavaTypeMapping(BigDecimal.class,
0379: Types.NUMERIC, true));
0380:
0381: add(ans, new JdbcJavaTypeMapping(BigInteger.class,
0382: Types.NUMERIC, 20, 0, JdbcJavaTypeMapping.TRUE, null));
0383:
0384: JdbcConverterFactory f = new LocaleConverter.Factory();
0385: add(ans, new JdbcJavaTypeMapping(Locale.class, Types.CHAR, 6,
0386: 0, JdbcJavaTypeMapping.TRUE, f));
0387:
0388: f = new CharConverter.Factory();
0389: add(ans, new JdbcJavaTypeMapping(Character.class, Types.CHAR,
0390: 1, 0, JdbcJavaTypeMapping.TRUE, f));
0391: add(ans, new JdbcJavaTypeMapping(Character.TYPE, Types.CHAR, 1,
0392: 0, JdbcJavaTypeMapping.FALSE, f));
0393:
0394: // primitive array[] mappings
0395: f = new ByteArrayConverter.Factory();
0396: add(ans, new JdbcJavaTypeMapping(byte[].class, Types.BLOB, f));
0397: f = new ShortArrayConverter.Factory();
0398: add(ans, new JdbcJavaTypeMapping(short[].class, Types.BLOB, f));
0399: f = new IntArrayConverter.Factory();
0400: add(ans, new JdbcJavaTypeMapping(int[].class, Types.BLOB, f));
0401: f = new LongArrayConverter.Factory();
0402: add(ans, new JdbcJavaTypeMapping(long[].class, Types.BLOB, f));
0403: f = new BooleanArrayConverter.Factory();
0404: add(ans,
0405: new JdbcJavaTypeMapping(boolean[].class, Types.BLOB, f));
0406: f = new CharArrayConverter.Factory();
0407: add(ans, new JdbcJavaTypeMapping(char[].class, Types.BLOB, f));
0408: f = new FloatArrayConverter.Factory();
0409: add(ans, new JdbcJavaTypeMapping(float[].class, Types.BLOB, f));
0410: f = new DoubleArrayConverter.Factory();
0411: add(ans, new JdbcJavaTypeMapping(double[].class, Types.BLOB, f));
0412:
0413: // extended types
0414: add(ans, new JdbcJavaTypeMapping(File.class, Types.VARCHAR, -1,
0415: 0, JdbcJavaTypeMapping.NOT_SET,
0416: new FileConverter.Factory(), false));
0417: add(ans, new JdbcJavaTypeMapping(URL.class, Types.VARCHAR, -1,
0418: 0, JdbcJavaTypeMapping.NOT_SET,
0419: new URLConverter.Factory(), false));
0420: add(ans, new JdbcJavaTypeMapping(Timestamp.class,
0421: Types.TIMESTAMP, new TimestampConverter.Factory(),
0422: false));
0423:
0424: return ans;
0425: }
0426:
0427: /**
0428: * Add m to ans using its javaType as the key.
0429: */
0430: protected void add(HashMap ans, JdbcJavaTypeMapping m) {
0431: m.setDatabase(getName());
0432: ans.put(m.getJavaType(), m);
0433: }
0434:
0435: /**
0436: * Perform any driver specific customization. This can be used to control
0437: * functionality depending on the version of JDBC driver in use etc.
0438: */
0439: protected void init(Driver jdbcDriver) {
0440: }
0441:
0442: /**
0443: * Does this store do anything in {@link #customizeForServer(java.sql.Connection)}.
0444: * This avoids allocating a connection if it is not required which sorts
0445: * out a problem we have with Torpedo.
0446: */
0447: public boolean isCustomizeForServerRequired() {
0448: return false;
0449: }
0450:
0451: /**
0452: * Perform any specific configuration appropriate for the database server
0453: * in use. If any SQL is done on con call con.commit() before returning.
0454: */
0455: public void customizeForServer(Connection con) throws SQLException {
0456: }
0457:
0458: /**
0459: * Create a default name generator instance for JdbcStore's using this
0460: * driver.
0461: */
0462: public JdbcNameGenerator createJdbcNameGenerator() {
0463: return createDefaultJdbcNameGenerator();
0464: }
0465:
0466: protected DefaultJdbcNameGenerator createDefaultJdbcNameGenerator() {
0467: DefaultJdbcNameGenerator ans = new DefaultJdbcNameGenerator();
0468: ans.setDatabaseType(getName());
0469: return ans;
0470: }
0471:
0472: /**
0473: * Some drivers require a call to clear the batches.
0474: *
0475: * @return
0476: */
0477: public boolean isClearBatchRequired() {
0478: return false;
0479: }
0480:
0481: /**
0482: * Does the JDBC driver support statement batching for inserts?
0483: */
0484: public boolean isInsertBatchingSupported() {
0485: return false;
0486: }
0487:
0488: /**
0489: * Does the JDBC driver support statement batching for updates?
0490: */
0491: public boolean isUpdateBatchingSupported() {
0492: return false;
0493: }
0494:
0495: /**
0496: * Can batching be used if the statement contains a column with the
0497: * given JDBC type?
0498: */
0499: public boolean isBatchingSupportedForJdbcType(int jdbcType) {
0500: return true;
0501: }
0502:
0503: /**
0504: * Does the JDBC driver support scrollable result sets?
0505: */
0506: public boolean isScrollableResultSetSupported() {
0507: return false;
0508: }
0509:
0510: /**
0511: * Does the JDBC driver support Statement.setFetchSize()?
0512: */
0513: public boolean isFetchSizeSupported() {
0514: return true;
0515: }
0516:
0517: /**
0518: * Should PreparedStatement batching be used for this database and
0519: * JDBC driver?
0520: */
0521: public boolean isPreparedStatementPoolingOK() {
0522: return true;
0523: }
0524:
0525: /**
0526: * How many PreparedStatement's should the pool be limited to by default
0527: * (0 for unlimited) ?
0528: */
0529: public int getDefaultPsCacheMax() {
0530: return 0;
0531: }
0532:
0533: /**
0534: * Does this driver use the ANSI join syntax (i.e. the join clauses appear
0535: * in the from list e.g. postgres)?
0536: */
0537: public boolean isAnsiJoinSyntax() {
0538: return false;
0539: }
0540:
0541: /**
0542: * May the ON clauses for joins in a subquery reference columns from the
0543: * enclosing query? DB2 does not allow this.
0544: */
0545: public boolean isSubQueryJoinMayUseOuterQueryCols() {
0546: return true;
0547: }
0548:
0549: /**
0550: * Is null a valid value for a column with a foreign key constraint?
0551: */
0552: public boolean isNullForeignKeyOk() {
0553: return false;
0554: }
0555:
0556: /**
0557: * Must columns used in an order by statement appear in the select list?
0558: */
0559: public boolean isPutOrderColsInSelect() {
0560: return false;
0561: }
0562:
0563: /**
0564: * Should indexes be used for columns in the order by list that are
0565: * also in the select list? This is used for databases that will not
0566: * order by a column that is duplicated in the select list (e.g. Oracle).
0567: */
0568: public boolean isUseIndexesForOrderCols() {
0569: return false;
0570: }
0571:
0572: public String getAliasPrepend() {
0573: return " ";
0574: }
0575:
0576: /**
0577: * Does the LIKE operator only support literal string and column
0578: * arguments (e.g. Informix)?
0579: */
0580: public boolean isLikeStupid() {
0581: return false;
0582: }
0583:
0584: /**
0585: * What is the maximum number of parameters allowed for the IN (?, .. ?)
0586: * operator?
0587: */
0588: public int getMaxInOperands() {
0589: return 10;
0590: }
0591:
0592: /**
0593: * Is it ok to convert simple 'exists (select ...)' clauses under an
0594: * 'or' into outer joins?
0595: */
0596: public boolean isOptimizeExistsUnderOrToOuterJoin() {
0597: return true;
0598: }
0599:
0600: /**
0601: * Must 'exists (select ...)' clauses be converted into a join and
0602: * distinct be added to the select (e.g. MySQL) ?
0603: */
0604: public boolean isConvertExistsToDistinctJoin() {
0605: return false;
0606: }
0607:
0608: public boolean isConvertExistsToJoins(int type) {
0609: switch (type) {
0610: case SqlExp.NO:
0611: return false;
0612: case SqlExp.YES:
0613: return true;
0614: case SqlExp.YES_DISTINCT:
0615: case SqlExp.YES_DISTINCT_NOT:
0616: return false;
0617: default:
0618: throw BindingSupportImpl.getInstance().internal(
0619: "Unknown type '" + type + "'");
0620: }
0621: }
0622:
0623: /**
0624: * Must some expressions (+, -, string concat) be wrapped in brackets?
0625: */
0626: public boolean isExtraParens() {
0627: return false;
0628: }
0629:
0630: /**
0631: * Can the tx isolation level be set on this database?
0632: */
0633: public boolean isSetTransactionIsolationLevelSupported() {
0634: return true;
0635: }
0636:
0637: /**
0638: * Does this database support autoincrement or serial columns?
0639: */
0640: public boolean isAutoIncSupported() {
0641: return false;
0642: }
0643:
0644: /**
0645: * Does this database support comments embedded in SQL?
0646: */
0647: public boolean isCommentSupported() {
0648: return true;
0649: }
0650:
0651: /**
0652: * Generate SQL to create the database schema for the supplied tables.
0653: * If con is not null then it must have autoCommit true.
0654: */
0655: public void generateDDL(ArrayList tables, Connection con,
0656: PrintWriter out, boolean comments) {
0657: StatementWithLastSQL stat = null;
0658: try {
0659: if (con != null) {
0660: stat = new StatementWithLastSQL(con.createStatement());
0661: }
0662: // generate the 'create table' statements
0663: int n = tables.size();
0664: for (int i = 0; i < n; i++) {
0665: JdbcTable t = (JdbcTable) tables.get(i);
0666: generateCreateTable(t, stat, out, comments);
0667: }
0668: // generate the 'create index' statements
0669: for (int i = 0; i < n; i++) {
0670: JdbcTable t = (JdbcTable) tables.get(i);
0671: generateCreateIndexes(t, stat, out, comments);
0672: }
0673: // generate the 'add constraint' statements
0674: for (int i = 0; i < n; i++) {
0675: JdbcTable t = (JdbcTable) tables.get(i);
0676: generateConstraints(t, stat, out, comments);
0677: }
0678: } catch (SQLException x) {
0679: String msg;
0680: if (stat == null) {
0681: msg = x.toString();
0682: } else {
0683: msg = x + "\nMost recent SQL:\n" + stat.getLastSQL();
0684: }
0685: throw mapException(x, msg, false);
0686: } finally {
0687: if (stat != null) {
0688: try {
0689: stat.close();
0690: } catch (SQLException e) {
0691: // ignore
0692: }
0693: }
0694: }
0695: }
0696:
0697: /**
0698: * Generate a 'create table' statement for t.
0699: */
0700: public void generateCreateTable(JdbcTable t, Statement stat,
0701: PrintWriter out, boolean comments) throws SQLException {
0702: CharBuf s = new CharBuf();
0703: if (comments && isCommentSupported() && t.comment != null) {
0704: s.append(comment(t.comment));
0705: s.append('\n');
0706: }
0707: s.append("CREATE TABLE ");
0708: s.append(t.name);
0709: s.append(" (\n");
0710: JdbcColumn[] cols = t.getColsForCreateTable();
0711: int nc = cols.length;
0712: boolean first = true;
0713: for (int i = 0; i < nc; i++) {
0714: if (first) {
0715: first = false;
0716: } else {
0717: s.append("\n");
0718: }
0719: s.append(" ");
0720: appendCreateColumn(t, cols[i], s, comments);
0721: }
0722: s.append("\n ");
0723: appendPrimaryKeyConstraint(t, s);
0724: appendIndexesInCreateTable(t, s);
0725: s.append("\n)");
0726: appendTableType(t, s);
0727: String sql = s.toString();
0728: if (out != null) {
0729: print(out, sql);
0730: }
0731: if (stat != null) {
0732: stat.execute(sql);
0733: }
0734: }
0735:
0736: /**
0737: * Format a comment.
0738: */
0739: public String comment(String msg) {
0740: return "-- " + msg;
0741: }
0742:
0743: /**
0744: * Hook for drivers that have to append a table type to the create table
0745: * statement (e.g. MySQL).
0746: */
0747: protected void appendTableType(JdbcTable t, CharBuf s) {
0748: }
0749:
0750: /**
0751: * Hook for drivers that must create indexes in the create table
0752: * statement (e.g. MySQL).
0753: */
0754: protected void appendIndexesInCreateTable(JdbcTable t, CharBuf s) {
0755: }
0756:
0757: /**
0758: * Write an SQL statement to a script with appropriate separator.
0759: */
0760: protected void print(PrintWriter out, String sql) {
0761: out.print(sql);
0762: out.println(";");
0763: out.println();
0764: }
0765:
0766: /**
0767: * Append the part of a create table statement for a column.
0768: */
0769: protected void appendCreateColumn(JdbcTable t, JdbcColumn c,
0770: CharBuf s, boolean comments) {
0771: int si = s.size();
0772: s.append(c.name);
0773: s.append(' ');
0774: appendColumnType(c, s);
0775: if (c.autoinc)
0776: appendCreateColumnAutoInc(t, c, s);
0777: appendCreateColumnNulls(t, c, s);
0778: s.append(',');
0779: if (comments && isCommentSupported() && c.comment != null) {
0780: s.append(' ');
0781: si += COMMENT_COL;
0782: for (; s.size() < si; s.append(' '))
0783: ;
0784: s.append(comment(c.comment));
0785: }
0786: }
0787:
0788: /**
0789: * Append the column type part of a create table statement for a column.
0790: */
0791: protected void appendColumnType(JdbcColumn c, CharBuf s) {
0792: appendColumnType(c, s, useZeroScale(c));
0793: }
0794:
0795: protected boolean useZeroScale(JdbcColumn c) {
0796: return false;
0797: }
0798:
0799: /**
0800: * Append the column type part of a create table statement for a column.
0801: */
0802: protected void appendColumnType(JdbcColumn c, CharBuf s,
0803: boolean useZeroScale) {
0804: if (c.sqlType == null) {
0805: throw BindingSupportImpl.getInstance().internal(
0806: "sqlType is null: " + c);
0807: }
0808: s.append(c.sqlType);
0809: if (c.length != 0 || c.scale != 0) {
0810: s.append('(');
0811: s.append(c.length);
0812: if (c.scale != 0 || useZeroScale) {
0813: s.append(',');
0814: s.append(c.scale);
0815: }
0816: s.append(')');
0817: }
0818: }
0819:
0820: /**
0821: * Get the database specific name for the jdbcType.
0822: *
0823: * @see Types
0824: */
0825: protected String getTypeName(int jdbcType) {
0826: return JdbcTypes.toString(jdbcType);
0827: }
0828:
0829: /**
0830: * Append the allow nulls part of the definition for a column in a
0831: * create table statement.
0832: */
0833: protected void appendCreateColumnNulls(JdbcTable t, JdbcColumn c,
0834: CharBuf s) {
0835: if (!c.nulls)
0836: s.append(" NOT NULL");
0837: }
0838:
0839: /**
0840: * Append the column auto increment part of a create table statement for a
0841: * column.
0842: */
0843: protected void appendCreateColumnAutoInc(JdbcTable t, JdbcColumn c,
0844: CharBuf s) {
0845: }
0846:
0847: /**
0848: * Add the primary key constraint part of a create table statement to s.
0849: */
0850: protected void appendPrimaryKeyConstraint(JdbcTable t, CharBuf s) {
0851: s.append("PRIMARY KEY (");
0852: appendColumnNameList(t.pk, s);
0853: s.append(") CONSTRAINT ");
0854: s.append(t.pkConstraintName);
0855: }
0856:
0857: /**
0858: * Append a comma separated list of column names to s.
0859: */
0860: protected void appendColumnNameList(JdbcColumn[] cols, CharBuf s) {
0861: int colslen = cols.length;
0862: for (int i = 0; i < colslen; i++) {
0863: if (i > 0)
0864: s.append(", ");
0865: s.append(cols[i].name);
0866: }
0867: }
0868:
0869: /**
0870: * Generate the 'create index' statements for t.
0871: */
0872: public void generateCreateIndexes(JdbcTable t, Statement stat,
0873: PrintWriter out, boolean comments) throws SQLException {
0874: JdbcIndex[] a = t.indexes;
0875: if (a == null)
0876: return;
0877: CharBuf s = new CharBuf();
0878: for (int i = 0; i < a.length; i++) {
0879: JdbcIndex idx = a[i];
0880: s.clear();
0881: appendCreateIndex(s, t, idx, comments);
0882: if (s.size() > 0) {
0883: String sql = s.toString();
0884: if (out != null)
0885: print(out, sql);
0886: if (stat != null)
0887: stat.execute(sql);
0888: }
0889: }
0890: }
0891:
0892: /**
0893: * Generate a 'create index' statement for idx.
0894: */
0895: protected void appendCreateIndex(CharBuf s, JdbcTable t,
0896: JdbcIndex idx, boolean comments) {
0897: if (comments && isCommentSupported() && idx.comment != null) {
0898: s.append(comment(idx.comment));
0899: s.append('\n');
0900: }
0901: s.append("CREATE ");
0902: if (idx.unique)
0903: s.append("UNIQUE ");
0904: //if (idx.clustered) s.append("clustered ");
0905: s.append("INDEX ");
0906: s.append(idx.name);
0907: s.append(" ON ");
0908: s.append(t.name);
0909: s.append('(');
0910: s.append(idx.cols[0].name);
0911: int n = idx.cols.length;
0912: for (int i = 1; i < n; i++) {
0913: s.append(',');
0914: s.append(' ');
0915: s.append(idx.cols[i].name);
0916: }
0917: s.append(')');
0918: }
0919:
0920: /**
0921: * Generate the 'add constraint' statements for t.
0922: */
0923: public void generateConstraints(JdbcTable t, Statement stat,
0924: PrintWriter out, boolean comments) throws SQLException {
0925: JdbcConstraint[] cons = t.constraints;
0926: if (cons == null)
0927: return;
0928: CharBuf s = new CharBuf();
0929: for (int i = 0; i < cons.length; i++) {
0930: JdbcConstraint c = cons[i];
0931: s.clear();
0932: appendRefConstraint(s, c);
0933: String sql = s.toString();
0934: if (!sql.equals("")) {
0935: if (out != null)
0936: print(out, sql);
0937: if (stat != null)
0938: stat.execute(sql);
0939: }
0940: }
0941: }
0942:
0943: /**
0944: * Append an 'add constraint' statement for c.
0945: */
0946: protected void appendRefConstraint(CharBuf s, JdbcConstraint c) {
0947: s.append("ALTER TABLE ");
0948: s.append(c.src.name);
0949: s.append(" ADD CONSTRAINT (FOREIGN KEY (");
0950: appendColumnNameList(c.srcCols, s);
0951: s.append(") REFERENCES ");
0952: s.append(c.dest.name);
0953: s.append('(');
0954: appendColumnNameList(c.dest.pk, s);
0955: s.append(") CONSTRAINT ");
0956: s.append(c.name);
0957: s.append(')');
0958: }
0959:
0960: /**
0961: * Get the names of all tables in the database con is connected to.
0962: */
0963: public ArrayList getTableNames(Connection con) throws SQLException {
0964: ResultSet rs = null;
0965: try {
0966: rs = con.getMetaData().getTables(null, getSchema(con),
0967: null, null);
0968: ArrayList a = new ArrayList();
0969: for (; rs.next();) {
0970: if (rs.getString(4).trim().equals("TABLE")) {
0971: a.add(rs.getString(3).trim());
0972: }
0973: }
0974: return a;
0975: } finally {
0976: if (rs != null) {
0977: try {
0978: rs.close();
0979: } catch (SQLException x) {
0980: // ignore
0981: }
0982: }
0983: }
0984: }
0985:
0986: // /**
0987: // * Get the names of all tables in the database con is connected to.
0988: // */
0989: // public ArrayList getTableNames(Connection con) throws SQLException {
0990: // ResultSet rs = null;
0991: // try {
0992: //// getSchema(con);
0993: //
0994: //
0995: // rs = con.getMetaData().getTables(null, getSchema(con), null, null);
0996: // ArrayList a = new ArrayList();
0997: //
0998: // String name = null;
0999: // String type = null;
1000: // String schema = null;
1001: // for (; rs.next();) {
1002: // type = rs.getString(4).trim();
1003: // name = rs.getString(3).trim();
1004: // schema = rs.getString(2).trim();
1005: // if (name.lastIndexOf('/') == -1 && name.lastIndexOf('$') == -1){
1006: //
1007: // System.out.println("SELECT * FROM "+
1008: // (schema == null ? "": schema+".")+
1009: // name +"; --"+ type);
1010: // }
1011: // if (type.equals("TABLE")) {
1012: // a.add(name);
1013: // }
1014: // }
1015: // return a;
1016: // } finally {
1017: // if (rs != null) {
1018: // try {
1019: // rs.close();
1020: // } catch (SQLException x) {
1021: // // ignore
1022: // }
1023: // }
1024: // }
1025: // }
1026:
1027: /**
1028: * Drop the table and all its constraints etc. This must remove
1029: * constraints to this table from other tables so it can be dropped.
1030: */
1031: public void dropTable(Connection con, String table)
1032: throws SQLException {
1033: StatementWithLastSQL stat = null;
1034: try {
1035: stat = new StatementWithLastSQL(con.createStatement());
1036: dropTable(con, table, stat);
1037: } catch (SQLException x) {
1038: String msg;
1039: if (stat == null) {
1040: msg = x.toString();
1041: } else {
1042: msg = x + "\nMost recent SQL:\n" + stat.getLastSQL();
1043: }
1044: throw mapException(x, msg, false);
1045: } finally {
1046: if (stat != null) {
1047: try {
1048: stat.close();
1049: } catch (SQLException e) {
1050: // ignore
1051: }
1052: }
1053: }
1054: }
1055:
1056: /**
1057: * Drop the table and all its constraints etc. This must remove
1058: * constraints to this table from other tables so it can be dropped.
1059: */
1060: protected void dropTable(Connection con, String table,
1061: Statement stat) throws SQLException {
1062: stat.execute("DROP TABLE " + table);
1063: }
1064:
1065: /**
1066: * Append a replacable parameter part of a where clause for the column.
1067: * This gives the driver a chance to embed type conversions and so on
1068: * for types not handled well by the JDBC driver (e.g. BigDecimals and
1069: * the postgres JDBC driver).
1070: */
1071: public void appendWhereParam(CharBuf s, JdbcColumn c) {
1072: s.append("?");
1073: }
1074:
1075: /**
1076: * Get the string form of a binary operator.
1077: *
1078: * @see BinaryOpExp
1079: */
1080: public String getSqlBinaryOp(int op) {
1081: switch (op) {
1082: case BinaryOpExp.EQUAL:
1083: return "=";
1084: case BinaryOpExp.NOT_EQUAL:
1085: return "<>";
1086: case BinaryOpExp.GT:
1087: return ">";
1088: case BinaryOpExp.LT:
1089: return "<";
1090: case BinaryOpExp.GE:
1091: return ">=";
1092: case BinaryOpExp.LE:
1093: return "<=";
1094: case BinaryOpExp.LIKE:
1095: return "LIKE";
1096: case BinaryOpExp.CONCAT:
1097: return "||";
1098: case BinaryOpExp.PLUS:
1099: return "+";
1100: case BinaryOpExp.MINUS:
1101: return "-";
1102: case BinaryOpExp.TIMES:
1103: return "*";
1104: case BinaryOpExp.DIVIDE:
1105: return "/";
1106: }
1107: throw BindingSupportImpl.getInstance().internal(
1108: "Unknown op: " + op);
1109: }
1110:
1111: /**
1112: * Append the value of the literal to s.
1113: *
1114: * @see LiteralExp
1115: */
1116: public void appendSqlLiteral(int type, String value, CharBuf s) {
1117: if (type == LiteralExp.TYPE_STRING) {
1118: s.append('\'');
1119: int len = value.length();
1120: for (int i = 0; i < len; i++) {
1121: char c = value.charAt(i);
1122: if (c == '\'')
1123: s.append('\'');
1124: s.append(c);
1125: }
1126: s.append('\'');
1127: } else {
1128: s.append(value);
1129: }
1130: }
1131:
1132: /**
1133: * Append the name of the column to s. If col is null then append '*'.
1134: *
1135: * @see ColumnExp
1136: */
1137: public void appendSqlColumn(JdbcColumn col, String alias, CharBuf s) {
1138: if (alias != null) {
1139: s.append(alias);
1140: s.append('.');
1141: }
1142: if (col == null) {
1143: s.append('*');
1144: } else {
1145: s.append(col.name);
1146: }
1147: }
1148:
1149: /**
1150: * Append the from list entry for a table.
1151: */
1152: public void appendSqlFrom(JdbcTable table, String alias, CharBuf s) {
1153: s.append(table.name);
1154: if (alias != null) {
1155: s.append(' ');
1156: s.append(alias);
1157: }
1158: }
1159:
1160: /**
1161: * Append the from list entry for a table that is the right hand table
1162: * in a join i.e. it is being joined to.
1163: *
1164: * @param exp This is the expression that joins the tables
1165: * @param outer If true then this is an outer join
1166: */
1167: public void appendSqlFromJoin(JdbcTable table, String alias,
1168: SqlExp exp, boolean outer, CharBuf s) {
1169: s.append(',');
1170: s.append(' ');
1171: if (outer)
1172: s.append("OUTER ");
1173: s.append(table.name);
1174: if (alias != null) {
1175: s.append(' ');
1176: s.append(alias);
1177: }
1178: }
1179:
1180: /**
1181: * Append a join expression.
1182: */
1183: public void appendSqlJoin(String leftAlias, JdbcColumn left,
1184: String rightAlias, JdbcColumn right, boolean outer,
1185: CharBuf s) {
1186: s.append(leftAlias);
1187: s.append('.');
1188: s.append(left.name);
1189: s.append(' ');
1190: s.append('=');
1191: s.append(' ');
1192: s.append(rightAlias);
1193: s.append('.');
1194: s.append(right.name);
1195: }
1196:
1197: /**
1198: * Get a String for a replacable parameter. This gives the driver a
1199: * chance to embed type conversions and so on for types not handled well
1200: * by the JDBC driver (e.g. BigDecimals and the postgres JDBC driver).
1201: * <p/>
1202: * If you override this method then you must also override {@link #getSqlParamStringChars(int)}.
1203: */
1204: public String getSqlParamString(int jdbcType) {
1205: return "?";
1206: }
1207:
1208: /**
1209: * Return a shared char[] of the sqlParamString. This array may not be modified.
1210: * It is used as a stamp when creating a in list.
1211: *
1212: * @param jdbcType
1213: * @return
1214: */
1215: public char[] getSqlParamStringChars(int jdbcType) {
1216: return DEFAULT_PARAM_CHARS;
1217: }
1218:
1219: /**
1220: * Get the name of a function that accepts one argument.
1221: *
1222: * @see UnaryFunctionExp
1223: */
1224: public String getSqlUnaryFunctionName(int func) {
1225: switch (func) {
1226: case UnaryFunctionExp.FUNC_TO_LOWER_CASE:
1227: return "lower";
1228: }
1229: throw BindingSupportImpl.getInstance().internal(
1230: "Unknown func: " + func);
1231: }
1232:
1233: /**
1234: * Get default SQL to test a connection or null if none available. This
1235: * must be a query that returns at least one row.
1236: */
1237: public String getConnectionValidateSQL() {
1238: return null;
1239: }
1240:
1241: /**
1242: * Get default SQL used to init a connection or null if none required.
1243: */
1244: public String getConnectionInitSQL() {
1245: return null;
1246: }
1247:
1248: /**
1249: * Get con ready for a getQueryPlan call. Example: On Sybase this will
1250: * do a 'set showplan 1' and 'set noexec 1'. Also make whatever changes
1251: * are necessary to sql to prepare it for a getQueryPlan call. Example:
1252: * On Oracle this will prepend 'explain '. The cleanupForGetQueryPlan
1253: * method must be called in a finally block if this method is called.
1254: *
1255: * @see #cleanupForGetQueryPlan
1256: * @see #getQueryPlan
1257: */
1258: public String prepareForGetQueryPlan(Connection con, String sql) {
1259: return sql;
1260: }
1261:
1262: /**
1263: * Get the query plan for ps and cleanup anything done in
1264: * prepareForGetQueryPlan. Return null if this is not supported.
1265: *
1266: * @see #prepareForGetQueryPlan
1267: * @see #cleanupForGetQueryPlan
1268: */
1269: public String getQueryPlan(Connection con, PreparedStatement ps) {
1270: return null;
1271: }
1272:
1273: /**
1274: * Cleanup anything done in prepareForGetQueryPlan. Example: On Sybase this
1275: * will do a 'set showplan 0' and 'set noexec 0'.
1276: *
1277: * @see #prepareForGetQueryPlan
1278: * @see #getQueryPlan
1279: */
1280: public void cleanupForGetQueryPlan(Connection con) {
1281: }
1282:
1283: /**
1284: * Get extra SQL to be appended to the insert statement for retrieving
1285: * the value of an autoinc column after insert. Return null if none
1286: * is required or a separate query is run.
1287: *
1288: * @see #getAutoIncColumnValue(JdbcTable, Connection, Statement)
1289: */
1290: public String getAutoIncPostInsertSQLSuffix(JdbcTable classTable) {
1291: return null;
1292: }
1293:
1294: /**
1295: * Retrieve the value of the autoinc or serial column for a row just
1296: * inserted using stat on con.
1297: *
1298: * @see #getAutoIncPostInsertSQLSuffix(JdbcTable)
1299: */
1300: public Object getAutoIncColumnValue(JdbcTable classTable,
1301: Connection con, Statement stat) throws SQLException {
1302: throw BindingSupportImpl.getInstance().internal(
1303: "autoincrement or identity columns "
1304: + "not supported for '" + getName()
1305: + "' table '" + classTable.name + "'");
1306: }
1307:
1308: /**
1309: * Enable or disable identity insert for the given table if this is
1310: * required to insert a value into an identity column.
1311: */
1312: public void enableIdentityInsert(Connection con, String table,
1313: boolean on) throws SQLException {
1314: }
1315:
1316: /**
1317: * Get whatever needs to be appended to a SELECT statement to lock the
1318: * rows if this makes sense for the database. This must have a leading
1319: * space if not empty.
1320: */
1321: public char[] getSelectForUpdate() {
1322: return FOR_UPDATE;
1323: }
1324:
1325: /**
1326: * Does 'SELECT FOR UPDATE' require the main table of the query to be
1327: * appended (ala postgres)?
1328: */
1329: public boolean isSelectForUpdateAppendTable() {
1330: return false;
1331: }
1332:
1333: /**
1334: * Can 'SELECT FOR UPDATE' be used with a DISTINCT?
1335: */
1336: public boolean isSelectForUpdateWithDistinctOk() {
1337: return true;
1338: }
1339:
1340: public boolean isSelectForUpdateWithAggregateOk() {
1341: return false;
1342: }
1343:
1344: /*########################################*/
1345: /*# These are the schema migration stuff #*/
1346: /*########################################*/
1347:
1348: /**
1349: * Append a column that needs to be added.
1350: */
1351: protected void appendAddNewColumn(JdbcTable t, JdbcColumn c,
1352: CharBuf s, boolean comments) {
1353: if (comments && isCommentSupported() && c.comment != null) {
1354: s.append(comment("add column for field " + c.comment));
1355: }
1356:
1357: s.append("\n");
1358: s.append("ALTER TABLE ");
1359: s.append(t.name);
1360: s.append(" ADD ");
1361: s.append(c.name);
1362: s.append(' ');
1363: appendColumnType(c, s);
1364: if (c.autoinc) {
1365: appendCreateColumnAutoInc(t, c, s);
1366: }
1367: if (c.nulls) {
1368: appendCreateColumnNulls(t, c, s);
1369: s.append(";\n");
1370: } else {
1371: s.append(";\n");
1372: s.append("UPDATE ");
1373: s.append(t.name);
1374: s.append(" SET ");
1375: s.append(c.name);
1376: s.append(" = ");
1377: s.append(getDefaultForType(c));
1378: s.append(";\n");
1379:
1380: s.append("ALTER TABLE ");
1381: s.append(t.name);
1382: s.append(" MODIFY ");
1383: s.append(c.name);
1384: s.append(' ');
1385: appendColumnType(c, s);
1386: if (c.autoinc) {
1387: appendCreateColumnAutoInc(t, c, s);
1388: }
1389: appendCreateColumnNulls(t, c, s);
1390: s.append(";\n");
1391: }
1392: }
1393:
1394: /**
1395: * Append a column that needs to be added.
1396: */
1397: protected void appendModifyColumn(TableDiff tableDiff,
1398: ColumnDiff diff, CharBuf s, boolean comments) {
1399: JdbcTable t = tableDiff.getOurTable();
1400: JdbcColumn c = diff.getOurCol();
1401: if (comments && isCommentSupported() && c.comment != null) {
1402: s.append(comment("modify column for field " + c.comment));
1403: }
1404: if (comments && isCommentSupported() && c.comment == null) {
1405: s.append(comment("modify column " + c.name));
1406: }
1407: s.append("\n");
1408: s.append("ALTER TABLE ");
1409: s.append(t.name);
1410: s.append(" MODIFY ");
1411: s.append(c.name);
1412: s.append(' ');
1413: appendColumnType(c, s);
1414: if (c.autoinc) {
1415: appendCreateColumnAutoInc(t, c, s);
1416: }
1417: appendCreateColumnNulls(t, c, s);
1418: }
1419:
1420: /**
1421: * Append a column that needs to be added.
1422: */
1423: protected void appendDropColumn(TableDiff tableDiff, JdbcColumn c,
1424: CharBuf s, boolean comments) {
1425: if (comments && isCommentSupported()) {
1426: s.append(comment("dropping unknown column " + c.name));
1427: }
1428:
1429: s.append("\n");
1430: s.append("ALTER TABLE ");
1431: s.append(tableDiff.getOurTable().name);
1432: s.append(" DROP COLUMN ");
1433: s.append(c.name);
1434:
1435: }
1436:
1437: /**
1438: * Append an 'drop constraint' statement for c.
1439: */
1440: protected void appendRefDropConstraint(CharBuf s, JdbcConstraint c,
1441: boolean comments) {
1442: // if (comments && isCommentSupported()) {
1443: // s.append(comment("dropping unknown constraint " + c.name));
1444: // s.append('\n');
1445: // }
1446: s.append("ALTER TABLE ");
1447: s.append(c.src.name);
1448: s.append(" DROP CONSTRAINT ");
1449: s.append(c.name);
1450: }
1451:
1452: /**
1453: * Generate a 'drop index' statement for idx.
1454: */
1455: protected void appendDropIndex(CharBuf s, JdbcTable t,
1456: JdbcIndex idx, boolean comments) {
1457: // if (comments && isCommentSupported()) {
1458: // s.append(comment("dropping unknown index "+ idx.name));
1459: // s.append('\n');
1460: // }
1461: s.append("DROP INDEX ");
1462: s.append(idx.name);
1463: }
1464:
1465: /**
1466: * Add the primary key constraint in isolation.
1467: */
1468: protected void addPrimaryKeyConstraint(JdbcTable t, CharBuf s) {
1469: s.append("ALTER TABLE ");
1470: s.append(t.name);
1471: s.append(" ADD ");
1472: appendPrimaryKeyConstraint(t, s);
1473: }
1474:
1475: /**
1476: * Drop the primary key constraint in isolation.
1477: */
1478: protected void dropPrimaryKeyConstraint(JdbcTable t, CharBuf s) {
1479: s.append("ALTER TABLE ");
1480: s.append(t.name);
1481: s.append(" DROP CONSTRAINT ");
1482: s.append(t.pkConstraintName);
1483: }
1484:
1485: public String getRunCommand() {
1486: return ";\n";
1487: }
1488:
1489: /**
1490: * Make sure the database tables and columns exist. This is used as a
1491: * quick check when the server starts.
1492: */
1493: public boolean checkDDLForStartup(ArrayList tables, Connection con,
1494: PrintWriter out, PrintWriter fix, ControlParams params)
1495: throws SQLException {
1496: Statement stat = null;
1497: boolean allIsWell = true;
1498: try {
1499: con.rollback();
1500: con.setAutoCommit(true);
1501: stat = con.createStatement();
1502: int n = tables.size();
1503: for (int i = 0; i < n; i++) {
1504: JdbcTable t = (JdbcTable) tables.get(i);
1505: if (!checkTable(t, stat, out))
1506: allIsWell = false;
1507: }
1508: } finally {
1509: if (stat != null) {
1510: try {
1511: stat.close();
1512: } catch (SQLException e) {
1513: // ignore
1514: }
1515: }
1516: con.setAutoCommit(false);
1517: }
1518: if (!allIsWell) {
1519: fix.println(comment("NOT IMPLEMENTED"));
1520: }
1521: return allIsWell;
1522: }
1523:
1524: /**
1525: * Check that the columns of t match those in the database schema.
1526: */
1527: protected boolean checkTable(JdbcTable t, Statement stat,
1528: PrintWriter out) {
1529: CharBuf s = new CharBuf();
1530: s.append("select ");
1531: int nc = t.cols.length;
1532: for (int i = 0; i < nc; i++) {
1533: s.append(t.cols[i].name);
1534: if (i != (nc - 1)) {
1535: s.append(", ");
1536: } else {
1537: s.append(" from ");
1538: }
1539: }
1540: s.append(t.name);
1541: s.append(" where 1 = 2");
1542: String sql = s.toString();
1543:
1544: try {
1545: stat.executeQuery(sql);
1546: return true;
1547: } catch (SQLException x) {
1548: printError(out, t.name);
1549: // we have a error, now we check if the table exist
1550: boolean tableExist = false;
1551: try {
1552: s.clear();
1553: s.append("select * from ");
1554: s.append(t.name);
1555: s.append(" where 1 = 2");
1556: stat.executeQuery(s.toString());
1557: tableExist = true;
1558: } catch (SQLException tablex) {
1559: printErrorMsg(out, "Table '" + t.name
1560: + "' does not exist.");
1561: }
1562: if (tableExist) {
1563: // the table does exist, now we find the what column does not exist
1564: s.clear();
1565: s.append(" from ");
1566: s.append(t.name);
1567: s.append(" where 1 = 2");
1568: String from = s.toString();
1569: String column = null;
1570: for (int i = 0; i < nc; i++) {
1571: column = t.cols[i].name;
1572: s.clear();
1573: s.append("select ");
1574: s.append(column);
1575: s.append(from);
1576: try {
1577: stat.executeQuery(s.toString());
1578: } catch (SQLException columnx) {
1579: printErrorMsg(out, "Column '" + column
1580: + "' does not exist.");
1581: }
1582: }
1583: }
1584: return false;
1585: }
1586: }
1587:
1588: private static void printError(PrintWriter out, String tableName) {
1589: out.print("\nTable ");
1590: out.print(tableName);
1591: out.println(" : FAIL");
1592: }
1593:
1594: private static void printErrorMsg(PrintWriter out, String error) {
1595: out.print(" ");
1596: out.println(error);
1597: }
1598:
1599: /**
1600: * Get all the database tables and columns that is not system tables
1601: * and that is filled with what field it belongs to.
1602: */
1603: public HashMap getDatabaseMetaData(ArrayList tables, Connection con)
1604: throws SQLException {
1605: HashMap dbMap;
1606: ControlParams params = new ControlParams();
1607: params.setColumnsOnly(true);
1608: try {
1609: customizeForServer(con);
1610: con.rollback();
1611: con.setAutoCommit(true);
1612: dbMap = getDBSchema(con, params);
1613: setAllTableAndViewNames(con);
1614: } finally {
1615: con.setAutoCommit(false);
1616: }
1617:
1618: fillDatabaseMetaData(tables, dbMap);
1619: return dbMap;
1620: }
1621:
1622: /**
1623: * Fill the db classes with mapping info
1624: */
1625: public void fillDatabaseMetaData(ArrayList tables, HashMap dbMap) {
1626: int n = tables.size();
1627: for (int m = 0; m < n; m++) {
1628: JdbcTable ourTable = (JdbcTable) tables.get(m);
1629: JdbcTable dbTable = (JdbcTable) dbMap.get(ourTable.name
1630: .toLowerCase());
1631: if (dbTable != null) {
1632: dbTable.comment = ourTable.comment;
1633: if (ourTable.cols != null) {
1634: for (int i = 0; i < ourTable.cols.length; i++) {
1635: JdbcColumn ourCol = ourTable.cols[i];
1636: // check if our column is in there
1637: JdbcColumn dbCol = null;
1638: if (dbTable.cols != null) {
1639: for (int j = 0; j < dbTable.cols.length; j++) {
1640: JdbcColumn col = dbTable.cols[j];
1641: if (ourCol.name
1642: .equalsIgnoreCase(col.name)) {
1643: dbCol = col;
1644: dbCol.comment = ourCol.comment;
1645: break;
1646: }
1647: }
1648: }
1649: }
1650: }
1651: }
1652: }
1653: }
1654:
1655: public boolean checkDDL(ArrayList tables, Connection con,
1656: PrintWriter errors, PrintWriter fix, ControlParams params)
1657: throws SQLException {
1658: HashMap dbMap;
1659: try {
1660: customizeForServer(con);
1661: con.rollback();
1662: con.setAutoCommit(true);
1663: dbMap = getDBSchema(con, params);
1664: setAllTableAndViewNames(con);
1665: } finally {
1666: con.setAutoCommit(false);
1667: }
1668: HashMap nameMap = new HashMap();
1669: try {
1670: for (Iterator iterator = tables.iterator(); iterator
1671: .hasNext();) {
1672: JdbcTable ourTable = (JdbcTable) iterator.next();
1673: JdbcTable dbTable = (JdbcTable) dbMap.get(ourTable.name
1674: .toLowerCase());
1675:
1676: if (dbTable != null) {
1677: nameMap.put(ourTable.name, ourTable);
1678: ourTable.name = dbTable.name;
1679: }
1680: }
1681:
1682: ArrayList diffList = checkAllTables(tables, dbMap, params);
1683:
1684: if (diffList.isEmpty()) {
1685: allTableList = null;
1686: return true;
1687: } else {
1688: DiffUtil.reportErrors(diffList, errors);
1689: reportFixes(diffList, fix);
1690: allTableList = null;
1691: return false;
1692: }
1693: } finally {
1694: Set set = nameMap.keySet();
1695: for (Iterator iter = set.iterator(); iter.hasNext();) {
1696: String name = (String) iter.next();
1697: JdbcTable table = (JdbcTable) nameMap.get(name);
1698: table.name = name;
1699: }
1700: }
1701: }
1702:
1703: protected String getCatalog(Connection con) throws SQLException {
1704: return null;
1705: }
1706:
1707: protected String getSchema(Connection con) {
1708: return null;
1709: }
1710:
1711: protected boolean isValidSchemaTable(String tableName) {
1712: return true;
1713: }
1714:
1715: /**
1716: * Get the JdbcTable from the database for the given database connection and table name.
1717: */
1718: public HashMap getDBSchema(Connection con, ControlParams params)
1719: throws SQLException {
1720: DatabaseMetaData meta = con.getMetaData();
1721:
1722: HashMap jdbcTableMap = new HashMap(); // main map of jdbc tables
1723:
1724: String catalog = getCatalog(con);
1725: String schema = getSchema(con);
1726:
1727: // now we do columns
1728: String tableName = null;
1729: ResultSet rsColumn = meta.getColumns(catalog, schema, null,
1730: null);
1731: ArrayList currentColumns = null;
1732:
1733: while (rsColumn.next()) {
1734:
1735: String temptableName = rsColumn.getString("TABLE_NAME");
1736:
1737: if (!isValidSchemaTable(temptableName)) {
1738: continue;
1739: }
1740:
1741: if (tableName == null) { // this is the first one
1742: tableName = temptableName;
1743: currentColumns = new ArrayList();
1744: JdbcTable jdbcTable = new JdbcTable();
1745: jdbcTable.name = tableName;
1746: jdbcTableMap.put(tableName, jdbcTable);
1747: }
1748:
1749: if (!temptableName.equals(tableName)) { // now we set everyting up for prev table
1750: JdbcColumn[] jdbcColumns = new JdbcColumn[currentColumns
1751: .size()];
1752: currentColumns.toArray(jdbcColumns);
1753: JdbcTable jdbcTable0 = (JdbcTable) jdbcTableMap
1754: .get(tableName);
1755: jdbcTable0.cols = jdbcColumns;
1756:
1757: tableName = temptableName;
1758: currentColumns.clear();
1759: JdbcTable jdbcTable1 = new JdbcTable();
1760: jdbcTable1.name = tableName;
1761: jdbcTableMap.put(tableName, jdbcTable1);
1762: }
1763:
1764: JdbcColumn col = new JdbcColumn();
1765:
1766: col.name = rsColumn.getString("COLUMN_NAME");
1767: col.sqlType = rsColumn.getString("TYPE_NAME");
1768: col.jdbcType = rsColumn.getInt("DATA_TYPE");
1769: col.length = rsColumn.getInt("COLUMN_SIZE");
1770: col.scale = rsColumn.getInt("DECIMAL_DIGITS");
1771: col.nulls = "YES".equals(rsColumn.getString("IS_NULLABLE"));
1772:
1773: // if (tableName.equalsIgnoreCase("custom_types")){ //sqlType longtext
1774: // System.out.println(col.name);
1775: // System.out.println(col.sqlType);
1776: // System.out.println(col.jdbcType);
1777: // System.out.println(col.length);
1778: // System.out.println(col.scale);
1779: // System.out.println("---------------------");
1780: // }
1781:
1782: if (col.jdbcType == java.sql.Types.OTHER
1783: && col.sqlType.equals("longtext")) {
1784: col.jdbcType = java.sql.Types.CLOB;
1785: }
1786:
1787: if (col.jdbcType == 16) {
1788: col.jdbcType = java.sql.Types.BIT;
1789: }
1790:
1791: switch (col.jdbcType) {
1792: case java.sql.Types.BIT:
1793: case java.sql.Types.TINYINT:
1794: case java.sql.Types.SMALLINT:
1795: case java.sql.Types.INTEGER:
1796: case java.sql.Types.BIGINT:
1797: case java.sql.Types.LONGVARBINARY:
1798: case java.sql.Types.BLOB:
1799: case java.sql.Types.LONGVARCHAR:
1800: case java.sql.Types.CLOB:
1801: case java.sql.Types.DATE:
1802: case java.sql.Types.TIME:
1803: case java.sql.Types.TIMESTAMP:
1804: col.length = 0;
1805: col.scale = 0;
1806: default:
1807: }
1808:
1809: currentColumns.add(col);
1810: }
1811: // we fin last table
1812: if (currentColumns != null) {
1813: JdbcColumn[] lastJdbcColumns = new JdbcColumn[currentColumns
1814: .size()];
1815: if (lastJdbcColumns != null) {
1816: currentColumns.toArray(lastJdbcColumns);
1817: JdbcTable colJdbcTable = (JdbcTable) jdbcTableMap
1818: .get(tableName);
1819: colJdbcTable.cols = lastJdbcColumns;
1820: tableName = null;
1821: currentColumns.clear();
1822: }
1823: }
1824:
1825: if (rsColumn != null) {
1826: try {
1827: rsColumn.close();
1828: } catch (SQLException e) {
1829: }
1830: }
1831:
1832: if (!params.checkColumnsOnly()) {
1833: Set mainTableNames = jdbcTableMap.keySet();
1834: if (params.isCheckPK()) {
1835: // now we do primaryKeys ///////////////////////////////////////////////////////////////////////
1836: for (Iterator iterator = mainTableNames.iterator(); iterator
1837: .hasNext();) {
1838: tableName = (String) iterator.next();
1839: JdbcTable jdbcTable = (JdbcTable) jdbcTableMap
1840: .get(tableName);
1841: HashMap pkMap = new HashMap();
1842: HashMap pkNames = new HashMap();
1843: ResultSet rsPKs = meta.getPrimaryKeys(catalog,
1844: schema, tableName);
1845: int pkCount = 0;
1846: while (rsPKs.next()) {
1847: pkCount++;
1848: pkMap.put(rsPKs.getString("COLUMN_NAME"), null);
1849: String pkName = rsPKs.getString("PK_NAME");
1850: jdbcTable.pkConstraintName = pkName;
1851: pkNames.put(pkName, null);
1852: }
1853: rsPKs.close();
1854: if (pkCount != 0) {
1855: JdbcColumn[] pkColumns = new JdbcColumn[pkCount];
1856: if (pkColumns != null) {
1857: int indexOfPKCount = 0;
1858: for (int i = 0; i < jdbcTable.cols.length; i++) {
1859: JdbcColumn jdbcColumn = jdbcTable.cols[i];
1860: if (pkMap.containsKey(jdbcColumn.name)) {
1861: pkColumns[indexOfPKCount] = jdbcColumn;
1862: jdbcColumn.pk = true;
1863: indexOfPKCount++;
1864: }
1865: }
1866: jdbcTable.pk = pkColumns;
1867: }
1868: }
1869:
1870: }
1871:
1872: // end of primaryKeys ///////////////////////////////////////////////////////////////////////
1873: }
1874: if (params.isCheckIndex()) {
1875: // now we do index /////////////////////////////////////////////////////////////////////////
1876: for (Iterator iterator = mainTableNames.iterator(); iterator
1877: .hasNext();) {
1878: tableName = (String) iterator.next();
1879: JdbcTable jdbcTable = (JdbcTable) jdbcTableMap
1880: .get(tableName);
1881: ResultSet rsIndex = null;
1882: try {
1883: rsIndex = meta.getIndexInfo(catalog, schema,
1884: tableName, false, false);
1885: } catch (SQLException e) {
1886: iterator.remove();
1887: continue;
1888: }
1889:
1890: HashMap indexNameMap = new HashMap();
1891: ArrayList indexes = new ArrayList();
1892: while (rsIndex.next()) {
1893:
1894: String indexName = rsIndex
1895: .getString("INDEX_NAME");
1896: if (indexName != null
1897: && !indexName
1898: .equals(jdbcTable.pkConstraintName)
1899: && !indexName.startsWith("SYS_IDX_")) {
1900: if (indexNameMap.containsKey(indexName)) {
1901: JdbcIndex index = null;
1902: for (Iterator iter = indexes.iterator(); iter
1903: .hasNext();) {
1904: JdbcIndex jdbcIndex = (JdbcIndex) iter
1905: .next();
1906: if (jdbcIndex.name
1907: .equals(indexName)) {
1908: index = jdbcIndex;
1909: }
1910: }
1911: if (index != null) {
1912: JdbcColumn[] tempIndexColumns = index.cols;
1913: JdbcColumn[] indexColumns = new JdbcColumn[tempIndexColumns.length + 1];
1914: System.arraycopy(tempIndexColumns,
1915: 0, indexColumns, 0,
1916: tempIndexColumns.length);
1917: String colName = rsIndex
1918: .getString("COLUMN_NAME");
1919: for (int i = 0; i < jdbcTable.cols.length; i++) {
1920: JdbcColumn jdbcColumn = jdbcTable.cols[i];
1921: if (colName
1922: .equals(jdbcColumn.name)) {
1923: indexColumns[tempIndexColumns.length] = jdbcColumn;
1924: jdbcColumn.partOfIndex = true;
1925: }
1926: }
1927: index.setCols(indexColumns);
1928: }
1929: } else {
1930: indexNameMap.put(indexName, null);
1931: JdbcIndex index = new JdbcIndex();
1932: index.name = indexName;
1933: index.unique = !rsIndex
1934: .getBoolean("NON_UNIQUE");
1935: short indexType = rsIndex
1936: .getShort("TYPE");
1937: switch (indexType) {
1938: case DatabaseMetaData.tableIndexClustered:
1939: index.clustered = true;
1940: break;
1941: }
1942: String colName = rsIndex
1943: .getString("COLUMN_NAME");
1944: JdbcColumn[] indexColumns = new JdbcColumn[1];
1945: for (int i = 0; i < jdbcTable.cols.length; i++) {
1946: JdbcColumn jdbcColumn = jdbcTable.cols[i];
1947: if (colName.equals(jdbcColumn.name)) {
1948: indexColumns[0] = jdbcColumn;
1949: jdbcColumn.partOfIndex = true;
1950: }
1951: }
1952: if (indexColumns[0] != null) {
1953: index.setCols(indexColumns);
1954: indexes.add(index);
1955: }
1956: }
1957: }
1958: }
1959: if (indexes != null) {
1960: JdbcIndex[] jdbcIndexes = new JdbcIndex[indexes
1961: .size()];
1962: if (jdbcIndexes != null) {
1963: indexes.toArray(jdbcIndexes);
1964: jdbcTable.indexes = jdbcIndexes;
1965: }
1966: }
1967: if (rsIndex != null) {
1968: try {
1969: rsIndex.close();
1970: } catch (SQLException e) {
1971: }
1972: }
1973: }
1974:
1975: // end of index ///////////////////////////////////////////////////////////////////////
1976: }
1977: if (params.isCheckConstraint()) {
1978: // now we do forign keys /////////////////////////////////////////////////////////////
1979: for (Iterator iterator = mainTableNames.iterator(); iterator
1980: .hasNext();) {
1981: tableName = (String) iterator.next();
1982: JdbcTable jdbcTable = (JdbcTable) jdbcTableMap
1983: .get(tableName);
1984: ResultSet rsFKs = null;
1985: try {
1986: rsFKs = meta.getImportedKeys(catalog, schema,
1987: tableName);
1988: } catch (SQLException e) {
1989: iterator.remove();
1990: continue;
1991: }
1992: HashMap constraintNameMap = new HashMap();
1993: ArrayList constraints = new ArrayList();
1994: while (rsFKs.next()) {
1995:
1996: String fkName = rsFKs.getString("FK_NAME");
1997:
1998: if (constraintNameMap.containsKey(fkName)) {
1999: JdbcConstraint constraint = null;
2000: for (Iterator iter = constraints.iterator(); iter
2001: .hasNext();) {
2002: JdbcConstraint jdbcConstraint = (JdbcConstraint) iter
2003: .next();
2004: if (jdbcConstraint.name.equals(fkName)) {
2005: constraint = jdbcConstraint;
2006: }
2007: }
2008:
2009: JdbcColumn[] tempConstraintColumns = constraint.srcCols;
2010: JdbcColumn[] constraintColumns = new JdbcColumn[tempConstraintColumns.length + 1];
2011: System.arraycopy(tempConstraintColumns, 0,
2012: constraintColumns, 0,
2013: tempConstraintColumns.length);
2014: String colName = rsFKs
2015: .getString("FKCOLUMN_NAME");
2016: for (int i = 0; i < jdbcTable.cols.length; i++) {
2017: JdbcColumn jdbcColumn = jdbcTable.cols[i];
2018: if (colName.equals(jdbcColumn.name)) {
2019: constraintColumns[tempConstraintColumns.length] = jdbcColumn;
2020: jdbcColumn.foreignKey = true;
2021: }
2022: }
2023: constraint.srcCols = constraintColumns;
2024: } else {
2025: constraintNameMap.put(fkName, null);
2026: JdbcConstraint constraint = new JdbcConstraint();
2027: constraint.name = fkName;
2028: constraint.src = jdbcTable;
2029: String colName = rsFKs
2030: .getString("FKCOLUMN_NAME");
2031: JdbcColumn[] constraintColumns = new JdbcColumn[1];
2032: for (int i = 0; i < jdbcTable.cols.length; i++) {
2033: JdbcColumn jdbcColumn = jdbcTable.cols[i];
2034: if (colName.equals(jdbcColumn.name)) {
2035: constraintColumns[0] = jdbcColumn;
2036: jdbcColumn.foreignKey = true;
2037: }
2038: }
2039: constraint.srcCols = constraintColumns;
2040: constraint.dest = (JdbcTable) jdbcTableMap
2041: .get(rsFKs
2042: .getString("PKTABLE_NAME"));
2043: constraints.add(constraint);
2044: }
2045:
2046: }
2047: if (constraints != null) {
2048: JdbcConstraint[] jdbcConstraints = new JdbcConstraint[constraints
2049: .size()];
2050: if (jdbcConstraints != null) {
2051: constraints.toArray(jdbcConstraints);
2052: jdbcTable.constraints = jdbcConstraints;
2053: }
2054: }
2055: if (rsFKs != null) {
2056: try {
2057: rsFKs.close();
2058: } catch (SQLException e) {
2059: }
2060: }
2061: }
2062: // end of forign keys /////////////////////////////////////////////////////////////
2063: }
2064: }
2065:
2066: HashMap returnMap = new HashMap();
2067: Collection col = jdbcTableMap.values();
2068: for (Iterator iterator = col.iterator(); iterator.hasNext();) {
2069: JdbcTable table = (JdbcTable) iterator.next();
2070: returnMap.put(table.name.toLowerCase(), table);
2071: }
2072: fixAllNames(returnMap);
2073: return returnMap;
2074: }
2075:
2076: boolean isDirectDropColumnSupported() {
2077: return true;
2078: }
2079:
2080: boolean isDirectAddColumnSupported(JdbcColumn ourCol) {
2081: return true;
2082: }
2083:
2084: boolean isDirectNullColumnChangesSupported() {
2085: return true;
2086: }
2087:
2088: boolean isDirectScaleColumnChangesSupported(JdbcColumn ourCol,
2089: JdbcColumn dbCol) {
2090: return true;
2091: }
2092:
2093: boolean isDirectLenghtColumnChangesSupported(JdbcColumn ourCol,
2094: JdbcColumn dbCol) {
2095: return true;
2096: }
2097:
2098: boolean isDirectTypeColumnChangesSupported(JdbcColumn ourCol,
2099: JdbcColumn dbCol) {
2100: return true;
2101: }
2102:
2103: boolean isDropConstraintsForDropTableSupported() {
2104: return true;
2105: }
2106:
2107: boolean isDropPrimaryKeySupported() {
2108: return true;
2109: }
2110:
2111: /**
2112: * Is this a sequence column from a List implementation that we are dropping to create a Set?
2113: */
2114: boolean isDropSequenceColumn(TableDiff tableDiff,
2115: JdbcColumn dropColumn) {
2116: JdbcTable ourTable = tableDiff.getOurTable();
2117: JdbcTable dbTable = tableDiff.getDbTable();
2118: // we have a seq if the old col was in the old pk
2119: // and the new pk includes another column
2120: if (ourTable.getColsForCreateTable().length == 2
2121: && ourTable.getPkNames().length == 2) {
2122: try {
2123: if (dbTable.findPkColumn(dropColumn.name) != null) {
2124: return true;
2125: }
2126: } catch (Exception e) {
2127: return false;
2128: }
2129: }
2130: return false;
2131: }
2132:
2133: /**
2134: * @param addColumn
2135: * @return
2136: */
2137: boolean isAddSequenceColumn(JdbcColumn addColumn) {
2138: if (addColumn.comment != null
2139: && addColumn.comment
2140: .equals(JdbcMetaDataBuilder.SEQUENCE_FIELDNAME)) {
2141: return true;
2142: } else {
2143: return false;
2144: }
2145: }
2146:
2147: private ArrayList checkAllTables(ArrayList tables, HashMap dbMap,
2148: ControlParams params) {
2149: ArrayList diffList = new ArrayList();
2150: int n = tables.size();
2151: for (int i = 0; i < n; i++) {
2152: JdbcTable ourTable = (JdbcTable) tables.get(i);
2153: TableDiff diff = DiffUtil.checkTable(this , ourTable,
2154: (JdbcTable) dbMap.get(ourTable.name.toLowerCase()),
2155: params);
2156: if (diff != null) {
2157: diffList.add(diff);
2158: }
2159: }
2160:
2161: // if we have pks that we drop then we have to check if there are constraints that we
2162: // have to drop first that refrences this pk.
2163: ArrayList dropConsList = new ArrayList();
2164: for (Iterator iter = diffList.iterator(); iter.hasNext();) {
2165: TableDiff tableDiff = (TableDiff) iter.next();
2166: JdbcTable destTable = tableDiff.getDbTable();
2167: for (Iterator iterIndex = tableDiff.getPkDiffs().iterator(); iterIndex
2168: .hasNext();) {
2169: PKDiff pkDiff = (PKDiff) iterIndex.next();
2170: if (!pkDiff.isMissingPK()) {
2171: for (Iterator mainTables = tables.iterator(); mainTables
2172: .hasNext();) {
2173: JdbcTable ourJdbcTable = (JdbcTable) mainTables
2174: .next();
2175: JdbcTable dbJdbcTable = (JdbcTable) dbMap
2176: .get(ourJdbcTable.name.toLowerCase());
2177: if (dbJdbcTable != null
2178: && dbJdbcTable.constraints != null) {
2179: for (int i = 0; i < dbJdbcTable.constraints.length; i++) {
2180: JdbcConstraint dbConstraint = dbJdbcTable.constraints[i];
2181: if (dbConstraint.dest != null
2182: && destTable != null) {
2183: if (dbConstraint.dest.name
2184: .equalsIgnoreCase(destTable.name)) { // we found the constraint that we need to drop
2185: if (!dropConsList
2186: .contains(dbConstraint)) {
2187: pkDiff
2188: .setDropConstraintsRefs(dbConstraint);
2189: dropConsList
2190: .add(dbConstraint);
2191: for (int j = 0; j < ourJdbcTable.constraints.length; j++) {
2192: JdbcConstraint ourConstraint = ourJdbcTable.constraints[j];
2193: if (ourConstraint.name
2194: .equalsIgnoreCase(dbConstraint.name)) {
2195: if (null == DiffUtil
2196: .checkConstraint(
2197: ourConstraint,
2198: dbConstraint,
2199: params)) {
2200: // we found the constraint in our schema that did not have a problem,
2201: // so we add it again
2202: pkDiff
2203: .setAddConstraintsRefs(ourConstraint);
2204: }
2205: }
2206: }
2207: }
2208: }
2209: }
2210: }
2211: }
2212: }
2213: }
2214: }
2215: }
2216:
2217: for (Iterator iter = diffList.iterator(); iter.hasNext();) {
2218: TableDiff tableDiff = (TableDiff) iter.next();
2219: for (Iterator iterator = tableDiff.getConstraintDiffs()
2220: .iterator(); iterator.hasNext();) {
2221: ConstraintDiff diff = (ConstraintDiff) iterator.next();
2222: if (dropConsList.contains(diff.getDbConstraint())) {
2223: diff.setDrop(false); // don't drop this, it has already been dropped
2224: }
2225: }
2226: }
2227:
2228: // if we have tables that we are going to drop then we have to check if there are constraints that we
2229: // have to recreate that refrences this table.
2230: ArrayList dropTableList = new ArrayList();
2231: for (Iterator iter = diffList.iterator(); iter.hasNext();) {
2232: TableDiff tableDiff = (TableDiff) iter.next();
2233: JdbcTable ourTable = tableDiff.getOurTable();
2234: boolean direct = true;
2235: for (Iterator iterCol = tableDiff.getColDiffs().iterator(); iterCol
2236: .hasNext();) {
2237: ColumnDiff diff = (ColumnDiff) iterCol.next();
2238: if (diff.isExtraCol()) {
2239: if (isDropSequenceColumn(tableDiff, diff.getDbCol())) {
2240: direct = false;
2241: } else if (!isDirectDropColumnSupported()) {
2242: direct = false;
2243: }
2244: }
2245: if (diff.isMissingCol()) {
2246: if (isAddSequenceColumn(diff.getOurCol())) {
2247: direct = false;
2248: } else if (!isDirectAddColumnSupported(diff
2249: .getOurCol())) {
2250: direct = false;
2251: }
2252: }
2253:
2254: if (diff.isLenghtDiff()) {
2255: if (!isDirectLenghtColumnChangesSupported(diff
2256: .getOurCol(), diff.getDbCol())) {
2257: direct = false;
2258: }
2259: }
2260: if (diff.isNullDiff()) {
2261: if (!isDirectNullColumnChangesSupported()) {
2262: direct = false;
2263: }
2264: }
2265: if (diff.isScaleDiff()) {
2266: if (!isDirectScaleColumnChangesSupported(diff
2267: .getOurCol(), diff.getDbCol())) {
2268: direct = false;
2269: }
2270: }
2271: if (diff.isTypeDiff()) {
2272: if (!isDirectTypeColumnChangesSupported(diff
2273: .getOurCol(), diff.getDbCol())) {
2274: direct = false;
2275: }
2276: }
2277: }
2278: if (!direct) {
2279: dropTableList.add(ourTable);
2280: }
2281: }
2282:
2283: for (Iterator iter = dropTableList.iterator(); iter.hasNext();) {
2284: JdbcTable jdbcTable = (JdbcTable) iter.next(); // this table will be droped
2285: // we must find all constraints that references this table and put them back
2286: for (Iterator iterator = tables.iterator(); iterator
2287: .hasNext();) {
2288: JdbcTable table = (JdbcTable) iterator.next();
2289: if (!dropTableList.contains(table)) {
2290: if (table.constraints != null) {
2291: for (int i = 0; i < table.constraints.length; i++) {
2292: JdbcConstraint constraint = table.constraints[i];
2293: if (constraint.dest == jdbcTable) {// this constraint will be dropped
2294:
2295: TableDiff tableDiff = null;
2296: for (Iterator iterDiff = diffList
2297: .iterator(); iterDiff.hasNext();) {
2298: TableDiff tempTableDiff = (TableDiff) iterDiff
2299: .next();
2300: if (tempTableDiff.getOurTable() == constraint.src) {
2301: tableDiff = tempTableDiff;
2302: }
2303: }
2304: if (tableDiff == null) {
2305: tableDiff = new TableDiff(
2306: constraint.src, null);
2307: ConstraintDiff diff = new ConstraintDiff(
2308: constraint, null);
2309:
2310: diff.setRecreate(true);
2311: diff.setDrop(false);
2312: tableDiff.getConstraintDiffs().add(
2313: diff);
2314: // there are no real errors, so we set it
2315: tableDiff.setHasRealErrors(false);
2316: diffList.add(tableDiff);
2317: } else {
2318: ConstraintDiff ourConstDiff = null;
2319: for (Iterator iterConstraint = tableDiff
2320: .getConstraintDiffs()
2321: .iterator(); iterConstraint
2322: .hasNext();) {
2323: ConstraintDiff diff = (ConstraintDiff) iterConstraint
2324: .next();
2325: if (diff.getOurConstraint() == constraint) {
2326: diff.setRecreate(true);
2327: diff.setDrop(false);
2328: ourConstDiff = diff;
2329: }
2330: }
2331:
2332: if (ourConstDiff == null) {// we need to add it
2333: ourConstDiff = new ConstraintDiff(
2334: constraint, null);
2335: ourConstDiff.setRecreate(true);
2336: ourConstDiff.setDrop(false);
2337: tableDiff.getConstraintDiffs()
2338: .add(ourConstDiff);
2339: }
2340: }
2341: }
2342: }
2343: }
2344: }
2345: }
2346: }
2347: if (!isDropConstraintsForDropTableSupported()) {
2348:
2349: for (Iterator iter = dropTableList.iterator(); iter
2350: .hasNext();) {
2351: JdbcTable jdbcTable = (JdbcTable) iter.next(); // this table will be droped
2352: // we must find all constraints that references this table and drop them
2353: for (Iterator iterator = dbMap.keySet().iterator(); iterator
2354: .hasNext();) {
2355: JdbcTable table = (JdbcTable) dbMap
2356: .get(((String) iterator.next())
2357: .toLowerCase());
2358: boolean isGoingToBeDroped = false;
2359: if (table != null) {
2360: for (Iterator myiter = dropTableList.iterator(); myiter
2361: .hasNext();) {
2362: JdbcTable tempJdbcTable = (JdbcTable) myiter
2363: .next();
2364: if (tempJdbcTable.name
2365: .equalsIgnoreCase(table.name)) {
2366: isGoingToBeDroped = true;
2367: }
2368: }
2369: }
2370:
2371: if (!isGoingToBeDroped) {
2372: if (table.constraints != null) {
2373: for (int i = 0; i < table.constraints.length; i++) {
2374: JdbcConstraint constraint = table.constraints[i];
2375: if (constraint.dest.name
2376: .equalsIgnoreCase(jdbcTable.name)) {// this constraint must be dropped
2377: TableDiff tableDiff = null;
2378: for (Iterator iterDiff = diffList
2379: .iterator(); iterDiff
2380: .hasNext();) {
2381: TableDiff tempTableDiff = (TableDiff) iterDiff
2382: .next();
2383: if (tempTableDiff.getOurTable() == constraint.src) {
2384: tableDiff = tempTableDiff;
2385: }
2386: }
2387: if (tableDiff == null) {
2388: tableDiff = new TableDiff(null,
2389: constraint.src);
2390: ConstraintDiff diff = new ConstraintDiff(
2391: null, constraint);
2392: diff.setDrop(true);
2393: tableDiff.getConstraintDiffs()
2394: .add(diff);
2395: // there are no real errors, so we set it
2396: tableDiff
2397: .setHasRealErrors(false);
2398: diffList.add(tableDiff);
2399: } else {
2400: ConstraintDiff dbConstDiff = null;
2401: for (Iterator iterConstraint = tableDiff
2402: .getConstraintDiffs()
2403: .iterator(); iterConstraint
2404: .hasNext();) {
2405: ConstraintDiff diff = (ConstraintDiff) iterConstraint
2406: .next();
2407: if (diff.getDbConstraint() == constraint) {
2408: diff.setDrop(true);
2409: dbConstDiff = diff;
2410: }
2411: }
2412:
2413: if (dbConstDiff == null) {// we need to add it
2414: dbConstDiff = new ConstraintDiff(
2415: null, constraint);
2416: dbConstDiff.setDrop(true);
2417: tableDiff
2418: .getConstraintDiffs()
2419: .add(dbConstDiff);
2420: }
2421: }
2422: }
2423: }
2424: }
2425: }
2426: }
2427: }
2428: }
2429:
2430: return diffList;
2431: }
2432:
2433: protected void fixCoulumns(TableDiff tableDiff, PrintWriter out) {
2434: CharBuf buff = new CharBuf();
2435: ArrayList colList = tableDiff.getColDiffs();
2436: ArrayList pkList = tableDiff.getPkDiffs();
2437: boolean isMissingPK = false; // flag, else we get multiple pk creations
2438: boolean otherPKProblems = false;
2439: for (Iterator iterator = pkList.iterator(); iterator.hasNext();) {
2440: PKDiff diff = (PKDiff) iterator.next();
2441: if (diff.isMissingPK()) {
2442: isMissingPK = true;
2443: } else if (diff.isMissingPKCol() || diff.isExtraPKCol()) {
2444: otherPKProblems = true;
2445: if (!diff.getDropConstraintsRefs().isEmpty()) {
2446: for (Iterator iter = diff.getDropConstraintsRefs()
2447: .iterator(); iter.hasNext();) {
2448: JdbcConstraint constraint = (JdbcConstraint) iter
2449: .next();
2450: buff.clear();
2451: appendRefDropConstraint(buff, constraint, true);
2452: String sql = buff.toString();
2453: print(out, sql);
2454: }
2455: }
2456: }
2457: }
2458:
2459: if (otherPKProblems && isDropPrimaryKeySupported()) {
2460: buff.clear();
2461: dropPrimaryKeyConstraint(tableDiff.getDbTable(), buff);
2462: print(out, buff.toString());
2463: }
2464: boolean direct = true; // check if we can do all the column changes direct
2465: boolean sequence = false;
2466: for (Iterator iterator = colList.iterator(); iterator.hasNext();) {
2467: ColumnDiff diff = (ColumnDiff) iterator.next();
2468: if (diff.isExtraCol()) {
2469: if (isDropSequenceColumn(tableDiff, diff.getDbCol())) {
2470: sequence = true;
2471: } else if (!isDirectDropColumnSupported()) {
2472: direct = false;
2473: }
2474:
2475: }
2476: if (diff.isMissingCol()) {
2477: if (isAddSequenceColumn(diff.getOurCol())) {
2478: sequence = true;
2479: } else if (!isDirectAddColumnSupported(diff.getOurCol())) {
2480: direct = false;
2481: }
2482: }
2483: if (diff.isLenghtDiff()) {
2484: if (!isDirectLenghtColumnChangesSupported(diff
2485: .getOurCol(), diff.getDbCol())) {
2486: direct = false;
2487: }
2488: }
2489: if (diff.isNullDiff()) {
2490: if (!isDirectNullColumnChangesSupported()) {
2491: direct = false;
2492: }
2493: }
2494: if (diff.isScaleDiff()) {
2495: if (!isDirectScaleColumnChangesSupported(diff
2496: .getOurCol(), diff.getDbCol())) {
2497: direct = false;
2498: }
2499: }
2500: if (diff.isTypeDiff()) {
2501: if (!isDirectTypeColumnChangesSupported(diff
2502: .getOurCol(), diff.getDbCol())) {
2503: direct = false;
2504: }
2505: }
2506:
2507: }
2508: if (sequence && direct) {
2509: if (!isDropConstraintsForDropTableSupported()) {
2510: fixConstraintsForNonDirectColumns(tableDiff, out, true);
2511: fixIndexForNonDirectColumns(tableDiff, out, true);
2512: }
2513: for (Iterator iterator = colList.iterator(); iterator
2514: .hasNext();) {
2515: ColumnDiff diff = (ColumnDiff) iterator.next();
2516: if (diff.isExtraCol()) {
2517: buff.clear();
2518: appendDropColumn(tableDiff, diff.getDbCol(), buff,
2519: true);
2520: print(out, buff.toString());
2521: }
2522: if (diff.isMissingCol()) {
2523: buff.clear();
2524: appendAddNewColumn(tableDiff.getOurTable(), diff
2525: .getOurCol(), buff, true);
2526: out.println(buff.toString());
2527: } else if (diff.isLenghtDiff() || diff.isNullDiff()
2528: || diff.isScaleDiff() || diff.isTypeDiff()) {
2529: buff.clear();
2530: appendModifyColumn(tableDiff, diff, buff, true);
2531: print(out, buff.toString());
2532: }
2533:
2534: }
2535: fixIndexForNonDirectColumns(tableDiff, out, false);
2536: fixConstraintsForNonDirectColumns(tableDiff, out, false);
2537:
2538: } else if (direct) {
2539: for (Iterator iterator = colList.iterator(); iterator
2540: .hasNext();) {
2541: ColumnDiff diff = (ColumnDiff) iterator.next();
2542: if (diff.isExtraCol()) {
2543: buff.clear();
2544: appendDropColumn(tableDiff, diff.getDbCol(), buff,
2545: true);
2546: print(out, buff.toString());
2547: }
2548: if (diff.isMissingCol()) {
2549: buff.clear();
2550: appendAddNewColumn(tableDiff.getOurTable(), diff
2551: .getOurCol(), buff, true);
2552: out.println(buff.toString());
2553: } else if (diff.isLenghtDiff() || diff.isNullDiff()
2554: || diff.isScaleDiff() || diff.isTypeDiff()) {
2555: buff.clear();
2556: appendModifyColumn(tableDiff, diff, buff, true);
2557: print(out, buff.toString());
2558: }
2559:
2560: }
2561:
2562: if (isMissingPK || otherPKProblems) {
2563: buff.clear();
2564: addPrimaryKeyConstraint(tableDiff.getOurTable(), buff);
2565: print(out, buff.toString());
2566: }
2567:
2568: } else {
2569: if (!isDropConstraintsForDropTableSupported()) {
2570: fixConstraintsForNonDirectColumns(tableDiff, out, true);
2571: fixIndexForNonDirectColumns(tableDiff, out, true);
2572: }
2573: fixColumnsNonDirect(tableDiff, out);
2574: fixIndexForNonDirectColumns(tableDiff, out, false);
2575: fixConstraintsForNonDirectColumns(tableDiff, out, false);
2576: }
2577: }
2578:
2579: protected void fixConstraintsForNonDirectColumns(
2580: TableDiff tableDiff, PrintWriter out, boolean drop) {
2581: // we need to create all the constraints that was droped during drop table,
2582: // but only ones that did not have problems
2583: JdbcConstraint[] constraints = null;
2584: if (drop) {
2585: if (tableDiff.getDbTable() != null) {
2586: constraints = tableDiff.getDbTable().constraints;
2587: }
2588: } else {
2589: constraints = tableDiff.getOurTable().constraints;
2590: }
2591:
2592: if (constraints != null) {
2593: for (int i = 0; i < constraints.length; i++) {
2594: JdbcConstraint constraint = constraints[i];
2595: ConstraintDiff diff = getConstraintDiffForName(
2596: tableDiff, constraint.name, drop);
2597: if (diff == null) {
2598: CharBuf buff = new CharBuf();
2599: if (drop) {
2600: appendRefDropConstraint(buff, constraint, false);
2601: } else {
2602: appendRefConstraint(buff, constraint);
2603: }
2604: String sql = buff.toString();
2605: print(out, sql);
2606: }
2607: }
2608: }
2609: }
2610:
2611: /**
2612: * Fixes all the names with spaces in
2613: *
2614: * @param nameMap
2615: */
2616: public void fixAllNames(HashMap nameMap) {
2617: Collection col = nameMap.values();
2618: for (Iterator iterator = col.iterator(); iterator.hasNext();) {
2619: JdbcTable table = (JdbcTable) iterator.next();
2620: String temptableName = table.name;
2621: if (temptableName.indexOf(' ') != -1) {
2622: table.name = "\"" + temptableName + "\"";
2623: }
2624: JdbcColumn[] cols = table.cols;
2625: if (cols != null) {
2626: for (int i = 0; i < cols.length; i++) {
2627: JdbcColumn jdbcColumn = cols[i];
2628: String tempColName = jdbcColumn.name;
2629: if (tempColName.indexOf(' ') != -1) {
2630: jdbcColumn.name = "\"" + tempColName + "\"";
2631: }
2632: }
2633: }
2634: JdbcIndex[] indexes = table.indexes;
2635: if (indexes != null) {
2636: for (int i = 0; i < indexes.length; i++) {
2637: JdbcIndex index = indexes[i];
2638: String tempIndexName = index.name;
2639: if (tempIndexName.indexOf(' ') != -1) {
2640: index.name = "\"" + tempIndexName + "\"";
2641: }
2642: }
2643: }
2644: JdbcConstraint[] constraints = table.constraints;
2645: if (constraints != null) {
2646: for (int i = 0; i < constraints.length; i++) {
2647: JdbcConstraint constraint = constraints[i];
2648: String tempConstraintName = constraint.name;
2649: if (tempConstraintName.indexOf(' ') != -1) {
2650: constraint.name = "\"" + tempConstraintName
2651: + "\"";
2652: }
2653:
2654: }
2655: }
2656: String tempPkConstraintName = table.pkConstraintName;
2657: if (tempPkConstraintName != null) {
2658: if (tempPkConstraintName.indexOf(' ') != -1) {
2659: table.pkConstraintName = "\""
2660: + tempPkConstraintName + "\"";
2661: }
2662: }
2663: }
2664:
2665: }
2666:
2667: protected void fixIndexForNonDirectColumns(TableDiff tableDiff,
2668: PrintWriter out, boolean drop) {
2669: // we need to create all the indexes that was droped during drop table,
2670: // but only ones that did not have problems
2671: JdbcIndex[] indexes = null;
2672: if (drop) {
2673: if (tableDiff.getDbTable() != null) {
2674: indexes = tableDiff.getDbTable().indexes;
2675: }
2676: } else {
2677: indexes = tableDiff.getOurTable().indexes;
2678: }
2679: if (indexes != null) {
2680: for (int i = 0; i < indexes.length; i++) {
2681: JdbcIndex index = indexes[i];
2682: IndexDiff diff = getIndexDiffForName(tableDiff,
2683: index.name, drop);
2684: if (diff == null) {
2685: CharBuf buff = new CharBuf();
2686: if (drop) {
2687: appendDropIndex(buff, tableDiff.getDbTable(),
2688: index, false);
2689: } else {
2690: appendCreateIndex(buff,
2691: tableDiff.getOurTable(), index, false);
2692: }
2693: String sql = buff.toString();
2694: print(out, sql);
2695: }
2696: }
2697: }
2698: }
2699:
2700: protected void fixColumnsNonDirect(TableDiff tableDiff,
2701: PrintWriter out) {
2702:
2703: }
2704:
2705: protected void reportFixes(ArrayList diffList, PrintWriter out)
2706: throws SQLException {
2707: // do all drop constraints
2708: for (Iterator iter = diffList.iterator(); iter.hasNext();) {
2709: TableDiff tableDiff = (TableDiff) iter.next();
2710: ArrayList constraintList = tableDiff.getConstraintDiffs();
2711: for (Iterator iterator = constraintList.iterator(); iterator
2712: .hasNext();) {
2713: ConstraintDiff diff = (ConstraintDiff) iterator.next();
2714: if (diff.drop() && !diff.isMissingConstraint()) {
2715: JdbcConstraint constraint = diff.getDbConstraint();
2716: if (constraint == null) {
2717: constraint = diff.getOurConstraint();
2718: }
2719:
2720: CharBuf buff = new CharBuf();
2721: appendRefDropConstraint(buff, constraint, true);
2722: String sql = buff.toString();
2723: print(out, sql);
2724: }
2725: }
2726: }
2727:
2728: // do all drop index
2729: for (Iterator iter = diffList.iterator(); iter.hasNext();) {
2730: TableDiff tableDiff = (TableDiff) iter.next();
2731: JdbcTable dbTable = tableDiff.getDbTable();
2732: ArrayList indexList = tableDiff.getIndexDiffs();
2733: for (Iterator iterator = indexList.iterator(); iterator
2734: .hasNext();) {
2735: IndexDiff diff = (IndexDiff) iterator.next();
2736: if (diff.isExtraIndex() || diff.isExtraCol()
2737: || diff.isMissingCol() || diff.isUniqueness()) {
2738: JdbcIndex idx = diff.getDbIndex();
2739: CharBuf buff = new CharBuf();
2740: if (idx != null) {
2741: appendDropIndex(buff, dbTable, idx, true);
2742: String sql = buff.toString();
2743: print(out, sql);
2744: }
2745: }
2746: }
2747: }
2748:
2749: for (Iterator iter = diffList.iterator(); iter.hasNext();) {
2750: TableDiff tableDiff = (TableDiff) iter.next();
2751: if (tableDiff.isMissingTable()) {
2752: generateCreateTable(tableDiff.getOurTable(), null, out,
2753: true);
2754: } else {
2755: fixCoulumns(tableDiff, out);
2756: }
2757:
2758: }
2759:
2760: // do all create index
2761: for (Iterator iter = diffList.iterator(); iter.hasNext();) {
2762: TableDiff tableDiff = (TableDiff) iter.next();
2763: JdbcTable ourTable = tableDiff.getOurTable();
2764: ArrayList indexList = tableDiff.getIndexDiffs();
2765: for (Iterator iterator = indexList.iterator(); iterator
2766: .hasNext();) {
2767: IndexDiff diff = (IndexDiff) iterator.next();
2768: if (diff.isMissingIndex() || diff.isExtraCol()
2769: || diff.isMissingCol() || diff.isUniqueness()) {
2770: JdbcIndex idx = diff.getOurIndex();
2771: CharBuf buff = new CharBuf();
2772: appendCreateIndex(buff, ourTable, idx, false);
2773: String sql = buff.toString();
2774: print(out, sql);
2775: }
2776: }
2777: }
2778:
2779: // do all create constraints
2780: for (Iterator iter = diffList.iterator(); iter.hasNext();) {
2781: TableDiff tableDiff = (TableDiff) iter.next();
2782: ArrayList constraintList = tableDiff.getConstraintDiffs();
2783: for (Iterator iterator = constraintList.iterator(); iterator
2784: .hasNext();) {
2785: ConstraintDiff diff = (ConstraintDiff) iterator.next();
2786: if (diff.isMissingConstraint() || diff.isExtraCol()
2787: || diff.isMissingCol() || diff.isRecreate()) {
2788: JdbcConstraint constraint = diff.getOurConstraint();
2789: CharBuf buff = new CharBuf();
2790: appendRefConstraint(buff, constraint);
2791: String sql = buff.toString();
2792: print(out, sql);
2793: }
2794: }
2795: }
2796:
2797: // do all create constraints that we had to drop to fix column's
2798: for (Iterator iter = diffList.iterator(); iter.hasNext();) {
2799: TableDiff tableDiff = (TableDiff) iter.next();
2800: ArrayList pkList = tableDiff.getPkDiffs();
2801: for (Iterator iterator = pkList.iterator(); iterator
2802: .hasNext();) {
2803: PKDiff diff = (PKDiff) iterator.next();
2804: for (Iterator iterPk = diff.getAddConstraintsRefs()
2805: .iterator(); iterPk.hasNext();) {
2806: JdbcConstraint constraint = (JdbcConstraint) iterPk
2807: .next();
2808: CharBuf buff = new CharBuf();
2809: appendRefConstraint(buff, constraint);
2810: String sql = buff.toString();
2811: print(out, sql);
2812: }
2813: }
2814: }
2815: }
2816:
2817: public boolean checkType(JdbcColumn ourCol, JdbcColumn dbCol) {
2818: String ourSqlType = ourCol.sqlType.toUpperCase();
2819: String dbSqlType = dbCol.sqlType.toUpperCase();
2820: if (ourCol.jdbcType == dbCol.jdbcType) {
2821: return true;
2822: } else if (ourSqlType.startsWith(dbSqlType)) {
2823: return true;
2824: } else {
2825: switch (ourCol.jdbcType) {
2826: case Types.BIT:
2827: switch (dbCol.jdbcType) {
2828: case Types.TINYINT:
2829: case Types.SMALLINT:
2830: return true;
2831: default:
2832: return false;
2833: }
2834: case Types.TINYINT:
2835: switch (dbCol.jdbcType) {
2836: case Types.BIT:
2837: case Types.SMALLINT:
2838: return true;
2839: default:
2840: return false;
2841: }
2842: case Types.SMALLINT:
2843: switch (dbCol.jdbcType) {
2844: case Types.BIT:
2845: case Types.TINYINT:
2846: return true;
2847: default:
2848: return false;
2849: }
2850: case Types.INTEGER:
2851: switch (dbCol.jdbcType) {
2852: case Types.NUMERIC:
2853: return true;
2854: default:
2855: return false;
2856: }
2857: case Types.BIGINT:
2858: switch (dbCol.jdbcType) {
2859: case Types.NUMERIC:
2860: case Types.DECIMAL:
2861: return true;
2862: default:
2863: return false;
2864: }
2865: case Types.FLOAT:
2866: switch (dbCol.jdbcType) {
2867: case Types.DOUBLE:
2868: case Types.REAL:
2869: return true;
2870: default:
2871: return false;
2872: }
2873: case Types.REAL:
2874: switch (dbCol.jdbcType) {
2875: case Types.DOUBLE:
2876: case Types.FLOAT:
2877: return true;
2878: default:
2879: return false;
2880: }
2881: case Types.DOUBLE:
2882: switch (dbCol.jdbcType) {
2883: case Types.FLOAT:
2884: case Types.REAL:
2885: return true;
2886: default:
2887: return false;
2888: }
2889: case Types.NUMERIC:
2890: switch (dbCol.jdbcType) {
2891: case Types.DECIMAL:
2892: case Types.BIGINT:
2893: return true;
2894: default:
2895: return false;
2896: }
2897: case Types.DECIMAL:
2898: switch (dbCol.jdbcType) {
2899: case Types.NUMERIC:
2900: return true;
2901: default:
2902: return false;
2903: }
2904: case Types.CHAR:
2905: switch (dbCol.jdbcType) {
2906: case Types.VARCHAR:
2907: return true;
2908: default:
2909: return false;
2910: }
2911: case Types.VARCHAR:
2912: switch (dbCol.jdbcType) {
2913: case Types.CHAR:
2914: return true;
2915: default:
2916: return false;
2917: }
2918: case Types.LONGVARCHAR:
2919: switch (dbCol.jdbcType) {
2920: case Types.CLOB:
2921: return true;
2922: default:
2923: return false;
2924: }
2925: case Types.DATE:
2926: switch (dbCol.jdbcType) {
2927: case Types.TIMESTAMP:
2928: case Types.TIME:
2929: return true;
2930: default:
2931: return false;
2932: }
2933: case Types.TIME:
2934: switch (dbCol.jdbcType) {
2935: case Types.TIMESTAMP:
2936: case Types.DATE:
2937: return true;
2938: default:
2939: return false;
2940: }
2941: case Types.TIMESTAMP:
2942: switch (dbCol.jdbcType) {
2943: case Types.DATE:
2944: case Types.TIME:
2945: return true;
2946: default:
2947: return false;
2948: }
2949: case Types.BINARY:
2950: switch (dbCol.jdbcType) {
2951: case Types.BINARY:
2952: return true;
2953: default:
2954: return false;
2955: }
2956: case Types.VARBINARY:
2957: switch (dbCol.jdbcType) {
2958: case Types.VARBINARY:
2959: return true;
2960: default:
2961: return false;
2962: }
2963: case Types.LONGVARBINARY:
2964: switch (dbCol.jdbcType) {
2965: case Types.BLOB:
2966: return true;
2967: default:
2968: return false;
2969: }
2970: case Types.BLOB:
2971: switch (dbCol.jdbcType) {
2972: case Types.LONGVARBINARY:
2973: return true;
2974: default:
2975: return false;
2976: }
2977: case Types.CLOB:
2978: switch (dbCol.jdbcType) {
2979: case Types.LONGVARCHAR:
2980: return true;
2981: default:
2982: return false;
2983: }
2984: case Types.NULL:
2985: switch (dbCol.jdbcType) {
2986: case Types.NULL:
2987: return true;
2988: default:
2989: return false;
2990: }
2991: case Types.OTHER:
2992: switch (dbCol.jdbcType) {
2993: case Types.OTHER:
2994: return true;
2995: default:
2996: return false;
2997: }
2998: case Types.DISTINCT:
2999: switch (dbCol.jdbcType) {
3000: case Types.DISTINCT:
3001: return true;
3002: default:
3003: return false;
3004: }
3005: case Types.STRUCT:
3006: switch (dbCol.jdbcType) {
3007: case Types.STRUCT:
3008: return true;
3009: default:
3010: return false;
3011: }
3012: case Types.REF:
3013: switch (dbCol.jdbcType) {
3014: case Types.REF:
3015: return true;
3016: default:
3017: return false;
3018: }
3019: case Types.JAVA_OBJECT:
3020: switch (dbCol.jdbcType) {
3021: case Types.JAVA_OBJECT:
3022: return true;
3023: default:
3024: return false;
3025: }
3026: default:
3027: return false;
3028: }
3029: }
3030: }
3031:
3032: public boolean checkScale(JdbcColumn ourCol, JdbcColumn dbCol) {
3033: switch (ourCol.jdbcType) {
3034: case Types.DATE:
3035: case Types.TIME:
3036: case Types.TIMESTAMP:
3037: return true;
3038: default:
3039: if (ourCol.scale != dbCol.scale) {
3040: return false;
3041: } else {
3042: return true;
3043: }
3044: }
3045: }
3046:
3047: public boolean checkNulls(JdbcColumn ourCol, JdbcColumn dbCol) {
3048: if (ourCol.nulls != dbCol.nulls) {
3049: return false;
3050: }
3051: return true;
3052: }
3053:
3054: public boolean checkLenght(JdbcColumn ourCol, JdbcColumn dbCol) {
3055: if (ourCol.length != dbCol.length) {
3056: if (ourCol.length != 0) {
3057: return false;
3058: }
3059: }
3060: return true;
3061: }
3062:
3063: protected String getDefaultValueComment() {
3064: return " "
3065: + comment("Please enter your own default value here.");
3066: }
3067:
3068: protected String getDefaultForType(JdbcColumn ourCol) {
3069: switch (ourCol.jdbcType) {
3070: case Types.BIT:
3071: case Types.TINYINT:
3072: case Types.SMALLINT:
3073: case Types.INTEGER:
3074: case Types.BIGINT:
3075: case Types.FLOAT:
3076: case Types.REAL:
3077: case Types.DOUBLE:
3078: case Types.NUMERIC:
3079: case Types.DECIMAL:
3080: return "0";
3081: case Types.CHAR:
3082: case Types.VARCHAR:
3083: case Types.LONGVARCHAR:
3084: case Types.CLOB:
3085: return "' '";
3086: case Types.DATE:
3087: case Types.TIME:
3088: case Types.TIMESTAMP:
3089: return "' '";
3090: case Types.BINARY:
3091: case Types.VARBINARY:
3092: case Types.LONGVARBINARY:
3093: case Types.BLOB:
3094: return "' '";
3095: case Types.NULL:
3096: case Types.OTHER:
3097: case Types.DISTINCT:
3098: case Types.STRUCT:
3099: case Types.REF:
3100: case Types.JAVA_OBJECT:
3101: return "' '";
3102: default:
3103: return "' '";
3104: }
3105:
3106: }
3107:
3108: /**
3109: * Gets the Column diff for the column name, else returns null
3110: */
3111: protected ColumnDiff getColumnDiffForName(TableDiff tableDiff,
3112: String name) {
3113: for (Iterator iter = tableDiff.getColDiffs().iterator(); iter
3114: .hasNext();) {
3115: ColumnDiff diff = (ColumnDiff) iter.next();
3116: JdbcColumn our = diff.getOurCol();
3117: if (our != null) {
3118: if (our.name.equalsIgnoreCase(name)) {// we have the right column
3119: return diff;
3120: }
3121: }
3122: }
3123: return null;
3124:
3125: }
3126:
3127: /**
3128: * Gets the Index diff for the index name, else returns null
3129: */
3130: protected IndexDiff getIndexDiffForName(TableDiff tableDiff,
3131: String name, boolean db) {
3132: for (Iterator iter = tableDiff.getIndexDiffs().iterator(); iter
3133: .hasNext();) {
3134: IndexDiff diff = (IndexDiff) iter.next();
3135: JdbcIndex index = null;
3136: if (db) {
3137: index = diff.getDbIndex();
3138: } else {
3139: index = diff.getOurIndex();
3140: }
3141: if (index != null) {
3142: if (index.name.equalsIgnoreCase(name)) {// we have the right index
3143: return diff;
3144: }
3145: }
3146: }
3147: return null;
3148:
3149: }
3150:
3151: /**
3152: * Gets the Constraint diff for the constraint name, else returns null
3153: */
3154: protected ConstraintDiff getConstraintDiffForName(
3155: TableDiff tableDiff, String name, boolean db) {
3156: for (Iterator iter = tableDiff.getConstraintDiffs().iterator(); iter
3157: .hasNext();) {
3158: ConstraintDiff diff = (ConstraintDiff) iter.next();
3159:
3160: JdbcConstraint cons = null;
3161: if (db) {
3162: cons = diff.getDbConstraint();
3163: } else {
3164: cons = diff.getOurConstraint();
3165: }
3166: if (cons != null) {
3167: if (cons.name.equalsIgnoreCase(name)) {// we have the right index
3168: return diff;
3169: }
3170: }
3171: }
3172: return null;
3173: }
3174:
3175: /**
3176: * Get the names of all tables in the database con is connected to.
3177: */
3178: public void setAllTableAndViewNames(Connection con)
3179: throws SQLException {
3180: ResultSet rs = null;
3181: try {
3182: rs = con.getMetaData().getTables(null, null, null, null);
3183: allTableList = new ArrayList();
3184: for (; rs.next();) {
3185: allTableList.add(rs.getString(3).trim().toUpperCase());
3186: }
3187: } finally {
3188: if (rs != null) {
3189: try {
3190: rs.close();
3191: } catch (SQLException x) {
3192: // ignore
3193: }
3194: }
3195: }
3196: }
3197:
3198: protected String getTempColumnName(JdbcTable table) {
3199: char j = 'a';
3200: CharBuf tempColName = new CharBuf("temp_column_" + j);
3201:
3202: for (int i = 0; i < table.cols.length; i++) {
3203: JdbcColumn col = table.cols[i];
3204: if (col.name.equalsIgnoreCase(tempColName.toString())) {
3205: int lastIndex = tempColName.toString().lastIndexOf(j);
3206: tempColName.replace(lastIndex, lastIndex + 1, ++j);
3207: i = 0;
3208: }
3209: }
3210: return tempColName.toString();
3211: }
3212:
3213: protected String getTempTableName(JdbcTable table, int lenght) {
3214: String temp = "temp_" + table.name;
3215: if (lenght < temp.length()) {
3216: temp = shrinkName(temp, lenght);
3217: }
3218: char i = 'a';
3219:
3220: while (allTableList.contains(temp.toUpperCase())) {
3221: if (i == 'a') {
3222: temp = temp + '_' + i;
3223: i++;
3224: } else {
3225: int lastIndex = temp.lastIndexOf('_');
3226: CharBuf buff = new CharBuf(temp);
3227: buff.replace(lastIndex + 1, lastIndex + 2, ++i);
3228: temp = buff.toString();
3229: }
3230: if (lenght < temp.length()) {
3231: temp = shrinkName(temp, lenght);
3232: }
3233:
3234: }
3235: allTableList.add(temp.toUpperCase());
3236: return temp;
3237: }
3238:
3239: /**
3240: * Shrink the supplied name to maxlen chars if it is longer than maxlen.
3241: * This implementation removes vowels first and then truncates if it has
3242: * to.
3243: */
3244: protected String shrinkName(String name, int maxlen) {
3245: int len = name.length();
3246: if (len <= maxlen)
3247: return name;
3248: int todo = len - maxlen;
3249: StringBuffer s = new StringBuffer();
3250: s.append(name.charAt(0));
3251: int i;
3252: for (i = 1; todo > 0 && i < len;) {
3253: char c = name.charAt(i++);
3254: if (c == 'e' || c == 'a' || c == 'i' || c == 'o'
3255: || c == 'u') {
3256: --todo;
3257: } else {
3258: s.append(c);
3259: }
3260: }
3261: if (todo == 0) {
3262: s.append(name.substring(i));
3263: }
3264: if (s.length() > maxlen)
3265: s.setLength(maxlen);
3266: return s.toString();
3267: }
3268:
3269: /**
3270: * Get a string back with lengh i
3271: */
3272: protected String pad(int i) {
3273: char[] spaces = new char[i];
3274: for (int j = 0; j < spaces.length; j++) {
3275: spaces[j] = ' ';
3276: }
3277: return String.valueOf(spaces);
3278: }
3279:
3280: /**
3281: * Use the index of the column in the 'group by' expression.
3282: */
3283: public boolean useColumnIndexForGroupBy() {
3284: return false;
3285: }
3286:
3287: /**
3288: * Calculate the typeCode for a aggregate expression.
3289: *
3290: * @param aggType The aggregate type.
3291: * @param currentTypeCode The currenct calculated typeCode.
3292: * @see MDStatics
3293: */
3294: public int getAggregateTypeCode(int aggType, int currentTypeCode) {
3295: switch (aggType) {
3296: case AggregateNode.TYPE_AVG:
3297: if (MDStaticUtils.isIntegerType(currentTypeCode)) {
3298:
3299: return MDStatics.BIGDECIMAL;
3300:
3301: } else if (currentTypeCode == MDStatics.FLOATW
3302: || currentTypeCode == MDStatics.DOUBLEW) {
3303:
3304: return MDStatics.DOUBLEW;
3305:
3306: } else {
3307: return currentTypeCode;
3308: }
3309: case AggregateNode.TYPE_COUNT:
3310: return MDStatics.LONGW;
3311: case AggregateNode.TYPE_MAX:
3312: return currentTypeCode;
3313: case AggregateNode.TYPE_MIN:
3314: return currentTypeCode;
3315: case AggregateNode.TYPE_SUM:
3316: if (MDStaticUtils.isSignedIntegerType(currentTypeCode)) {
3317:
3318: return MDStatics.LONGW;
3319:
3320: } else {
3321: return currentTypeCode;
3322: }
3323: default:
3324: throw BindingSupportImpl.getInstance().internal(
3325: "Aggregate type '" + aggType
3326: + "' is not supported.");
3327: }
3328: }
3329:
3330: /**
3331: * Should columns be added to groupBy in appear in orderby and not in groupBy.
3332: */
3333: public boolean putOrderColsInGroupBy() {
3334: return true;
3335: }
3336:
3337: /**
3338: * Provide an oportunity for the driver to update the column's
3339: * used for post insert keygen.
3340: */
3341: public void updateClassForPostInsertKeyGen(ClassMetaData cmd,
3342: JdbcMappingResolver mappingResolver) {
3343: }
3344:
3345: /**
3346: * Should we use the col alias for columns that was added to the select
3347: * list because they are in the orderby and not in the selectList.
3348: *
3349: * @return
3350: */
3351: public boolean useColAliasForAddedCols() {
3352: return false;
3353: }
3354:
3355: public boolean isOracleStoreProcs() {
3356: return false;
3357: }
3358:
3359: /**
3360: * Maps a backend exception to a specific JDO exception
3361: *
3362: * @param cause the backend exception
3363: * @param message error message. if null, the error message is
3364: * taken from the backend exception
3365: * @param isFatal is the error fatal or not
3366: */
3367: public RuntimeException mapException(Throwable cause,
3368: String message, boolean isFatal) {
3369: return defaultMapException(cause, message, isFatal);
3370: }
3371:
3372: /**
3373: * Maps a backend exception to a specific JDO exception.
3374: * This is the default implementation which is used if no SqlDriver
3375: * instance is available.
3376: *
3377: * @param cause the backend exception
3378: * @param message error message. if null, the error message is
3379: * taken from the backend exception
3380: * @param isFatal is the error fatal or not
3381: */
3382: public static RuntimeException defaultMapException(Throwable cause,
3383: String message, boolean isFatal) {
3384: BindingSupportImpl bsi = BindingSupportImpl.getInstance();
3385: if (bsi.isOwnException(cause)) {
3386: return (RuntimeException) cause;
3387: } else {
3388: if (Debug.DEBUG) {
3389: cause.printStackTrace(System.out);
3390: }
3391: if (bsi.isError(cause)) {
3392: if (bsi.isOutOfMemoryError(cause)) {
3393: return bsi.exception(cause.toString(), cause);
3394: }
3395: throw (Error) cause;
3396: }
3397: if (isFatal) {
3398: return bsi.fatalDatastore(message == null ? JdbcUtils
3399: .toString(cause) : message, cause);
3400: } else {
3401: return bsi.datastore(message == null ? JdbcUtils
3402: .toString(cause) : message, cause);
3403: }
3404: }
3405: }
3406:
3407: /**
3408: * Convenience method, which gets the SqlDriver instance from the
3409: * store and calls its mapException() method. isFatal defaults to true.
3410: * If no SqlDriver is set, SqlDriver.defaultMapException() is called.
3411: *
3412: * @param sqlDriver
3413: * @param cause the backend exception
3414: * @param message error message. if null, the error message is
3415: */
3416: public static RuntimeException mapException(SqlDriver sqlDriver,
3417: Throwable cause, String message) {
3418: return SqlDriver.mapException(sqlDriver, cause, message, true);
3419: }
3420:
3421: /**
3422: * Convenience method, which gets the SqlDriver instance from the
3423: * store and calls its mapException() method.
3424: * If no SqlDriver is set, SqlDriver.defaultMapException() is called.
3425: *
3426: * @param sqlDriver
3427: * @param cause the backend exception
3428: * @param message error message. if null, the error message is
3429: * taken from the backend exception
3430: * @param isFatal is the error fatal or not
3431: */
3432: public static RuntimeException mapException(SqlDriver sqlDriver,
3433: Throwable cause, String message, boolean isFatal) {
3434: if (sqlDriver != null) {
3435: return sqlDriver.mapException(cause, message, isFatal);
3436: } else {
3437: return SqlDriver.defaultMapException(cause, message,
3438: isFatal);
3439: }
3440: }
3441:
3442: /**
3443: * Does the driver detect and handle exceptions caused by
3444: * lock timeouts?
3445: */
3446: public boolean isHandleLockTimeout() {
3447: return false;
3448: }
3449:
3450: /**
3451: * Does the driver detect and handle exceptions caused by
3452: * duplicate primary keys?
3453: */
3454: public boolean isHandleDuplicateKey() {
3455: return false;
3456: }
3457:
3458: /**
3459: * Is this a lock timeout exception?
3460: */
3461: public boolean isLockTimeout(Throwable e) {
3462: return false;
3463: }
3464:
3465: /**
3466: * Is this a duplicate key exception?
3467: */
3468: public boolean isDuplicateKey(Throwable e) {
3469: return false;
3470: }
3471:
3472: /**
3473: * Convert d to a String suitable for embedding as a literal in an SQL
3474: * statement.
3475: */
3476: public String toSqlLiteral(double d) {
3477: return doubleFormat.format(d);
3478: }
3479:
3480: /**
3481: * Convert l to a String suitable for embedding as a literal in an SQL
3482: * statement.
3483: */
3484: public String toSqlLiteral(long l) {
3485: return Long.toString(l);
3486: }
3487:
3488: /**
3489: * Convert s to a String suitable for embedding as a literal in an SQL
3490: * statement.
3491: */
3492: public String toSqlLiteral(String s) {
3493: return '\'' + s + '\'';
3494: }
3495:
3496: /**
3497: * Convert s to a String suitable for embedding as a literal in an SQL
3498: * statement.
3499: */
3500: public String toSqlLiteral(boolean b) {
3501: return b ? "TRUE" : "FALSE";
3502: }
3503:
3504: }
|