/* * JBoss, Home of Professional Open Source. * Copyright 2008, Red Hat Middleware LLC, and individual contributors * as indicated by the @author tags. See the copyright.txt file in the * distribution for a full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.jboss.ejb.plugins.cmp.jdbc; import java.sql.Connection; import java.sql.DatabaseMetaData; import javax.sql.DataSource; import java.sql.SQLException; import java.sql.Statement; import java.sql.ResultSet; import java.util.zip.CRC32; import java.util.ArrayList; import org.jboss.deployment.DeploymentException; import org.jboss.ejb.plugins.cmp.jdbc.bridge.JDBCEntityBridge; import org.jboss.ejb.plugins.cmp.jdbc.bridge.JDBCFieldBridge; import org.jboss.ejb.plugins.cmp.jdbc.bridge.JDBCAbstractEntityBridge; import org.jboss.ejb.plugins.cmp.jdbc.bridge.JDBCAbstractCMRFieldBridge; import org.jboss.logging.Logger; import java.util.Vector; /** * SQLUtil helps with building sql statements. * * @author <a href="mailto:dain@daingroup.com">Dain Sundstrom</a> * @author <a href="mailto:alex@jboss.org">Alex Loubyansky</a> * @author <a href="joachim@cabsoft.be">Joachim Van der Auwera</a> * @version $Revision: 81030 $ */ public final class SQLUtil { public static final String EMPTY_STRING = ""; public static final String INSERT_INTO = "INSERT INTO "; public static final String VALUES = " VALUES "; public static final String SELECT = "SELECT "; public static final String DISTINCT = "DISTINCT "; public static final String FROM = " FROM "; public static final String WHERE = " WHERE "; public static final String ORDERBY = " ORDER BY "; public static final String DELETE_FROM = "DELETE FROM "; public static final String AND = " AND "; public static final String OR = " OR "; public static final String NOT = " NOT "; public static final String EXISTS = "EXISTS "; public static final String COMMA = ", "; public static final String LEFT_JOIN = " LEFT JOIN "; public static final String LEFT_OUTER_JOIN = " LEFT OUTER JOIN "; public static final String ON = " ON "; public static final String NOT_EQUAL = "<>"; public static final String CREATE_TABLE = "CREATE TABLE "; public static final String DROP_TABLE = "DROP TABLE "; public static final String CREATE_INDEX = "CREATE INDEX "; public static final String NULL = "NULL"; public static final String IS = " IS "; public static final String IN = " IN "; public static final String EMPTY = "EMPTY"; public static final String BETWEEN = " BETWEEN "; public static final String LIKE = " LIKE "; public static final String MEMBER_OF = " MEMBER OF "; public static final String CONCAT = "CONCAT"; public static final String SUBSTRING = "SUBSTRING"; public static final String LCASE = "LCASE"; public static final String UCASE = "UCASE"; public static final String LENGTH = "LENGTH"; public static final String LOCATE = "LOCATE"; public static final String ABS = "ABS"; public static final String MOD = "MOD"; public static final String SQRT = "SQRT"; public static final String COUNT = "COUNT"; public static final String MAX = "MAX"; public static final String MIN = "MIN"; public static final String AVG = "AVG"; public static final String SUM = "SUM"; public static final String ASC = " ASC"; public static final String DESC = " DESC"; public static final String OFFSET = " OFFSET "; public static final String LIMIT = " LIMIT "; public static final String UPDATE = "UPDATE "; public static final String SET = " SET "; public static final String TYPE = " TYPE "; private static final String DOT = "."; private static final String EQ_QUESTMARK = "=?"; private static final Vector rwords = new Vector(); public static String getTableNameWithoutSchema(String tableName) { final int dot = tableName.indexOf('.'); if(dot != -1) { char firstChar = tableName.charAt(0); tableName = tableName.substring(dot + 1); if(firstChar == '"' || firstChar == '\'') { tableName = firstChar + tableName; } } return tableName; } public static String getSchema(String tableName) { String schema = null; final int dot = tableName.indexOf('.'); if(dot != -1) { char firstChar = tableName.charAt(0); final boolean quoted = firstChar == '"' || firstChar == '\''; schema = tableName.substring(quoted ? 1 : 0, dot); } return schema; } public static String fixTableName(String tableName, DataSource dataSource) throws DeploymentException { // don't fix the quited table name char firstChar = tableName.charAt(0); if(firstChar == '"' || firstChar == '\'') { return tableName; } // Separate schema name and table name String strSchema = ""; int iIndex; if((iIndex = tableName.indexOf('.')) != -1) { strSchema = tableName.substring(0, iIndex); tableName = tableName.substring(iIndex + 1); } // check for SQL reserved word and escape it with prepending a "X" // IMHO one should reject reserved words and throw a // DeploymentException - pilhuhn if(rwords != null) { for(int i = 0; i < rwords.size(); i++) { if(((String)rwords.elementAt(i)).equalsIgnoreCase(tableName)) { tableName = "X" + tableName; break; } } } Connection con = null; try { con = dataSource.getConnection(); DatabaseMetaData dmd = con.getMetaData(); // fix length int maxLength = dmd.getMaxTableNameLength(); if(maxLength > 0 && tableName.length() > maxLength) { CRC32 crc = new CRC32(); crc.update(tableName.getBytes()); String nameCRC = Long.toString(crc.getValue(), 36); tableName = tableName.substring( 0, maxLength - nameCRC.length() - 2); tableName += "_" + nameCRC; } // fix case if(dmd.storesLowerCaseIdentifiers()) { tableName = tableName.toLowerCase(); } else if(dmd.storesUpperCaseIdentifiers()) { tableName = tableName.toUpperCase(); } // now put the schema name back on the table name if(strSchema.length() > 0) { tableName = strSchema + "." + tableName; } return tableName; } catch(SQLException e) { // This should not happen. A J2EE compatiable JDBC driver is // required fully support metadata. throw new DeploymentException("Error while fixing table name", e); } finally { JDBCUtil.safeClose(con); } } public static void addToRwords(String word) { if(!rwords.contains(word)) rwords.add(word); } public static String fixConstraintName(String name, DataSource dataSource) throws DeploymentException { return fixTableName(name, dataSource).replace('.', '_'); } // ======================================================================= // Create Table Columns Clause // columnName0 sqlType0 // [, columnName1 sqlType0 // [, columnName2 sqlType0 [...]]] // ======================================================================= public static String getCreateTableColumnsClause(JDBCFieldBridge[] fields) { StringBuffer buf = new StringBuffer(100); boolean comma = false; for(int i = 0; i < fields.length; ++i) { JDBCType type = getJDBCType(fields[i]); if(type != null) { if(comma) buf.append(COMMA); else comma = true; buf.append(getCreateTableColumnsClause(type)); } } return buf.toString(); } /** * Returns columnName0 sqlType0 * [, columnName1 sqlType0 * [, columnName2 sqlType0 [...]]] */ public static String getCreateTableColumnsClause(JDBCType type) { String[] columnNames = type.getColumnNames(); String[] sqlTypes = type.getSQLTypes(); boolean[] notNull = type.getNotNull(); StringBuffer buf = new StringBuffer(); for(int i = 0; i < columnNames.length; i++) { if(i != 0) buf.append(COMMA); buf.append(columnNames[i]).append(' ').append(sqlTypes[i]); if(notNull[i]) buf.append(NOT).append(NULL); } return buf.toString(); } // ======================================================================= // Column Names Clause // columnName0 [, columnName1 [AND columnName2 [...]]] // ======================================================================= /** * Returns columnName0 [, columnName1 [AND columnName2 [...]]] */ public static StringBuffer getColumnNamesClause(JDBCFieldBridge[] fields, StringBuffer sb) { return getColumnNamesClause(fields, "", sb); } /** * Returns columnName0 [, columnName1 [AND columnName2 [...]]] */ public static StringBuffer getColumnNamesClause(JDBCFieldBridge[] fields, String identifier, StringBuffer buf) { boolean comma = false; for(int i = 0; i < fields.length; ++i) { JDBCType type = getJDBCType(fields[i]); if(type != null) { if(comma) buf.append(COMMA); else comma = true; getColumnNamesClause(type, identifier, buf); } } return buf; } /** * Returns columnName0 [, columnName1 [AND columnName2 [...]]] */ public static StringBuffer getSearchableColumnNamesClause(JDBCFieldBridge[] fields, String identifier, StringBuffer buf) { boolean comma = false; for(int i = 0; i < fields.length; ++i) { JDBCType type = getJDBCType(fields[i]); if(type != null && type.isSearchable()) { if(comma) buf.append(COMMA); else comma = true; getColumnNamesClause(type, identifier, buf); } } return buf; } /** * Returns columnName0 [, columnName1 [AND columnName2 [...]]] */ public static StringBuffer getColumnNamesClause(JDBCEntityBridge.FieldIterator loadIter, StringBuffer sb) { if(loadIter.hasNext()) getColumnNamesClause(loadIter.next(), sb); while(loadIter.hasNext()) { sb.append(COMMA); getColumnNamesClause(loadIter.next(), sb); } return sb; } /** * Returns columnName0 [, columnName1 [, columnName2 [...]]] */ public static StringBuffer getColumnNamesClause(JDBCFieldBridge field, StringBuffer sb) { return getColumnNamesClause(field.getJDBCType(), sb); } /** * Returns identifier.columnName0 * [, identifier.columnName1 * [, identifier.columnName2 [...]]] */ public static StringBuffer getColumnNamesClause(JDBCFieldBridge field, String identifier, StringBuffer sb) { return getColumnNamesClause(field.getJDBCType(), identifier, sb); } /** * Returns identifier.columnName0 * [, identifier.columnName1 * [, identifier.columnName2 [...]]] */ private static StringBuffer getColumnNamesClause(JDBCType type, String identifier, StringBuffer buf) { String[] columnNames = type.getColumnNames(); boolean hasIdentifier = identifier.length() > 0; if(hasIdentifier) buf.append(identifier).append(DOT); buf.append(columnNames[0]); int i = 1; while(i < columnNames.length) { buf.append(COMMA); if(hasIdentifier) buf.append(identifier).append(DOT); buf.append(columnNames[i++]); } return buf; } /** * Returns ', columnName0 [, columnName1 [AND columnName2 [...]]]' */ public static StringBuffer appendColumnNamesClause(JDBCAbstractEntityBridge entity, String eagerLoadGroup, StringBuffer sb) { return appendColumnNamesClause(entity, eagerLoadGroup, "", sb); } /** * Returns ', columnName0 [, columnName1 [AND columnName2 [...]]]' */ public static StringBuffer appendColumnNamesClause(JDBCAbstractEntityBridge entity, String eagerLoadGroup, String alias, StringBuffer sb) { return appendColumnNamesClause(entity.getTableFields(), entity.getLoadGroupMask(eagerLoadGroup), alias, sb); } /** * Returns ', columnName0 [, columnName1 [AND columnName2 [...]]]' */ public static StringBuffer appendColumnNamesClause(JDBCFieldBridge[] fields, boolean[] mask, String identifier, StringBuffer buf) { for(int i = 0; i < fields.length; ++i) { if(mask[i]) { JDBCType type = getJDBCType(fields[i]); if(type != null) { buf.append(COMMA); getColumnNamesClause(type, identifier, buf); } } } return buf; } public static StringBuffer appendSearchableColumnNamesClause(JDBCFieldBridge[] fields, boolean[] mask, String identifier, StringBuffer buf) { for(int i = 0; i < fields.length; ++i) { if(mask[i]) { JDBCType type = getJDBCType(fields[i]); if(type != null && type.isSearchable()) { buf.append(COMMA); getColumnNamesClause(type, identifier, buf); } } } return buf; } /** * Returns ', columnName0 [, columnName1 [AND columnName2 [...]]]' */ public static StringBuffer appendColumnNamesClause(JDBCFieldBridge[] fields, String identifier, StringBuffer buf) { for(int i = 0; i < fields.length; ++i) { JDBCType type = getJDBCType(fields[i]); if(type != null) { buf.append(COMMA); getColumnNamesClause(type, identifier, buf); } } return buf; } /** * Returns identifier.columnName0 * [, identifier.columnName1 * [, identifier.columnName2 [...]]] */ private static StringBuffer getColumnNamesClause(JDBCType type, StringBuffer buf) { String[] columnNames = type.getColumnNames(); buf.append(columnNames[0]); int i = 1; while(i < columnNames.length) { buf.append(COMMA).append(columnNames[i++]); } return buf; } // ======================================================================= // Set Clause // columnName0=? [, columnName1=? [, columnName2=? [...]]] // ======================================================================= /** * Returns columnName0=? [, columnName1=? [, columnName2=? [...]]] */ public static StringBuffer getSetClause(JDBCEntityBridge.FieldIterator fieldsIter, StringBuffer buf) { JDBCType type = getJDBCType(fieldsIter.next()); getSetClause(type, buf); while(fieldsIter.hasNext()) { type = getJDBCType(fieldsIter.next()); buf.append(COMMA); getSetClause(type, buf); } return buf; } /** * Returns columnName0=? [, columnName1=? [, columnName2=? [...]]] */ private static StringBuffer getSetClause(JDBCType type, StringBuffer buf) { String[] columnNames = type.getColumnNames(); buf.append(columnNames[0]).append(EQ_QUESTMARK); int i = 1; while(i < columnNames.length) { buf.append(COMMA).append(columnNames[i++]).append(EQ_QUESTMARK); } return buf; } // ======================================================================= // Values Clause // ? [, ? [, ? [...]]] // ======================================================================= /** * Returns ? [, ? [, ? [...]]] */ public static StringBuffer getValuesClause(JDBCFieldBridge[] fields, StringBuffer buf) { boolean comma = false; for(int i = 0; i < fields.length; ++i) { JDBCType type = getJDBCType(fields[i]); if(type != null) { if(comma) buf.append(COMMA); else comma = true; getValuesClause(type, buf); } } return buf; } /** * Returns ? [, ? [, ? [...]]] */ private static StringBuffer getValuesClause(JDBCType type, StringBuffer buf) { int columnCount = type.getColumnNames().length; buf.append('?'); int i = 1; while(i++ < columnCount) buf.append(COMMA).append('?'); return buf; } // ======================================================================= // Where Clause // columnName0=? [AND columnName1=? [AND columnName2=? [...]]] // ======================================================================= /** * Returns columnName0=? [AND columnName1=? [AND columnName2=? [...]]] */ public static StringBuffer getWhereClause(JDBCFieldBridge[] fields, StringBuffer buf) { return getWhereClause(fields, "", buf); } /** * Returns identifier.columnName0=? * [AND identifier.columnName1=? * [AND identifier.columnName2=? [...]]] */ public static StringBuffer getWhereClause(JDBCFieldBridge[] fields, String identifier, StringBuffer buf) { boolean and = false; for(int i = 0; i < fields.length; ++i) { JDBCType type = getJDBCType(fields[i]); if(type != null) { if(and) buf.append(AND); else and = true; getWhereClause(type, identifier, buf); } } return buf; } /** * Returns columnName0=? [AND columnName1=? [AND columnName2=? [...]]] */ public static StringBuffer getWhereClause(JDBCFieldBridge[] fields, long mask, StringBuffer buf) { return getWhereClause(fields, mask, "", buf); } /** * Returns columnName0=? [AND columnName1=? [AND columnName2=? [...]]] */ private static StringBuffer getWhereClause(JDBCFieldBridge[] fields, long mask, String identifier, StringBuffer buf) { boolean and = false; long fieldMask = 1; for(int i = 0; i < fields.length; ++i) { if((fieldMask & mask) > 0) { JDBCType type = getJDBCType(fields[i]); if(type != null) { if(and) buf.append(AND); else and = true; getWhereClause(type, identifier, buf); } } fieldMask <<= 1; } return buf; } /** * Returns columnName0=? [AND columnName1=? [AND columnName2=? [...]]] */ public static StringBuffer getWhereClause(JDBCFieldBridge field, StringBuffer buf) { return getWhereClause(field.getJDBCType(), "", buf); } /** * Returns identifier.columnName0=? * [AND identifier.columnName1=? * [AND identifier.columnName2=? [...]]] */ public static StringBuffer getWhereClause(JDBCType type, String identifier, StringBuffer buf) { if(identifier.length() > 0) { identifier += '.'; } String[] columnNames = type.getColumnNames(); buf.append(identifier).append(columnNames[0]).append(EQ_QUESTMARK); int i = 1; while(i < columnNames.length) { buf.append(AND).append(identifier).append(columnNames[i++]).append(EQ_QUESTMARK); } return buf; } /** * Returns identifier.columnName0{comparison}? * [AND identifier.columnName1{comparison}? * [AND identifier.columnName2{comparison}? [...]]] */ public static StringBuffer getWhereClause(JDBCType type, String identifier, String comparison, StringBuffer buf) { if(identifier.length() > 0) { identifier += '.'; } String[] columnNames = type.getColumnNames(); buf.append(identifier).append(columnNames[0]).append(comparison).append('?'); int i = 1; while(i < columnNames.length) { buf.append(AND).append(identifier).append(columnNames[i++]).append(comparison).append('?'); } return buf; } // ======================================================================= // Is [Not] Null Clause // columnName0 IS [NOT] NULL [AND columnName1 IS [NOT] NULL [...]] // ======================================================================= /** * Returns identifier.columnName0 IS [NOT] NULL * [AND identifier.columnName1 IS [NOT] NULL * [AND identifier.columnName2 IS [NOT] NULL [...]]] */ public static StringBuffer getIsNullClause(boolean not, JDBCFieldBridge[] fields, String identifier, StringBuffer buf) { boolean and = false; for(int i = 0; i < fields.length; ++i) { JDBCType type = getJDBCType(fields[i]); if(type != null) { if(and) buf.append(AND); else and = true; getIsNullClause(not, type, identifier, buf); } } return buf; } /** * Returns identifier.columnName0 IS [NOT] NULL * [AND identifier.columnName1 IS [NOT] NULL * [AND identifier.columnName2 IS [NOT] NULL [...]]] */ public static StringBuffer getIsNullClause(boolean not, JDBCFieldBridge field, String identifier, StringBuffer buf) { return getIsNullClause(not, field.getJDBCType(), identifier, buf); } /** * Returns identifier.columnName0 IS [NOT] NULL * [AND identifier.columnName1 IS [NOT] NULL * [AND identifier.columnName2 IS [NOT] NULL [...]]] */ private static StringBuffer getIsNullClause(boolean not, JDBCType type, String identifier, StringBuffer buf) { if(identifier.length() > 0) { identifier += '.'; } String[] columnNames = type.getColumnNames(); buf.append(identifier).append(columnNames[0]).append(IS); (not ? buf.append(NOT) : buf).append(NULL); int i = 1; while(i < columnNames.length) { buf.append(AND).append(identifier).append(columnNames[i++]).append(IS); (not ? buf.append(NOT) : buf).append(NULL); } return buf; } // ======================================================================= // Join Clause // parent.pkColumnName0=child.fkColumnName0 // [AND parent.pkColumnName1=child.fkColumnName1 // [AND parent.pkColumnName2=child.fkColumnName2 [...]]] // ======================================================================= public static StringBuffer getJoinClause(JDBCAbstractCMRFieldBridge cmrField, String parentAlias, String childAlias, StringBuffer buf) { JDBCAbstractEntityBridge parentEntity = cmrField.getEntity(); JDBCAbstractEntityBridge childEntity = (JDBCAbstractEntityBridge)cmrField.getRelatedEntity(); JDBCFieldBridge parentField; JDBCFieldBridge childField; if(cmrField.hasForeignKey()) { // parent has the foreign keys JDBCFieldBridge[] parentFkFields = cmrField.getForeignKeyFields(); int i = 0; while(i < parentFkFields.length) { parentField = parentFkFields[i++]; childField = (JDBCFieldBridge)childEntity.getFieldByName(parentField.getFieldName()); getJoinClause(parentField, parentAlias, childField, childAlias, buf); if(i < parentFkFields.length) buf.append(AND); } } else { // child has the foreign keys JDBCFieldBridge[] childFkFields = cmrField.getRelatedCMRField().getForeignKeyFields(); int i = 0; while(i < childFkFields.length) { childField = childFkFields[i++]; parentField = (JDBCFieldBridge)parentEntity.getFieldByName(childField.getFieldName()); // add the sql getJoinClause(parentField, parentAlias, childField, childAlias, buf); if(i < childFkFields.length) { buf.append(AND); } } } return buf; } public static StringBuffer getRelationTableJoinClause(JDBCAbstractCMRFieldBridge cmrField, String parentAlias, String relationTableAlias, StringBuffer buf) { JDBCAbstractEntityBridge parentEntity = cmrField.getEntity(); JDBCFieldBridge parentField; JDBCFieldBridge relationField; // parent to relation table join JDBCFieldBridge[] parentFields = cmrField.getTableKeyFields(); int i = 0; while(i < parentFields.length) { relationField = parentFields[i++]; parentField = (JDBCFieldBridge)parentEntity.getFieldByName(relationField.getFieldName()); getJoinClause(parentField, parentAlias, relationField, relationTableAlias, buf); if(i < parentFields.length) buf.append(AND); } return buf; } /** * Returns parent.pkColumnName0=child.fkColumnName0 * [AND parent.pkColumnName1=child.fkColumnName1 * [AND parent.pkColumnName2=child.fkColumnName2 [...]]] */ private static StringBuffer getJoinClause(JDBCFieldBridge pkField, String parent, JDBCFieldBridge fkField, String child, StringBuffer buf) { return getJoinClause(pkField.getJDBCType(), parent, fkField.getJDBCType(), child, buf); } public static StringBuffer getJoinClause(JDBCFieldBridge[] pkFields, String parent, JDBCFieldBridge[] fkFields, String child, StringBuffer buf) { if(pkFields.length != fkFields.length) { throw new IllegalArgumentException( "Error createing theta join clause:" + " pkField.size()=" + pkFields.length + " fkField.size()=" + fkFields.length); } boolean and = false; for(int i = 0; i < pkFields.length; ++i) { // these types should not be null JDBCType pkType = getJDBCType(pkFields[i]); JDBCType fkType = getJDBCType(fkFields[i]); if(and) buf.append(AND); else and = true; getJoinClause(pkType, parent, fkType, child, buf); } return buf; } /** * Returns parent.pkColumnName0=child.fkColumnName0 * [AND parent.pkColumnName1=child.fkColumnName1 * [AND parent.pkColumnName2=child.fkColumnName2 [...]]] */ private static StringBuffer getJoinClause(JDBCType pkType, String parent, JDBCType fkType, String child, StringBuffer buf) { if(parent.length() > 0) { parent += '.'; } if(child.length() > 0) { child += '.'; } String[] pkColumnNames = pkType.getColumnNames(); String[] fkColumnNames = fkType.getColumnNames(); if(pkColumnNames.length != fkColumnNames.length) { throw new IllegalArgumentException("PK and FK have different number of columns"); } buf.append(parent).append(pkColumnNames[0]).append('=').append(child).append(fkColumnNames[0]); int i = 1; while(i < pkColumnNames.length) { buf.append(AND) .append(parent) .append(pkColumnNames[i]) .append('=') .append(child) .append(fkColumnNames[i++]); } return buf; } // ======================================================================= // Self Compare Where Clause // fromIdentifier.pkColumnName0=toIdentifier.fkColumnName0 // [AND fromIdentifier.pkColumnName1=toIdentifier.fkColumnName1 // [AND fromIdentifier.pkColumnName2=toIdentifier.fkColumnName2 [...]]] // ======================================================================= public static StringBuffer getSelfCompareWhereClause(JDBCFieldBridge[] fields, String fromIdentifier, String toIdentifier, StringBuffer buf) { boolean and = false; for(int i = 0; i < fields.length; ++i) { JDBCType type = getJDBCType(fields[i]); if(type != null) { if(and) buf.append(AND); else and = true; getSelfCompareWhereClause(type, fromIdentifier, toIdentifier, buf); } } return buf; } private static StringBuffer getSelfCompareWhereClause(JDBCType type, String fromIdentifier, String toIdentifier, StringBuffer buf) { if(fromIdentifier.length() > 0) fromIdentifier += '.'; if(toIdentifier.length() > 0) toIdentifier += '.'; String[] columnNames = type.getColumnNames(); buf.append(fromIdentifier) .append(columnNames[0]) .append('=') .append(toIdentifier) .append(columnNames[0]); int i = 1; while(i < columnNames.length) { buf.append(AND) .append(fromIdentifier) .append(columnNames[i]) .append('=') .append(toIdentifier) .append(columnNames[i++]); } return buf; } public static StringBuffer getSelfCompareWhereClause(JDBCFieldBridge fromField, JDBCFieldBridge toField, String fromIdentifier, String toIdentifier, String comparison, StringBuffer buf) { return getSelfCompareWhereClause( fromField.getJDBCType(), toField.getJDBCType(), fromIdentifier, toIdentifier, comparison, buf ); } private static StringBuffer getSelfCompareWhereClause(JDBCType fromType, JDBCType toType, String fromIdentifier, String toIdentifier, String comparison, StringBuffer buf) { if(fromIdentifier.length() > 0) fromIdentifier += '.'; if(toIdentifier.length() > 0) toIdentifier += '.'; String[] fromColumnNames = fromType.getColumnNames(); String[] toColumnNames = toType.getColumnNames(); buf.append(fromIdentifier) .append(fromColumnNames[0]) .append(comparison) .append(toIdentifier) .append(toColumnNames[0]); int i = 1; while(i < fromColumnNames.length) { buf.append(AND) .append(fromIdentifier) .append(fromColumnNames[i]) .append(comparison) .append(toIdentifier) .append(toColumnNames[i++]); } return buf; } public static boolean tableExists(String tableName, DataSource dataSource) throws DeploymentException { Connection con = null; ResultSet rs = null; try { con = dataSource.getConnection(); // (a j2ee spec compatible jdbc driver has to fully // implement the DatabaseMetaData) DatabaseMetaData dmd = con.getMetaData(); String catalog = con.getCatalog(); String schema = null; String quote = dmd.getIdentifierQuoteString(); if(tableName.startsWith(quote)) { if(tableName.endsWith(quote) == false) { throw new DeploymentException("Mismatched quote in table name: " + tableName); } int quoteLength = quote.length(); tableName = tableName.substring(quoteLength, tableName.length() - quoteLength); if(dmd.storesLowerCaseQuotedIdentifiers()) tableName = tableName.toLowerCase(); else if(dmd.storesUpperCaseQuotedIdentifiers()) tableName = tableName.toUpperCase(); } else { if(dmd.storesLowerCaseIdentifiers()) tableName = tableName.toLowerCase(); else if(dmd.storesUpperCaseIdentifiers()) tableName = tableName.toUpperCase(); } // Patch #927759: Split tablename into "schema" and "table" separated by '.' int dotIndex; if ((dotIndex = tableName.indexOf('.')) != -1) { // Yank out schema name ... schema = tableName.substring(0, dotIndex); tableName = tableName.substring(dotIndex + 1); } rs = dmd.getTables(catalog, schema, tableName, null); return rs.next(); } catch(SQLException e) { // This should not happen. A J2EE compatiable JDBC driver is // required fully support metadata. throw new DeploymentException("Error while checking if table aleady exists "+tableName, e); } finally { JDBCUtil.safeClose(rs); JDBCUtil.safeClose(con); } } public static OldColumns getOldColumns(String tableName, DataSource dataSource) throws DeploymentException { Connection con = null; ResultSet rs = null; ArrayList columnNames = new ArrayList(); ArrayList typeNames = new ArrayList(); ArrayList columnSizes = new ArrayList(); try { con = dataSource.getConnection(); // (a j2ee spec compatible jdbc driver has to fully // implement the DatabaseMetaData) DatabaseMetaData dmd = con.getMetaData(); String catalog = con.getCatalog(); String schema = null; String quote = dmd.getIdentifierQuoteString(); if (tableName.startsWith(quote)) { if (tableName.endsWith(quote) == false) { throw new DeploymentException("Mismatched quote in table name: " + tableName); } int quoteLength = quote.length(); tableName = tableName.substring(quoteLength, tableName.length() - quoteLength); if (dmd.storesLowerCaseQuotedIdentifiers()) tableName = tableName.toLowerCase(); else if (dmd.storesUpperCaseQuotedIdentifiers()) tableName = tableName.toUpperCase(); } else { if (dmd.storesLowerCaseIdentifiers()) tableName = tableName.toLowerCase(); else if (dmd.storesUpperCaseIdentifiers()) tableName = tableName.toUpperCase(); } // Patch #927759: Split tablename into "schema" and "table" separated by '.' int dotIndex; if ((dotIndex = tableName.indexOf('.')) != -1) { // Yank out schema name ... schema = tableName.substring(0, dotIndex); tableName = tableName.substring(dotIndex + 1); } rs = dmd.getColumns(catalog, schema, tableName, null); while (rs.next()) { String columnName = rs.getString("COLUMN_NAME"); columnNames.add(columnName == null ? null : columnName.toUpperCase()); typeNames.add(rs.getString("TYPE_NAME")); columnSizes.add(new Integer(rs.getInt("COLUMN_SIZE"))); } return new OldColumns(columnNames, typeNames, columnSizes); } catch (SQLException e) { // This should not happen. A J2EE compatiable JDBC driver is // required fully support metadata. throw new DeploymentException("Error while geting column names", e); } finally { JDBCUtil.safeClose(rs); JDBCUtil.safeClose(con); } } public static OldIndexes getOldIndexes(String tableName, DataSource dataSource) throws DeploymentException { tableName = unquote(tableName, dataSource); Connection con = null; ResultSet rs = null; ArrayList indexNames = new ArrayList(); ArrayList columnNames = new ArrayList(); ArrayList ascDesc = new ArrayList(); try { con = dataSource.getConnection(); // (a j2ee spec compatible jdbc driver has to fully // implement the DatabaseMetaData) DatabaseMetaData dmd = con.getMetaData(); String catalog = con.getCatalog(); String schema = null; if (dmd.storesLowerCaseIdentifiers()) tableName = tableName.toLowerCase(); else if (dmd.storesUpperCaseIdentifiers()) tableName = tableName.toUpperCase(); // Patch #927759: Split tablename into "schema" and "table" separated by '.' int dotIndex; if ((dotIndex = tableName.indexOf('.')) != -1) { // Yank out schema name ... schema = tableName.substring(0, dotIndex); tableName = tableName.substring(dotIndex + 1); } rs = dmd.getIndexInfo(catalog, schema, tableName, false, false); while (rs.next()) { indexNames.add(rs.getString("INDEX_NAME")); columnNames.add(rs.getString("COLUMN_NAME")); ascDesc.add(rs.getString("ASC_OR_DESC")); } return new OldIndexes(indexNames, columnNames, ascDesc); } catch (SQLException e) { // This should not happen. A J2EE compatiable JDBC driver is // required fully support metadata. throw new DeploymentException("Error while geting column names", e); } finally { JDBCUtil.safeClose(rs); JDBCUtil.safeClose(con); } } public static String unquote(String tableName, DataSource ds) throws DeploymentException { Connection con = null; try { con = ds.getConnection(); String quote = con.getMetaData().getIdentifierQuoteString(); if (tableName.startsWith(quote)) { if (tableName.endsWith(quote) == false) { throw new DeploymentException("Mismatched quote in table name: " + tableName); } int quoteLength = quote.length(); tableName = tableName.substring(quoteLength, tableName.length() - quoteLength); } } catch(SQLException e) { throw new DeploymentException("Failed to get datasource connection"); } finally { JDBCUtil.safeClose(con); } return tableName; } private static JDBCType getJDBCType(JDBCFieldBridge field) { JDBCType type = field.getJDBCType(); if(type != null && type.getColumnNames().length > 0) { return type; } return null; } public static void dropTable(DataSource dataSource, String tableName) throws DeploymentException { Logger log = Logger.getLogger("CLEANER"); String sql = "DROP TABLE " + tableName; try { Connection con = null; Statement statement = null; try { // execute sql con = dataSource.getConnection(); statement = con.createStatement(); statement.executeUpdate(sql); } finally { // make sure to close the connection and statement before // comitting the transaction or XA will break JDBCUtil.safeClose(statement); JDBCUtil.safeClose(con); } } catch (Exception e) { throw new DeploymentException("Error while droping table "+tableName, e); } log.info("Dropped table "+tableName+" succesfuly"); } /** * utility class to store the information returned by getOldColumns() */ public static class OldColumns { private ArrayList columnNames; private ArrayList typeNames; private ArrayList columnSizes; private OldColumns(ArrayList cn, ArrayList tn, ArrayList cs) { columnNames = cn; typeNames = tn; columnSizes = cs; } public ArrayList getColumnNames() { return columnNames; } public ArrayList getTypeNames() { return typeNames; } public ArrayList getColumnSizes() { return columnSizes; } } /** * utility class to store the information returned by getOldColumns() */ public static class OldIndexes { private ArrayList indexNames; private ArrayList columnNames; private ArrayList columnAscDesc; private OldIndexes(ArrayList in, ArrayList cn, ArrayList ad) { indexNames = in; columnNames = cn; columnAscDesc = ad; } public ArrayList getColumnNames() { return columnNames; } public ArrayList getIndexNames() { return indexNames; } public ArrayList getColumnAscDesc() { return columnAscDesc; } } }