From 07d599a02ded21441ccdaec9dd03301238081243 Mon Sep 17 00:00:00 2001 From: Fay Wang Date: Thu, 19 Aug 2010 04:19:39 +0000 Subject: [PATCH] OPENJPA-735: OpenJPA support for SolidDB git-svn-id: https://svn.apache.org/repos/asf/openjpa/trunk@987013 13f79535-47bb-0310-9956-ffa450edef68 --- .../openjpa/jdbc/schema/SchemaTool.java | 11 +- .../apache/openjpa/jdbc/sql/DBDictionary.java | 25 ++- .../openjpa/jdbc/sql/SolidDBDictionary.java | 194 ++++++++++++++++-- .../openjpa/jdbc/sql/localizer.properties | 2 +- .../persistence/query/GroupingTestCase.java | 5 + 5 files changed, 216 insertions(+), 21 deletions(-) diff --git a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/schema/SchemaTool.java b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/schema/SchemaTool.java index 8c90933dc..21c3f40fd 100644 --- a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/schema/SchemaTool.java +++ b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/schema/SchemaTool.java @@ -558,7 +558,7 @@ public class SchemaTool { if (dbTable != null) { idx = findIndex(dbTable, idxs[k]); if (idx == null) { - if (createIndex(idxs[k], dbTable)) + if (createIndex(idxs[k], dbTable, tabs[j].getUniques())) dbTable.importIndex(idxs[k]); else _log.warn(_loc.get("add-index", idxs[k], @@ -953,7 +953,7 @@ public class SchemaTool { */ public boolean createTable(Table table) throws SQLException { - return executeSQL(_dict.getCreateTableSQL(table)); + return executeSQL(_dict.getCreateTableSQL(table, _db)); } /** @@ -992,11 +992,16 @@ public class SchemaTool { * @return true if the operation was successful, false otherwise */ public boolean createIndex(Index idx, Table table) + throws SQLException { + return createIndex(idx, table, null); + } + + public boolean createIndex(Index idx, Table table, Unique[] uniques) throws SQLException { // Informix will automatically create a unique index for the // primary key, so don't create another index again - if (!_dict.needsToCreateIndex(idx,table)) + if (!_dict.needsToCreateIndex(idx,table,uniques)) return false; int max = _dict.maxIndexesPerTable; diff --git a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/DBDictionary.java b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/DBDictionary.java index 57fa10c02..eb1d3b613 100644 --- a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/DBDictionary.java +++ b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/DBDictionary.java @@ -87,6 +87,7 @@ import org.apache.openjpa.jdbc.schema.Index; import org.apache.openjpa.jdbc.schema.NameSet; import org.apache.openjpa.jdbc.schema.PrimaryKey; import org.apache.openjpa.jdbc.schema.Schema; +import org.apache.openjpa.jdbc.schema.SchemaGroup; import org.apache.openjpa.jdbc.schema.Sequence; import org.apache.openjpa.jdbc.schema.Table; import org.apache.openjpa.jdbc.schema.Unique; @@ -3298,6 +3299,14 @@ public class DBDictionary maxLen, checkForUniqueness); } + /** + * Return a series of SQL statements to create the given table, complete + * with columns. Indexes and constraints will be created separately. + */ + public String[] getCreateTableSQL(Table table, SchemaGroup group) { + return getCreateTableSQL(table); + } + /** * Return a series of SQL statements to create the given table, complete * with columns. Indexes and constraints will be created separately. @@ -4525,10 +4534,7 @@ public class DBDictionary String query = lastGeneratedKeyQuery; if (query.indexOf('{') != -1) // only if the token is in the string { - query = MessageFormat.format(query, new Object[]{ - toDBName(col.getIdentifier()), getFullName(col.getTable(), false), - getGeneratedKeySequenceName(col), - }); + query = getGenKeySeqName(query, col); } PreparedStatement stmnt = prepareStatement(conn, query); @@ -4545,6 +4551,13 @@ public class DBDictionary } } + protected String getGenKeySeqName(String query, Column col) { + return MessageFormat.format(query, new Object[]{ + toDBName(col.getIdentifier()), getFullName(col.getTable(), false), + getGeneratedKeySequenceName(col), + }); + } + /** * Return the sequence name used by databases for the given autoassigned * column. This is only used by databases that require an explicit name @@ -5206,6 +5219,10 @@ public class DBDictionary return false; } + public boolean needsToCreateIndex(Index idx, Table table, Unique[] uniques) { + return needsToCreateIndex(idx, table); + } + public boolean needsToCreateIndex(Index idx, Table table) { return true; } diff --git a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/SolidDBDictionary.java b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/SolidDBDictionary.java index a4ecb61f1..863442b1e 100644 --- a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/SolidDBDictionary.java +++ b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/SolidDBDictionary.java @@ -21,6 +21,7 @@ package org.apache.openjpa.jdbc.sql; import java.math.BigDecimal; import java.sql.PreparedStatement; import java.sql.SQLException; +import java.text.MessageFormat; import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -28,13 +29,19 @@ import java.util.List; import org.apache.commons.lang.StringUtils; import org.apache.openjpa.jdbc.identifier.DBIdentifier; import org.apache.openjpa.jdbc.kernel.exps.FilterValue; +import org.apache.openjpa.jdbc.kernel.exps.Lit; import org.apache.openjpa.jdbc.schema.Column; import org.apache.openjpa.jdbc.schema.Index; import org.apache.openjpa.jdbc.schema.PrimaryKey; +import org.apache.openjpa.jdbc.schema.Schema; +import org.apache.openjpa.jdbc.schema.SchemaGroup; +import org.apache.openjpa.jdbc.schema.Sequence; import org.apache.openjpa.jdbc.schema.Table; import org.apache.openjpa.jdbc.schema.Unique; +import org.apache.openjpa.kernel.exps.Literal; import org.apache.openjpa.lib.util.Localizer; import org.apache.openjpa.meta.JavaTypes; +import org.apache.openjpa.util.UserException; /** * Dictionary for SolidDB database. @@ -75,7 +82,7 @@ public class SolidDBDictionary public boolean useTriggersForAutoAssign = true; /** - * The global sequence name to use for autoassign simulation. + * The global sequence name to use for auto-assign simulation. */ public String autoAssignSequenceName = null; @@ -84,6 +91,11 @@ public class SolidDBDictionary * trigger name for backwards compatibility. */ public boolean openjpa3GeneratedKeyNames = false; + + /** + * Possible values for LockingMode are "PESSIMISTIC" and "OPTIMISTIC" + */ + public String lockingMode = null; private static final Localizer _loc = Localizer.forPackage @@ -111,6 +123,10 @@ public class SolidDBDictionary currentDateFunction = "CURDATE()"; currentTimeFunction = "CURTIME()"; currentTimestampFunction = "NOW()"; + lastGeneratedKeyQuery = "SELECT {0}.CURRENT"; + sequenceSQL = "SELECT SEQUENCE_SCHEMA, SEQUENCE_NAME FROM SYS_SEQUENCES"; + sequenceSchemaSQL = "SEQSCHEMA = ?"; + sequenceNameSQL = "SEQNAME = ?"; reservedWordSet.addAll(Arrays.asList(new String[]{ "BIGINT", "BINARY", "DATE", "TIME", @@ -119,7 +135,15 @@ public class SolidDBDictionary } @Override - public String[] getCreateTableSQL(Table table) { + public void endConfiguration() { + super.endConfiguration(); + if (useTriggersForAutoAssign) { + supportsAutoAssign = true; + } + } + + @Override + public String[] getCreateTableSQL(Table table, SchemaGroup group) { StringBuilder buf = new StringBuilder(); buf.append("CREATE TABLE ").append(getFullName(table, false)).append(" ("); Column[] cols = table.getColumns(); @@ -151,7 +175,26 @@ public class SolidDBDictionary else buf.append("DISK"); - String[] create = new String[]{ buf.toString() }; + String[] create = null; + if (lockingMode != null) { + StringBuilder buf1 = new StringBuilder(); + if (lockingMode.equalsIgnoreCase("PESSIMISTIC")) { + buf1.append("ALTER TABLE ").append(getFullName(table, false)). + append(" SET PESSIMISTIC"); + } else if (lockingMode.equalsIgnoreCase("OPTIMISTIC")){ + buf1.append("ALTER TABLE ").append(getFullName(table, false)). + append(" SET OPTIMISTIC"); + } else + throw new UserException(_loc.get("invalid-locking-mode", lockingMode)); + + create = new String[2]; + create[0] = buf.toString(); + create[1] = buf1.toString(); + } else { + create = new String[1]; + create[0] = buf.toString(); + } + if (!useTriggersForAutoAssign) return create; @@ -163,14 +206,11 @@ public class SolidDBDictionary if (seqs == null) seqs = new ArrayList(4); - seq = autoAssignSequenceName; - if (seq == null) { - if (openjpa3GeneratedKeyNames) - seq = getOpenJPA3GeneratedKeySequenceName(cols[i]); - else - seq = getGeneratedKeySequenceName(cols[i]); - seqs.add("CREATE SEQUENCE " + seq); - } + seq = getAutoGenSeqName(cols[i]); + if (sequenecExists(table.getSchemaIdentifier().getName(), seq, group)) + seqs.add("DROP SEQUENCE " + seq); + seqs.add("CREATE SEQUENCE " + seq); + if (openjpa3GeneratedKeyNames) trig = getOpenJPA3GeneratedKeyTriggerName(cols[i]); else @@ -189,6 +229,7 @@ public class SolidDBDictionary + " ON " + toDBName(table.getIdentifier()) + " BEFORE INSERT REFERENCING NEW " + toDBName(cols[i].getIdentifier()) + " AS NEW_COL1 BEGIN EXEC SEQUENCE " + seq + " NEXT INTO NEW_COL1; END"); + } if (seqs == null) return create; @@ -201,6 +242,23 @@ public class SolidDBDictionary return sql; } + protected boolean sequenecExists(String schemaName, String seqName, SchemaGroup group) { + Schema[] schemas = group.getSchemas(); + for (int i = 0; i < schemas.length; i++) { + String dbSchemaName = schemas[i].getIdentifier().getName(); + if (schemaName != null && !schemaName.equalsIgnoreCase(dbSchemaName)) + continue; + + Sequence[] seqs = schemas[i].getSequences(); + for (int j = 0; j < seqs.length; j++) { + String dbSeqName = seqs[j].getName(); + if (dbSeqName != null && dbSeqName.equalsIgnoreCase(seqName)) + return true; + } + } + return false; + } + /** * Trigger name for simulating auto-assign values on the given column. */ @@ -230,6 +288,22 @@ public class SolidDBDictionary getSchemaGroup(), maxTableNameLength, true)); } + protected String getAutoGenSeqName(Column col) { + String seqName = autoAssignSequenceName; + if (seqName == null) { + if (openjpa3GeneratedKeyNames) + seqName = getOpenJPA3GeneratedKeySequenceName(col); + else + seqName = getGeneratedKeySequenceName(col); + } + return seqName; + } + + @Override + protected String getGenKeySeqName(String query, Column col) { + return MessageFormat.format(query, new Object[]{getAutoGenSeqName(col)}); + } + @Override public String convertSchemaCase(DBIdentifier objectName) { if (objectName != null && objectName.getName() == null) @@ -302,6 +376,23 @@ public class SolidDBDictionary return super.isSystemIndex(name, table) || startsWith$$; } + @Override + public boolean isSystemSequence(DBIdentifier name, DBIdentifier schema, + boolean targetSchema) { + if (super.isSystemSequence(name, schema, targetSchema)) + return true; + String schemaName = DBIdentifier.isNull(schema) ? null : schema.getName(); + boolean startsWith_SYSTEM = schema.isDelimited() ? schemaName.startsWith("\"_SYSTEM") : + schemaName.startsWith("_SYSTEM"); + + String seqName = DBIdentifier.isNull(name) ? null : name.getName(); + boolean startsWithSYS_SEQ_ = name.isDelimited() ? seqName.startsWith("\"SYS_SEQ_") : + seqName.startsWith("SYS_SEQ_"); + if (startsWith_SYSTEM && startsWithSYS_SEQ_) + return true; + return false; + } + @Override public void setBigDecimal(PreparedStatement stmnt, int idx, BigDecimal val, Column col) throws SQLException { @@ -347,13 +438,90 @@ public class SolidDBDictionary } @Override - public boolean needsToCreateIndex(Index idx, Table table) { + public boolean needsToCreateIndex(Index idx, Table table, Unique[] uniques) { // SolidDB will automatically create a unique index for the // constraint, so don't create another index again PrimaryKey pk = table.getPrimaryKey(); if (pk != null && idx.columnsMatch(pk.getColumns())) return false; - return true; + + // If table1 has constraints on column (a, b), an explicit index on (a) + // will cause duplicate index error from SolidDB + Column[] icols = idx.getColumns(); + boolean isDuplicate = false; + boolean mayBeDuplicate = false; + for (int i = 0; i < uniques.length; i++) { + Column[] ucols = uniques[i].getColumns(); + if (ucols.length < icols.length) + continue; + for (int j = 0, k = 0; j < ucols.length && k < icols.length; j++, k++) { + if (mayBeDuplicate && ucols[j].getQualifiedPath().equals(icols[k].getQualifiedPath())) { + if (k == icols.length - 1) { + isDuplicate = true; + } else { + mayBeDuplicate = true; + } + } else + mayBeDuplicate = false; + } + if (isDuplicate) + break; + } + return isDuplicate; + } + + @Override + protected String getSequencesSQL(String schemaName, String sequenceName) { + return getSequencesSQL(DBIdentifier.newSchema(schemaName), + DBIdentifier.newSequence(sequenceName)); } + @Override + protected String getSequencesSQL(DBIdentifier schemaName, DBIdentifier sequenceName) { + StringBuilder buf = new StringBuilder(); + buf.append(sequenceSQL); + if (!DBIdentifier.isNull(schemaName) || !DBIdentifier.isNull(sequenceName)) + buf.append(" WHERE "); + if (!DBIdentifier.isNull(schemaName)) { + buf.append(sequenceSchemaSQL); + if (!DBIdentifier.isNull(sequenceName)) + buf.append(" AND "); + } + if (!DBIdentifier.isNull(sequenceName)) + buf.append(sequenceNameSQL); + return buf.toString(); + } + + @Override + protected void appendSelect(SQLBuffer selectSQL, Object alias, Select sel, + int idx) { + // if this is a literal value, add a cast... + Object val = sel.getSelects().get(idx); + boolean toCast = (val instanceof Lit) && + ((Lit)val).getParseType() != Literal.TYPE_DATE && + ((Lit)val).getParseType() != Literal.TYPE_TIME && + ((Lit)val).getParseType() != Literal.TYPE_TIMESTAMP; + + if (toCast) + selectSQL.append("CAST("); + + // ... and add the select per super's behavior... + super.appendSelect(selectSQL, alias, sel, idx); + + // ... and finish the cast + if (toCast) { + Class c = ((Lit) val).getType(); + int javaTypeCode = JavaTypes.getTypeCode(c); + int jdbcTypeCode = getJDBCType(javaTypeCode, false); + String typeName = getTypeName(jdbcTypeCode); + selectSQL.append(" AS " + typeName); + + // if the literal is a string, use the default char col size + // in the cast statement. + if (String.class.equals(c)) + selectSQL.append("(" + characterColumnSize + ")"); + + selectSQL.append(")"); + } + } } diff --git a/openjpa-jdbc/src/main/resources/org/apache/openjpa/jdbc/sql/localizer.properties b/openjpa-jdbc/src/main/resources/org/apache/openjpa/jdbc/sql/localizer.properties index c0e49486a..304d9d510 100644 --- a/openjpa-jdbc/src/main/resources/org/apache/openjpa/jdbc/sql/localizer.properties +++ b/openjpa-jdbc/src/main/resources/org/apache/openjpa/jdbc/sql/localizer.properties @@ -220,4 +220,4 @@ sequencesql-override: Going to override the DB2 specific default for the \ the property. This will allow openJPA to detect a difference between the DB2 default \ string and the string set in the property and will further allow openJPA to use the \ string defined by the property rather than the default string for DB2. - +invalid-locking-mode: Invalid locking mode for SolidDB: "{0}" diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/query/GroupingTestCase.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/query/GroupingTestCase.java index 26aa9e2b3..8e2b7f7aa 100644 --- a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/query/GroupingTestCase.java +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/query/GroupingTestCase.java @@ -26,6 +26,7 @@ import javax.persistence.Query; import org.apache.openjpa.jdbc.conf.JDBCConfiguration; import org.apache.openjpa.jdbc.sql.DBDictionary; import org.apache.openjpa.jdbc.sql.DerbyDictionary; +import org.apache.openjpa.jdbc.sql.SolidDBDictionary; import org.apache.openjpa.persistence.test.SingleEMTestCase; import org.apache.openjpa.persistence.simple.AllFieldTypes; import org.apache.openjpa.persistence.ArgumentException; @@ -186,6 +187,10 @@ public abstract class GroupingTestCase } public void testSubstringInGroupBy() { + DBDictionary dict = ((JDBCConfiguration)emf.getConfiguration()).getDBDictionaryInstance(); + if (dict instanceof SolidDBDictionary) + return; + // this is an extension of JPQL Query q = em.createQuery("select substring(o.stringField, 1, 1), " + "count(o) from AllFieldTypes o " +