0001: package liquibase.database;
0002:
0003: import liquibase.ChangeSet;
0004: import liquibase.RanChangeSet;
0005: import liquibase.change.*;
0006: import liquibase.database.sql.*;
0007: import liquibase.database.structure.*;
0008: import liquibase.database.template.JdbcTemplate;
0009: import liquibase.diff.DiffStatusListener;
0010: import liquibase.exception.DatabaseHistoryException;
0011: import liquibase.exception.DateParseException;
0012: import liquibase.exception.JDBCException;
0013: import liquibase.exception.UnsupportedChangeException;
0014: import liquibase.log.LogFactory;
0015: import liquibase.util.ISODateFormat;
0016: import liquibase.util.LiquibaseUtil;
0017: import liquibase.util.StringUtils;
0018:
0019: import java.math.BigInteger;
0020: import java.sql.*;
0021: import java.text.ParseException;
0022: import java.text.SimpleDateFormat;
0023: import java.util.*;
0024: import java.util.Date;
0025: import java.util.logging.Logger;
0026:
0027: /**
0028: * AbstractDatabase is extended by all supported databases as a facade to the underlying database.
0029: * The physical connection can be retrieved from the AbstractDatabase implementation, as well as any
0030: * database-specific characteristics such as the datatype for "boolean" fields.
0031: */
0032: public abstract class AbstractDatabase implements Database {
0033:
0034: private DatabaseConnection connection;
0035: private String defaultSchemaName;
0036:
0037: static final protected Logger log = LogFactory.getLogger();
0038: protected boolean changeLogTableExists;
0039: protected boolean changeLogLockTableExists;
0040: protected boolean changeLogCreateAttempted;
0041: protected boolean changeLogLockCreateAttempted;
0042:
0043: protected String currentDateTimeFunction;
0044:
0045: private JdbcTemplate jdbcTemplate = new JdbcTemplate(this );
0046:
0047: protected AbstractDatabase() {
0048: }
0049:
0050: // ------- DATABASE INFORMATION METHODS ---- //
0051:
0052: public DatabaseConnection getConnection() {
0053: return connection;
0054: }
0055:
0056: public void setConnection(Connection conn) {
0057: this .connection = new SQLConnectionDelegate(conn);
0058: try {
0059: connection.setAutoCommit(getAutoCommitMode());
0060: } catch (SQLException sqle) {
0061: log.warning("Can not set auto commit to "
0062: + getAutoCommitMode() + " on connection");
0063: }
0064: }
0065:
0066: public void setConnection(DatabaseConnection conn) {
0067: this .connection = conn;
0068: try {
0069: connection.setAutoCommit(getAutoCommitMode());
0070: } catch (SQLException sqle) {
0071: log.warning("Can not set auto commit to "
0072: + getAutoCommitMode() + " on connection");
0073: }
0074: }
0075:
0076: /**
0077: * Auto-commit mode to run in
0078: */
0079: public boolean getAutoCommitMode() {
0080: return !supportsDDLInTransaction();
0081: }
0082:
0083: /**
0084: * By default databases should support DDL within a transaction.
0085: */
0086: public boolean supportsDDLInTransaction() {
0087: return true;
0088: }
0089:
0090: /**
0091: * Returns the name of the database product according to the underlying database.
0092: */
0093: public String getDatabaseProductName() {
0094: try {
0095: return connection.getMetaData().getDatabaseProductName();
0096: } catch (SQLException e) {
0097: throw new RuntimeException("Cannot get database name");
0098: }
0099: }
0100:
0101: public String getDatabaseProductName(Connection conn)
0102: throws JDBCException {
0103: try {
0104: return conn.getMetaData().getDatabaseProductName();
0105: } catch (SQLException e) {
0106: throw new JDBCException(e);
0107: }
0108: }
0109:
0110: public String getDatabaseProductVersion() throws JDBCException {
0111: try {
0112: return connection.getMetaData().getDatabaseProductVersion();
0113: } catch (SQLException e) {
0114: throw new JDBCException(e);
0115: }
0116: }
0117:
0118: public String getDriverName() throws JDBCException {
0119: try {
0120: return connection.getMetaData().getDriverName();
0121: } catch (SQLException e) {
0122: throw new JDBCException(e);
0123: }
0124: }
0125:
0126: public String getConnectionURL() throws JDBCException {
0127: try {
0128: return connection.getMetaData().getURL();
0129: } catch (SQLException e) {
0130: throw new JDBCException(e);
0131: }
0132: }
0133:
0134: public String getConnectionUsername() throws JDBCException {
0135: try {
0136: return connection.getMetaData().getUserName();
0137: } catch (SQLException e) {
0138: throw new JDBCException(e);
0139: }
0140: }
0141:
0142: public String getDefaultCatalogName() throws JDBCException {
0143: return null;
0144: }
0145:
0146: protected String getDefaultDatabaseSchemaName()
0147: throws JDBCException {
0148: return getConnectionUsername();
0149: }
0150:
0151: public String getDefaultSchemaName() {
0152: return defaultSchemaName;
0153: }
0154:
0155: public void setDefaultSchemaName(String schemaName)
0156: throws JDBCException {
0157: this .defaultSchemaName = schemaName;
0158: }
0159:
0160: /**
0161: * Returns system (undroppable) tables and views.
0162: */
0163: protected Set<String> getSystemTablesAndViews() {
0164: return new HashSet<String>();
0165: }
0166:
0167: // ------- DATABASE FEATURE INFORMATION METHODS ---- //
0168:
0169: /**
0170: * Does the database type support sequence.
0171: */
0172: public boolean supportsSequences() {
0173: return true;
0174: }
0175:
0176: public boolean supportsAutoIncrement() {
0177: return true;
0178: }
0179:
0180: // ------- DATABASE-SPECIFIC SQL METHODS ---- //
0181:
0182: public void setCurrentDateTimeFunction(String function) {
0183: if (function != null) {
0184: this .currentDateTimeFunction = function;
0185: }
0186: }
0187:
0188: /**
0189: * Returns the database-specific datatype for the given column configuration.
0190: * This method will convert some generic column types (e.g. boolean, currency) to the correct type
0191: * for the current database.
0192: */
0193: public String getColumnType(String columnType, Boolean autoIncrement) {
0194: if (columnType.startsWith("java.sql.Types")) {
0195: String dataTypeName = columnType.substring(columnType
0196: .lastIndexOf(".") + 1);
0197: String precision = null;
0198: if (dataTypeName.indexOf("(") >= 0) {
0199: precision = dataTypeName.substring(dataTypeName
0200: .indexOf("(") + 1, dataTypeName.indexOf(")"));
0201: dataTypeName = dataTypeName.substring(0, dataTypeName
0202: .indexOf("("));
0203: }
0204:
0205: ResultSet resultSet = null;
0206: try {
0207: DatabaseConnection connection = getConnection();
0208: if (connection == null) {
0209: throw new RuntimeException(
0210: "Cannot evaluate java.sql.Types without a connection");
0211: }
0212: resultSet = connection.getMetaData().getTypeInfo();
0213: while (resultSet.next()) {
0214: String typeName = resultSet.getString("TYPE_NAME");
0215: int dataType = resultSet.getInt("DATA_TYPE");
0216: Integer requestedType = (Integer) Class.forName(
0217: "java.sql.Types").getDeclaredField(
0218: dataTypeName).get(null);
0219: if (requestedType == dataType) {
0220: if (precision == null) {
0221: return typeName;
0222: } else {
0223: return typeName + "(" + precision + ")";
0224: }
0225: }
0226: }
0227: //did not find type, fall back on our defaults for ones we can figure out
0228: if (dataTypeName.equalsIgnoreCase("BLOB")) {
0229: return getBlobType();
0230: } else if (dataTypeName.equalsIgnoreCase("CLOB")) {
0231: return getClobType();
0232: } else if (dataTypeName.equalsIgnoreCase("BOOLEAN")) {
0233: return getBooleanType();
0234: } else if (dataTypeName.equalsIgnoreCase("DATE")) {
0235: return getDateType();
0236: } else if (dataTypeName.equalsIgnoreCase("TIME")) {
0237: return getTimeType();
0238: }
0239:
0240: throw new RuntimeException(
0241: "Could not find java.sql.Types value for "
0242: + dataTypeName);
0243: } catch (Exception e) {
0244: throw new RuntimeException(e);
0245: } finally {
0246: if (resultSet != null) {
0247: try {
0248: resultSet.close();
0249: } catch (SQLException e) {
0250: ;
0251: }
0252: }
0253: }
0254: } else if ("boolean".equalsIgnoreCase(columnType)) {
0255: return getBooleanType();
0256: } else if ("currency".equalsIgnoreCase(columnType)) {
0257: return getCurrencyType();
0258: } else if ("UUID".equalsIgnoreCase(columnType)) {
0259: return getUUIDType();
0260: } else if ("BLOB".equalsIgnoreCase(columnType)
0261: || "LONGVARBINARY".equalsIgnoreCase(columnType)) {
0262: return getBlobType();
0263: } else if ("CLOB".equalsIgnoreCase(columnType)
0264: || "TEXT".equalsIgnoreCase(columnType)
0265: || "LONGVARCHAR".equalsIgnoreCase(columnType)) {
0266: return getClobType();
0267: } else if ("date".equalsIgnoreCase(columnType)) {
0268: return getDateType();
0269: } else if ("time".equalsIgnoreCase(columnType)) {
0270: return getTimeType();
0271: } else if ("dateTime".equalsIgnoreCase(columnType)) {
0272: return getDateTimeType();
0273: } else if (columnType.toUpperCase().startsWith("FLOAT(")) {
0274: return "FLOAT";
0275: } else if (columnType.toUpperCase().startsWith("DOUBLE(")) {
0276: return "DOUBLE";
0277: } else {
0278: return columnType;
0279: }
0280: }
0281:
0282: public final String getColumnType(ColumnConfig columnConfig) {
0283: return getColumnType(columnConfig.getType(), columnConfig
0284: .isAutoIncrement());
0285: }
0286:
0287: /**
0288: * The database-specific value to use for "false" "boolean" columns.
0289: */
0290: public String getFalseBooleanValue() {
0291: return "false";
0292: }
0293:
0294: /**
0295: * The database-specific value to use for "true" "boolean" columns.
0296: */
0297: public String getTrueBooleanValue() {
0298: return "true";
0299: }
0300:
0301: /**
0302: * Return a date literal with the same value as a string formatted using ISO 8601.
0303: * <p/>
0304: * Note: many databases accept date literals in ISO8601 format with the 'T' replaced with
0305: * a space. Only databases which do not accept these strings should need to override this
0306: * method.
0307: * <p/>
0308: * Implementation restriction:
0309: * Currently, only the following subsets of ISO8601 are supported:
0310: * yyyy-MM-dd
0311: * hh:mm:ss
0312: * yyyy-MM-ddThh:mm:ss
0313: */
0314: public String getDateLiteral(String isoDate) {
0315: if (isDateOnly(isoDate) || isTimeOnly(isoDate)) {
0316: return "'" + isoDate + "'";
0317: } else if (isDateTime(isoDate)) {
0318: StringBuffer val = new StringBuffer();
0319: val.append("'");
0320: val.append(isoDate.substring(0, 10));
0321: val.append(" ");
0322: //noinspection MagicNumber
0323: val.append(isoDate.substring(11));
0324: val.append("'");
0325: return val.toString();
0326: } else {
0327: return "BAD_DATE_FORMAT:" + isoDate;
0328: }
0329: }
0330:
0331: public String getDateLiteral(java.sql.Timestamp date) {
0332: return getDateLiteral(new ISODateFormat().format(date)
0333: .replaceFirst("^'", "").replaceFirst("'$", ""));
0334: }
0335:
0336: public String getDateLiteral(java.sql.Date date) {
0337: return getDateLiteral(new ISODateFormat().format(date)
0338: .replaceFirst("^'", "").replaceFirst("'$", ""));
0339: }
0340:
0341: public String getDateLiteral(java.sql.Time date) {
0342: return getDateLiteral(new ISODateFormat().format(date)
0343: .replaceFirst("^'", "").replaceFirst("'$", ""));
0344: }
0345:
0346: public String getDateLiteral(Date date) {
0347: if (date instanceof java.sql.Date) {
0348: return getDateLiteral(((java.sql.Date) date));
0349: } else if (date instanceof java.sql.Time) {
0350: return getDateLiteral(((java.sql.Time) date));
0351: } else if (date instanceof Timestamp) {
0352: return getDateLiteral(((Timestamp) date));
0353: } else if (date instanceof ComputedDateValue) {
0354: return date.toString();
0355: } else {
0356: throw new RuntimeException("Unexpected type: "
0357: + date.getClass().getName());
0358: }
0359: }
0360:
0361: protected Date parseDate(String dateAsString)
0362: throws DateParseException {
0363: try {
0364: if (dateAsString.indexOf(" ") > 0) {
0365: return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
0366: .parse(dateAsString);
0367: } else {
0368: if (dateAsString.indexOf(":") > 0) {
0369: return new SimpleDateFormat("HH:mm:ss")
0370: .parse(dateAsString);
0371: } else {
0372: return new SimpleDateFormat("yyyy-MM-dd")
0373: .parse(dateAsString);
0374: }
0375: }
0376: } catch (ParseException e) {
0377: throw new DateParseException(dateAsString);
0378: }
0379: }
0380:
0381: protected boolean isDateOnly(String isoDate) {
0382: return isoDate.length() == "yyyy-MM-dd".length();
0383: }
0384:
0385: protected boolean isDateTime(String isoDate) {
0386: return isoDate.length() >= "yyyy-MM-ddThh:mm:ss".length();
0387: }
0388:
0389: protected boolean isTimeOnly(String isoDate) {
0390: return isoDate.length() == "hh:mm:ss".length();
0391: }
0392:
0393: /**
0394: * Returns the actual database-specific data type to use a "date" (no time information) column.
0395: */
0396: public String getDateType() {
0397: return "DATE";
0398: }
0399:
0400: /**
0401: * Returns the actual database-specific data type to use a "time" column.
0402: */
0403: public String getTimeType() {
0404: return "TIME";
0405: }
0406:
0407: /**
0408: * Returns database-specific line comment string.
0409: */
0410: public String getLineComment() {
0411: return "--";
0412: }
0413:
0414: /**
0415: * Returns database-specific auto-increment DDL clause.
0416: */
0417: public String getAutoIncrementClause() {
0418: return "AUTO_INCREMENT";
0419: }
0420:
0421: public String getConcatSql(String... values) {
0422: StringBuffer returnString = new StringBuffer();
0423: for (String value : values) {
0424: returnString.append(value).append(" || ");
0425: }
0426:
0427: return returnString.toString().replaceFirst(" \\|\\| $", "");
0428: }
0429:
0430: // ------- DATABASECHANGELOG / DATABASECHANGELOGLOCK METHODS ---- //
0431:
0432: public String getDatabaseChangeLogTableName() {
0433: return "DatabaseChangeLog".toUpperCase();
0434: }
0435:
0436: public String getDatabaseChangeLogLockTableName() {
0437: return "DatabaseChangeLogLock".toUpperCase();
0438: }
0439:
0440: private SqlStatement getChangeLogLockInsertSQL() {
0441: return new InsertStatement(getDefaultSchemaName(),
0442: getDatabaseChangeLogLockTableName()).addColumnValue(
0443: "ID", 1).addColumnValue("LOCKED", Boolean.FALSE);
0444: }
0445:
0446: protected SqlStatement getCreateChangeLogLockSQL() {
0447: return new CreateTableStatement(getDefaultSchemaName(),
0448: getDatabaseChangeLogLockTableName())
0449: .addPrimaryKeyColumn("ID", "INT",
0450: new NotNullConstraint()).addColumn("LOCKED",
0451: getBooleanType(), new NotNullConstraint())
0452: .addColumn("LOCKGRANTED", getDateTimeType()).addColumn(
0453: "LOCKEDBY", "VARCHAR(255)");
0454: }
0455:
0456: protected SqlStatement getCreateChangeLogSQL() {
0457: return new CreateTableStatement(getDefaultSchemaName(),
0458: getDatabaseChangeLogTableName()).addPrimaryKeyColumn(
0459: "ID", "VARCHAR(63)", new NotNullConstraint())
0460: .addPrimaryKeyColumn("AUTHOR", "VARCHAR(63)",
0461: new NotNullConstraint()).addPrimaryKeyColumn(
0462: "FILENAME", "VARCHAR(200)",
0463: new NotNullConstraint()).addColumn(
0464: "DATEEXECUTED", getDateTimeType(),
0465: new NotNullConstraint()).addColumn("MD5SUM",
0466: "VARCHAR(32)").addColumn("DESCRIPTION",
0467: "VARCHAR(255)").addColumn("COMMENTS",
0468: "VARCHAR(255)")
0469: .addColumn("TAG", "VARCHAR(255)").addColumn(
0470: "LIQUIBASE", "VARCHAR(10)");
0471: }
0472:
0473: public SqlStatement getSelectChangeLogLockSQL()
0474: throws JDBCException {
0475: return new RawSqlStatement(
0476: ("SELECT LOCKED FROM "
0477: + escapeTableName(getDefaultSchemaName(),
0478: getDatabaseChangeLogLockTableName()) + " WHERE ID=1"));
0479: }
0480:
0481: public boolean doesChangeLogTableExist() {
0482: return changeLogTableExists;
0483: }
0484:
0485: public boolean doesChangeLogLockTableExist() {
0486: return changeLogLockTableExists;
0487: }
0488:
0489: /**
0490: * This method will check the database ChangeLog table used to keep track of
0491: * the changes in the file. If the table does not exist it will create one
0492: * otherwise it will not do anything besides outputting a log message.
0493: */
0494: public void checkDatabaseChangeLogTable() throws JDBCException {
0495: DatabaseConnection connection = getConnection();
0496: ResultSet checkTableRS = null;
0497: ResultSet checkColumnsRS = null;
0498: List<SqlStatement> statementsToExecute = new ArrayList<SqlStatement>();
0499:
0500: try {
0501: checkTableRS = connection
0502: .getMetaData()
0503: .getTables(
0504: convertRequestedSchemaToCatalog(getDefaultSchemaName()),
0505: convertRequestedSchemaToSchema(getDefaultSchemaName()),
0506: getDatabaseChangeLogTableName(),
0507: new String[] { "TABLE" });
0508: if (checkTableRS.next()) {
0509: changeLogTableExists = true;
0510: checkColumnsRS = connection
0511: .getMetaData()
0512: .getColumns(
0513: convertRequestedSchemaToCatalog(getDefaultSchemaName()),
0514: convertRequestedSchemaToSchema(getDefaultSchemaName()),
0515: getDatabaseChangeLogTableName(), null);
0516: boolean hasDescription = false;
0517: boolean hasComments = false;
0518: boolean hasTag = false;
0519: boolean hasLiquibase = false;
0520: while (checkColumnsRS.next()) {
0521: String columnName = checkColumnsRS
0522: .getString("COLUMN_NAME");
0523: if ("DESCRIPTION".equalsIgnoreCase(columnName)) {
0524: hasDescription = true;
0525: } else if ("COMMENTS".equalsIgnoreCase(columnName)) {
0526: hasComments = true;
0527: } else if ("TAG".equalsIgnoreCase(columnName)) {
0528: hasTag = true;
0529: } else if ("LIQUIBASE".equalsIgnoreCase(columnName)) {
0530: hasLiquibase = true;
0531: }
0532: }
0533:
0534: if (!hasDescription) {
0535: statementsToExecute.add(new AddColumnStatement(
0536: getDefaultSchemaName(),
0537: getDatabaseChangeLogTableName(),
0538: "DESCRIPTION", "VARCHAR(255)", null));
0539: }
0540: if (!hasTag) {
0541: statementsToExecute.add(new AddColumnStatement(
0542: getDefaultSchemaName(),
0543: getDatabaseChangeLogTableName(), "TAG",
0544: "VARCHAR(255)", null));
0545: }
0546: if (!hasComments) {
0547: statementsToExecute.add(new AddColumnStatement(
0548: getDefaultSchemaName(),
0549: getDatabaseChangeLogTableName(),
0550: "COMMENTS", "VARCHAR(255)", null));
0551: }
0552: if (!hasLiquibase) {
0553: statementsToExecute.add(new AddColumnStatement(
0554: getDefaultSchemaName(),
0555: getDatabaseChangeLogTableName(),
0556: "LIQUIBASE", "VARCHAR(255)", null));
0557: }
0558:
0559: } else if (!changeLogCreateAttempted) {
0560: changeLogCreateAttempted = true;
0561: getJdbcTemplate().comment(
0562: "Create Database Change Log Table");
0563: SqlStatement createTableStatement = getCreateChangeLogSQL();
0564: if (!canCreateChangeLogTable()) {
0565: throw new JDBCException(
0566: "Cannot create "
0567: + escapeTableName(
0568: getDefaultSchemaName(),
0569: getDatabaseChangeLogTableName())
0570: + " table for your database.\n\n"
0571: + "Please construct it manually using the following SQL as a base and re-run LiquiBase:\n\n"
0572: + createTableStatement);
0573: }
0574: // If there is no table in the database for recording change history create one.
0575: statementsToExecute.add(createTableStatement);
0576: log.info("Creating database history table with name: "
0577: + escapeTableName(getDefaultSchemaName(),
0578: getDatabaseChangeLogTableName()));
0579: changeLogTableExists = true;
0580: // }
0581: }
0582:
0583: for (SqlStatement sql : statementsToExecute) {
0584: this .getJdbcTemplate().execute(sql);
0585: this .commit();
0586: }
0587: } catch (SQLException e) {
0588: throw new JDBCException(e);
0589: } finally {
0590: if (checkTableRS != null) {
0591: try {
0592: checkTableRS.close();
0593: } catch (SQLException e) {
0594: //noinspection ThrowFromFinallyBlock
0595: throw new JDBCException(e);
0596: }
0597: }
0598: if (checkColumnsRS != null) {
0599: try {
0600: checkColumnsRS.close();
0601: } catch (SQLException e) {
0602: //noinspection ThrowFromFinallyBlock
0603: throw new JDBCException(e);
0604: }
0605: }
0606: }
0607: }
0608:
0609: protected boolean canCreateChangeLogTable() throws JDBCException {
0610: return true;
0611: }
0612:
0613: /**
0614: * This method will check the database ChangeLogLock table used to keep track of
0615: * if a machine is updating the database. If the table does not exist it will create one
0616: * otherwise it will not do anything besides outputting a log message.
0617: */
0618: public void checkDatabaseChangeLogLockTable() throws JDBCException {
0619: DatabaseConnection connection = getConnection();
0620: ResultSet rs = null;
0621: boolean knowMustInsertIntoLockTable = false;
0622: try {
0623: rs = connection
0624: .getMetaData()
0625: .getTables(
0626: convertRequestedSchemaToCatalog(getDefaultSchemaName()),
0627: convertRequestedSchemaToSchema(getDefaultSchemaName()),
0628: getDatabaseChangeLogLockTableName(),
0629: new String[] { "TABLE" });
0630: if (!rs.next()) {
0631: if (!changeLogLockCreateAttempted) {
0632: changeLogLockCreateAttempted = true;
0633: SqlStatement createTableStatement = getCreateChangeLogLockSQL();
0634:
0635: getJdbcTemplate().comment(
0636: "Create Database Lock Table");
0637: this .getJdbcTemplate()
0638: .execute(createTableStatement);
0639: this .commit();
0640: log
0641: .info("Created database lock table with name: "
0642: + escapeTableName(
0643: getDefaultSchemaName(),
0644: getDatabaseChangeLogLockTableName()));
0645: changeLogLockTableExists = true;
0646: knowMustInsertIntoLockTable = true;
0647: }
0648: } else {
0649: changeLogLockTableExists = true;
0650: }
0651: rs.close();
0652:
0653: if (changeLogLockTableExists) {
0654: int rows = -1;
0655: if (!knowMustInsertIntoLockTable) {
0656: RawSqlStatement selectStatement = new RawSqlStatement(
0657: "SELECT COUNT(*) FROM "
0658: + escapeTableName(
0659: getDefaultSchemaName(),
0660: getDatabaseChangeLogLockTableName())
0661: + " WHERE ID=1");
0662: rows = this .getJdbcTemplate().queryForInt(
0663: selectStatement);
0664: }
0665: if (knowMustInsertIntoLockTable || rows == 0) {
0666: this .getJdbcTemplate().update(
0667: getChangeLogLockInsertSQL());
0668: this .commit();
0669: log
0670: .info("Inserted lock row into: "
0671: + escapeTableName(
0672: getDefaultSchemaName(),
0673: getDatabaseChangeLogLockTableName()));
0674: rs.close();
0675: }
0676: } else {
0677: throw new JDBCException(
0678: "Change log lock table does not exist");
0679: }
0680:
0681: } catch (SQLException e) {
0682: throw new JDBCException(e);
0683: } finally {
0684: if (rs != null) {
0685: try {
0686: rs.close();
0687: } catch (SQLException e) {
0688: //noinspection ThrowFromFinallyBlock
0689: throw new JDBCException(e);
0690: }
0691: }
0692: }
0693: }
0694:
0695: // ------- DATABASE OBJECT DROPPING METHODS ---- //
0696:
0697: /**
0698: * Drops all objects owned by the connected user.
0699: *
0700: * @param schema
0701: */
0702: public void dropDatabaseObjects(String schema) throws JDBCException {
0703: try {
0704: DatabaseSnapshot snapshot = new DatabaseSnapshot(this ,
0705: new HashSet<DiffStatusListener>(), schema);
0706:
0707: List<Change> dropChanges = new ArrayList<Change>();
0708:
0709: for (ForeignKey fk : snapshot.getForeignKeys()) {
0710: DropForeignKeyConstraintChange dropFK = new DropForeignKeyConstraintChange();
0711: dropFK.setBaseTableSchemaName(schema);
0712: dropFK.setBaseTableName(fk.getForeignKeyTable()
0713: .getName());
0714: dropFK.setConstraintName(fk.getName());
0715:
0716: dropChanges.add(dropFK);
0717: }
0718:
0719: for (View view : snapshot.getViews()) {
0720: DropViewChange dropChange = new DropViewChange();
0721: dropChange.setViewName(view.getName());
0722: dropChange.setSchemaName(schema);
0723:
0724: dropChanges.add(dropChange);
0725: }
0726:
0727: // for (Index index : snapshot.getIndexes()) {
0728: // DropIndexChange dropChange = new DropIndexChange();
0729: // dropChange.setIndexName(index.getName());
0730: // dropChange.setSchemaName(schema);
0731: // dropChange.setTableName(index.getTableName());
0732: //
0733: // dropChanges.add(dropChange);
0734: // }
0735:
0736: for (Table table : snapshot.getTables()) {
0737: DropTableChange dropChange = new DropTableChange();
0738: dropChange.setSchemaName(schema);
0739: dropChange.setTableName(table.getName());
0740:
0741: dropChanges.add(dropChange);
0742: }
0743:
0744: if (this .supportsSequences()) {
0745: for (Sequence seq : snapshot.getSequences()) {
0746: DropSequenceChange dropChange = new DropSequenceChange();
0747: dropChange.setSequenceName(seq.getName());
0748: dropChange.setSchemaName(schema);
0749:
0750: dropChanges.add(dropChange);
0751: }
0752: }
0753:
0754: if (this .changeLogTableExists) {
0755: RawSQLChange clearChangeLogChange = new RawSQLChange();
0756: clearChangeLogChange
0757: .setSql("DELETE FROM "
0758: + escapeTableName(
0759: convertRequestedSchemaToSchema(getDefaultSchemaName()),
0760: getDatabaseChangeLogTableName()));
0761: dropChanges.add(clearChangeLogChange);
0762: }
0763:
0764: try {
0765: for (Change change : dropChanges) {
0766: for (SqlStatement statement : change
0767: .generateStatements(this )) {
0768: this .getJdbcTemplate().execute(statement);
0769: }
0770: }
0771: } catch (UnsupportedChangeException e) {
0772: throw new JDBCException(e);
0773: }
0774:
0775: } finally {
0776: this .commit();
0777: }
0778: }
0779:
0780: public boolean isSystemTable(String catalogName, String schemaName,
0781: String tableName) {
0782: if ("information_schema".equalsIgnoreCase(schemaName)) {
0783: return true;
0784: } else if (tableName
0785: .equalsIgnoreCase(getDatabaseChangeLogLockTableName())) {
0786: return true;
0787: } else if (getSystemTablesAndViews().contains(tableName)) {
0788: return true;
0789: }
0790: return false;
0791: }
0792:
0793: public boolean isSystemView(String catalogName, String schemaName,
0794: String viewName) {
0795: if ("information_schema".equalsIgnoreCase(schemaName)) {
0796: return true;
0797: } else if (getSystemTablesAndViews().contains(viewName)) {
0798: return true;
0799: }
0800: return false;
0801: }
0802:
0803: public boolean isLiquibaseTable(String tableName) {
0804: return tableName.equalsIgnoreCase(this
0805: .getDatabaseChangeLogTableName())
0806: || tableName.equalsIgnoreCase(this
0807: .getDatabaseChangeLogLockTableName());
0808: }
0809:
0810: // ------- DATABASE TAGGING METHODS ---- //
0811:
0812: /**
0813: * Tags the database changelog with the given string.
0814: */
0815: public void tag(String tagString) throws JDBCException {
0816: try {
0817: int totalRows = this .getJdbcTemplate().queryForInt(
0818: new RawSqlStatement("SELECT COUNT(*) FROM "
0819: + escapeTableName(getDefaultSchemaName(),
0820: getDatabaseChangeLogTableName())));
0821: if (totalRows == 0) {
0822: throw new JDBCException("Cannot tag an empty database");
0823: }
0824:
0825: Timestamp lastExecutedDate = (Timestamp) this
0826: .getJdbcTemplate().queryForObject(
0827: createChangeToTagSQL(), Timestamp.class);
0828: int rowsUpdated = this .getJdbcTemplate().update(
0829: createTagSQL(tagString, lastExecutedDate));
0830: if (rowsUpdated == 0) {
0831: throw new JDBCException(
0832: "Did not tag database change log correctly. Should have tagged changeset from "
0833: + lastExecutedDate.toString());
0834: }
0835: this .commit();
0836: } catch (Exception e) {
0837: throw new JDBCException(e);
0838: }
0839: }
0840:
0841: /**
0842: * Returns SQL to return the date of the most recient changeset execution.
0843: */
0844: protected SqlStatement createChangeToTagSQL() {
0845: return new RawSqlStatement("SELECT MAX(DATEEXECUTED) FROM "
0846: + escapeTableName(getDefaultSchemaName(),
0847: getDatabaseChangeLogTableName()));
0848: }
0849:
0850: /**
0851: * Returns SQL to tag the database. SQL Contains two ?:
0852: * <ol>
0853: * <li>tag string</li>
0854: * <li>date executed</li>
0855: * </ol>
0856: */
0857: protected SqlStatement createTagSQL(String tagName,
0858: Date dateExecuted) {
0859: UpdateStatement statement = new UpdateStatement(
0860: getDefaultSchemaName(), getDatabaseChangeLogTableName());
0861: statement.addNewColumnValue("TAG", tagName);
0862: statement.setWhereClause("DATEEXECUTED = ?");
0863: statement.addWhereParameter(dateExecuted);
0864:
0865: return statement;
0866: }
0867:
0868: public SqlStatement createFindSequencesSQL(String schema)
0869: throws JDBCException {
0870: return null;
0871: }
0872:
0873: public boolean doesTagExist(String tag) throws JDBCException {
0874: int count = this .getJdbcTemplate().queryForInt(
0875: new RawSqlStatement("SELECT COUNT(*) FROM "
0876: + escapeTableName(getDefaultSchemaName(),
0877: getDatabaseChangeLogTableName())
0878: + " WHERE TAG='" + tag + "'"));
0879: return count > 0;
0880: }
0881:
0882: public DatabaseSnapshot getSnapshot() throws JDBCException {
0883: return new DatabaseSnapshot(this );
0884: }
0885:
0886: public String toString() {
0887: if (getConnection() == null) {
0888: return getProductName() + " Database";
0889: }
0890: try {
0891: return getConnectionUsername()
0892: + " @ "
0893: + getConnectionURL()
0894: + (getDefaultSchemaName() == null ? ""
0895: : " (Default Schema: "
0896: + getDefaultSchemaName() + ")");
0897: } catch (JDBCException e) {
0898: return super .toString();
0899: }
0900: }
0901:
0902: public boolean shouldQuoteValue(String value) {
0903: return true;
0904: }
0905:
0906: public String getViewDefinition(String schemaName, String viewName)
0907: throws JDBCException {
0908: if (schemaName == null) {
0909: schemaName = convertRequestedSchemaToSchema(schemaName);
0910: }
0911: String definition = (String) this .getJdbcTemplate()
0912: .queryForObject(
0913: getViewDefinitionSql(schemaName, viewName),
0914: String.class);
0915: if (definition == null) {
0916: return null;
0917: }
0918: return definition.replaceFirst("^CREATE VIEW [\\S]+ AS", "");
0919: }
0920:
0921: public SqlStatement getViewDefinitionSql(String schemaName,
0922: String viewName) throws JDBCException {
0923: String sql = "select view_definition from information_schema.views where upper(table_name)='"
0924: + viewName.toUpperCase() + "'";
0925: if (convertRequestedSchemaToCatalog(schemaName) != null) {
0926: sql += " and table_schema='"
0927: + convertRequestedSchemaToSchema(schemaName) + "'";
0928: } else if (convertRequestedSchemaToCatalog(schemaName) != null) {
0929: sql += " and table_catalog='"
0930: + convertRequestedSchemaToCatalog(schemaName) + "'";
0931: }
0932:
0933: log.info("GetViewDefinitionSQL: " + sql);
0934: return new RawSqlStatement(sql);
0935: }
0936:
0937: public int getDatabaseType(int type) {
0938: int returnType = type;
0939: if (returnType == Types.BOOLEAN) {
0940: String booleanType = getBooleanType();
0941: if (!booleanType.equalsIgnoreCase("boolean")) {
0942: returnType = Types.TINYINT;
0943: }
0944: }
0945:
0946: return returnType;
0947: }
0948:
0949: public Object convertDatabaseValueToJavaObject(Object defaultValue,
0950: int dataType, int columnSize, int decimalDigits)
0951: throws ParseException {
0952: if (defaultValue == null) {
0953: return null;
0954: } else if (defaultValue instanceof String) {
0955: return convertToCorrectJavaType(((String) defaultValue)
0956: .replaceFirst("^'", "").replaceFirst("'$", ""),
0957: dataType, columnSize, decimalDigits);
0958: } else {
0959: return defaultValue;
0960: }
0961: }
0962:
0963: protected Object convertToCorrectJavaType(String value,
0964: int dataType, int columnSize, int decimalDigits)
0965: throws ParseException {
0966: if (value == null) {
0967: return null;
0968: }
0969: if (dataType == Types.CLOB || dataType == Types.VARCHAR
0970: || dataType == Types.CHAR
0971: || dataType == Types.LONGVARCHAR) {
0972: if (value.equalsIgnoreCase("NULL")) {
0973: return null;
0974: } else {
0975: return value;
0976: }
0977: }
0978:
0979: value = StringUtils.trimToNull(value);
0980: if (value == null) {
0981: return null;
0982: }
0983:
0984: try {
0985: if (dataType == Types.DATE) {
0986: return new java.sql.Date(parseDate(value).getTime());
0987: } else if (dataType == Types.TIMESTAMP) {
0988: return new Timestamp(parseDate(value).getTime());
0989: } else if (dataType == Types.TIME) {
0990: return new Time(parseDate(value).getTime());
0991: } else if (dataType == Types.BIGINT) {
0992: return new BigInteger(value);
0993: } else if (dataType == Types.BIT) {
0994: if (value.equalsIgnoreCase("true")) {
0995: return Boolean.TRUE;
0996: } else if (value.equalsIgnoreCase("false")) {
0997: return Boolean.FALSE;
0998: } else if (value.equals("1")) {
0999: return Boolean.TRUE;
1000: } else if (value.equals("0")) {
1001: return Boolean.FALSE;
1002: } else if (value.equals("(1)")) {
1003: return Boolean.TRUE;
1004: } else if (value.equals("(0)")) {
1005: return Boolean.FALSE;
1006: }
1007: throw new ParseException("Unknown bit value: " + value,
1008: 0);
1009: } else if (dataType == Types.BOOLEAN) {
1010: return Boolean.valueOf(value);
1011: } else if (dataType == Types.DECIMAL) {
1012: if (decimalDigits == 0) {
1013: return new Integer(value);
1014: }
1015: return new Double(value);
1016: } else if (dataType == Types.DOUBLE
1017: || dataType == Types.NUMERIC) {
1018: return new Double(value);
1019: } else if (dataType == Types.FLOAT) {
1020: return new Float(value);
1021: } else if (dataType == Types.INTEGER) {
1022: return new Integer(value);
1023: } else if (dataType == Types.NULL) {
1024: return null;
1025: } else if (dataType == Types.REAL) {
1026: return new Float(value);
1027: } else if (dataType == Types.SMALLINT) {
1028: return new Integer(value);
1029: } else if (dataType == Types.TINYINT) {
1030: return new Integer(value);
1031: } else {
1032: throw new RuntimeException("Cannot convert type: "
1033: + dataType);
1034: }
1035: } catch (DateParseException e) {
1036: return new ComputedDateValue(value);
1037: } catch (NumberFormatException e) {
1038: return new ComputedNumericValue(value);
1039: }
1040: }
1041:
1042: public String convertJavaObjectToString(Object value) {
1043: if (value != null) {
1044: if (value instanceof String) {
1045: if ("null".equalsIgnoreCase(((String) value))) {
1046: return null;
1047: }
1048: return "'" + ((String) value).replaceAll("'", "''")
1049: + "'";
1050: } else if (value instanceof Number) {
1051: return value.toString();
1052: } else if (value instanceof Boolean) {
1053: String returnValue;
1054: if (((Boolean) value)) {
1055: returnValue = this .getTrueBooleanValue();
1056: } else {
1057: returnValue = this .getFalseBooleanValue();
1058: }
1059: if (returnValue.matches("\\d+")) {
1060: return returnValue;
1061: } else {
1062: return "'" + returnValue + "'";
1063: }
1064: } else if (value instanceof java.sql.Date) {
1065: return this .getDateLiteral(((java.sql.Date) value));
1066: } else if (value instanceof java.sql.Time) {
1067: return this .getDateLiteral(((java.sql.Time) value));
1068: } else if (value instanceof java.sql.Timestamp) {
1069: return this
1070: .getDateLiteral(((java.sql.Timestamp) value));
1071: } else {
1072: throw new RuntimeException(
1073: "Unknown default value type: "
1074: + value.getClass().getName());
1075: }
1076: } else {
1077: return null;
1078: }
1079: }
1080:
1081: public String escapeTableName(String schemaName, String tableName) {
1082: if (StringUtils.trimToNull(schemaName) == null
1083: || !supportsSchemas()) {
1084: return tableName;
1085: } else {
1086: return schemaName + "." + tableName;
1087: }
1088: }
1089:
1090: public String escapeSequenceName(String schemaName,
1091: String sequenceName) {
1092: if (StringUtils.trimToNull(schemaName) == null
1093: || !supportsSchemas()) {
1094: return sequenceName;
1095: } else {
1096: return schemaName + "." + sequenceName;
1097: }
1098: }
1099:
1100: public String escapeColumnName(String columnName) {
1101: return columnName;
1102: }
1103:
1104: public String escapeColumnNameList(String columnNames) {
1105: return columnNames;
1106: }
1107:
1108: public String convertRequestedSchemaToCatalog(String requestedSchema)
1109: throws JDBCException {
1110: if (getDefaultCatalogName() == null) {
1111: return null;
1112: } else {
1113: if (requestedSchema == null) {
1114: return getDefaultCatalogName();
1115: }
1116: return StringUtils.trimToNull(requestedSchema);
1117: }
1118: }
1119:
1120: public String convertRequestedSchemaToSchema(String requestedSchema)
1121: throws JDBCException {
1122: String returnSchema = requestedSchema;
1123: if (returnSchema == null) {
1124: returnSchema = getDefaultDatabaseSchemaName();
1125: }
1126:
1127: if (returnSchema != null) {
1128: returnSchema = returnSchema.toUpperCase();
1129: }
1130: return returnSchema;
1131: }
1132:
1133: public boolean supportsSchemas() {
1134: return true;
1135: }
1136:
1137: public String generatePrimaryKeyName(String tableName) {
1138: return "PK_" + tableName.toUpperCase();
1139: }
1140:
1141: public String escapeViewName(String schemaName, String viewName) {
1142: return escapeTableName(schemaName, viewName);
1143: }
1144:
1145: public boolean isColumnAutoIncrement(String schemaName,
1146: String tableName, String columnName) throws SQLException,
1147: JDBCException {
1148: if (!supportsAutoIncrement()) {
1149: return false;
1150: }
1151:
1152: boolean autoIncrement = false;
1153:
1154: ResultSet selectRS = null;
1155: try {
1156: selectRS = getConnection().createStatement().executeQuery(
1157: "SELECT " + escapeColumnName(columnName) + " FROM "
1158: + escapeTableName(schemaName, tableName)
1159: + " WHERE 1 = 0");
1160: ResultSetMetaData meta = selectRS.getMetaData();
1161: autoIncrement = meta.isAutoIncrement(1);
1162: } finally {
1163: if (selectRS != null) {
1164: selectRS.close();
1165: }
1166: }
1167:
1168: return autoIncrement;
1169: }
1170:
1171: /**
1172: * Returns the run status for the given ChangeSet
1173: */
1174: public ChangeSet.RunStatus getRunStatus(ChangeSet changeSet)
1175: throws JDBCException, DatabaseHistoryException {
1176: if (!doesChangeLogTableExist()) {
1177: return ChangeSet.RunStatus.NOT_RAN;
1178: }
1179:
1180: RanChangeSet foundRan = getRanChangeSet(changeSet);
1181:
1182: if (foundRan == null) {
1183: return ChangeSet.RunStatus.NOT_RAN;
1184: } else {
1185: if (foundRan.getMd5sum() == null) {
1186: try {
1187: log.info("Updating NULL md5sum for "
1188: + changeSet.toString());
1189: DatabaseConnection connection = getConnection();
1190: PreparedStatement updatePstmt = connection
1191: .prepareStatement("UPDATE "
1192: + escapeTableName(
1193: getDefaultSchemaName(),
1194: getDatabaseChangeLogTableName())
1195: + " SET MD5SUM=? WHERE ID=? AND AUTHOR=? AND FILENAME=?");
1196: updatePstmt.setString(1, changeSet.getMd5sum());
1197: updatePstmt.setString(2, changeSet.getId());
1198: updatePstmt.setString(3, changeSet.getAuthor());
1199: updatePstmt.setString(4, changeSet.getFilePath());
1200:
1201: updatePstmt.executeUpdate();
1202: updatePstmt.close();
1203: this .commit();
1204: } catch (SQLException e) {
1205: throw new JDBCException(e);
1206: }
1207:
1208: return ChangeSet.RunStatus.ALREADY_RAN;
1209: } else {
1210: if (foundRan.getMd5sum().equals(changeSet.getMd5sum())) {
1211: return ChangeSet.RunStatus.ALREADY_RAN;
1212: } else {
1213: if (changeSet.shouldRunOnChange()) {
1214: return ChangeSet.RunStatus.RUN_AGAIN;
1215: } else {
1216: return ChangeSet.RunStatus.INVALID_MD5SUM;
1217: // throw new DatabaseHistoryException("MD5 Check for " + changeSet.toString() + " failed");
1218: }
1219: }
1220: }
1221: }
1222: }
1223:
1224: public RanChangeSet getRanChangeSet(ChangeSet changeSet)
1225: throws JDBCException, DatabaseHistoryException {
1226: if (!doesChangeLogTableExist()) {
1227: throw new DatabaseHistoryException(
1228: "Database change table does not exist");
1229: }
1230:
1231: RanChangeSet foundRan = null;
1232: for (RanChangeSet ranChange : getRanChangeSetList()) {
1233: if (ranChange.isSameAs(changeSet)) {
1234: foundRan = ranChange;
1235: break;
1236: }
1237: }
1238: return foundRan;
1239: }
1240:
1241: /**
1242: * Returns the ChangeSets that have been run against the current database.
1243: */
1244: public List<RanChangeSet> getRanChangeSetList()
1245: throws JDBCException {
1246: try {
1247: String databaseChangeLogTableName = escapeTableName(
1248: getDefaultSchemaName(),
1249: getDatabaseChangeLogTableName());
1250: List<RanChangeSet> ranChangeSetList = new ArrayList<RanChangeSet>();
1251: if (doesChangeLogTableExist()) {
1252: log.info("Reading from " + databaseChangeLogTableName);
1253: String sql = "SELECT * FROM "
1254: + databaseChangeLogTableName
1255: + " ORDER BY DATEEXECUTED ASC".toUpperCase();
1256: Statement statement = getConnection().createStatement();
1257: ResultSet rs = statement.executeQuery(sql);
1258: while (rs.next()) {
1259: String fileName = rs.getString("FILENAME");
1260: String author = rs.getString("AUTHOR");
1261: String id = rs.getString("ID");
1262: String md5sum = rs.getString("MD5SUM");
1263: Date dateExecuted = rs.getTimestamp("DATEEXECUTED");
1264: String tag = rs.getString("TAG");
1265: RanChangeSet ranChangeSet = new RanChangeSet(
1266: fileName, id, author, md5sum, dateExecuted,
1267: tag);
1268: ranChangeSetList.add(ranChangeSet);
1269: }
1270: rs.close();
1271: statement.close();
1272: }
1273: return ranChangeSetList;
1274: } catch (SQLException e) {
1275: if (!getJdbcTemplate().executesStatements()) {
1276: //probably not created, no problem
1277: return new ArrayList<RanChangeSet>();
1278: } else {
1279: throw new JDBCException(e);
1280: }
1281: }
1282: }
1283:
1284: public Date getRanDate(ChangeSet changeSet) throws JDBCException,
1285: DatabaseHistoryException {
1286: RanChangeSet ranChange = getRanChangeSet(changeSet);
1287: if (ranChange == null) {
1288: return null;
1289: } else {
1290: return ranChange.getDateExecuted();
1291: }
1292: }
1293:
1294: /**
1295: * After the change set has been ran against the database this method will update the change log table
1296: * with the information.
1297: */
1298: public void markChangeSetAsRan(ChangeSet changeSet)
1299: throws JDBCException {
1300: String dateValue = getCurrentDateTimeFunction();
1301:
1302: InsertStatement statement = new InsertStatement(
1303: getDefaultSchemaName(), getDatabaseChangeLogTableName());
1304: statement.addColumnValue("ID",
1305: escapeStringForDatabase(changeSet.getId()));
1306: statement.addColumnValue("AUTHOR", changeSet.getAuthor());
1307: statement.addColumnValue("FILENAME", changeSet.getFilePath());
1308: statement.addColumnValue("DATEEXECUTED", new ComputedDateValue(
1309: dateValue));
1310: statement.addColumnValue("MD5SUM", changeSet.getMd5sum());
1311: statement.addColumnValue("DESCRIPTION", limitSize(changeSet
1312: .getDescription()));
1313: statement.addColumnValue("COMMENTS", limitSize(StringUtils
1314: .trimToEmpty(changeSet.getComments())));
1315: statement.addColumnValue("LIQUIBASE", LiquibaseUtil
1316: .getBuildVersion());
1317:
1318: this .getJdbcTemplate().execute(statement);
1319:
1320: getRanChangeSetList().add(new RanChangeSet(changeSet));
1321: }
1322:
1323: public void markChangeSetAsReRan(ChangeSet changeSet)
1324: throws JDBCException {
1325: String dateValue = getCurrentDateTimeFunction();
1326: String sql = "UPDATE DATABASECHANGELOG SET DATEEXECUTED="
1327: + dateValue
1328: + ", MD5SUM='?' WHERE ID='?' AND AUTHOR='?' AND FILENAME='?'";
1329: sql = sql.replaceFirst("\\?", escapeStringForDatabase(changeSet
1330: .getMd5sum()));
1331: sql = sql.replaceFirst("\\?", escapeStringForDatabase(changeSet
1332: .getId()));
1333: sql = sql.replaceFirst("\\?", escapeStringForDatabase(changeSet
1334: .getAuthor()));
1335: sql = sql.replaceFirst("\\?", escapeStringForDatabase(changeSet
1336: .getFilePath()));
1337:
1338: this .getJdbcTemplate().execute(new RawSqlStatement(sql));
1339: this .commit();
1340: }
1341:
1342: public void removeRanStatus(ChangeSet changeSet)
1343: throws JDBCException {
1344: String sql = "DELETE FROM "
1345: + escapeTableName(getDefaultSchemaName(),
1346: getDatabaseChangeLogTableName())
1347: + " WHERE ID='?' AND AUTHOR='?' AND FILENAME='?'";
1348: sql = sql.replaceFirst("\\?", escapeStringForDatabase(changeSet
1349: .getId()));
1350: sql = sql.replaceFirst("\\?", escapeStringForDatabase(changeSet
1351: .getAuthor()));
1352: sql = sql.replaceFirst("\\?", escapeStringForDatabase(changeSet
1353: .getFilePath()));
1354:
1355: this .getJdbcTemplate().execute(new RawSqlStatement(sql));
1356: commit();
1357: }
1358:
1359: public String escapeStringForDatabase(String string) {
1360: return string.replaceAll("'", "''");
1361: }
1362:
1363: private String limitSize(String string) {
1364: int maxLength = 255;
1365: if (string.length() > maxLength) {
1366: return string.substring(0, maxLength - 3) + "...";
1367: }
1368: return string;
1369: }
1370:
1371: public void commit() throws JDBCException {
1372: try {
1373: getConnection().commit();
1374: } catch (SQLException e) {
1375: throw new JDBCException(e);
1376: }
1377: }
1378:
1379: public void rollback() throws JDBCException {
1380: try {
1381: getConnection().rollback();
1382: } catch (SQLException e) {
1383: throw new JDBCException(e);
1384: }
1385: }
1386:
1387: public JdbcTemplate getJdbcTemplate() {
1388: return jdbcTemplate;
1389: }
1390:
1391: public void setJdbcTemplate(JdbcTemplate template) {
1392: this .jdbcTemplate = template;
1393: }
1394:
1395: public boolean equals(Object o) {
1396: if (this == o)
1397: return true;
1398: if (o == null || getClass() != o.getClass())
1399: return false;
1400:
1401: AbstractDatabase that = (AbstractDatabase) o;
1402:
1403: return !(connection != null ? !connection
1404: .equals(that.connection) : that.connection != null);
1405:
1406: }
1407:
1408: public int hashCode() {
1409: return (connection != null ? connection.hashCode() : 0);
1410: }
1411:
1412: public void close() throws JDBCException {
1413: try {
1414: DatabaseConnection connection = getConnection();
1415: if (connection != null) {
1416: connection.close();
1417: }
1418: } catch (SQLException e) {
1419: throw new JDBCException(e);
1420: }
1421: }
1422: }
|