OPENJPA-735: OpenJPA support for SolidDB

git-svn-id: https://svn.apache.org/repos/asf/openjpa/trunk@987013 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Fay Wang 2010-08-19 04:19:39 +00:00
parent 63a24dc7bb
commit 07d599a02d
5 changed files with 216 additions and 21 deletions

View File

@ -558,7 +558,7 @@ public class SchemaTool {
if (dbTable != null) { if (dbTable != null) {
idx = findIndex(dbTable, idxs[k]); idx = findIndex(dbTable, idxs[k]);
if (idx == null) { if (idx == null) {
if (createIndex(idxs[k], dbTable)) if (createIndex(idxs[k], dbTable, tabs[j].getUniques()))
dbTable.importIndex(idxs[k]); dbTable.importIndex(idxs[k]);
else else
_log.warn(_loc.get("add-index", idxs[k], _log.warn(_loc.get("add-index", idxs[k],
@ -953,7 +953,7 @@ public class SchemaTool {
*/ */
public boolean createTable(Table table) public boolean createTable(Table table)
throws SQLException { 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 * @return true if the operation was successful, false otherwise
*/ */
public boolean createIndex(Index idx, Table table) 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 { throws SQLException {
// Informix will automatically create a unique index for the // Informix will automatically create a unique index for the
// primary key, so don't create another index again // primary key, so don't create another index again
if (!_dict.needsToCreateIndex(idx,table)) if (!_dict.needsToCreateIndex(idx,table,uniques))
return false; return false;
int max = _dict.maxIndexesPerTable; int max = _dict.maxIndexesPerTable;

View File

@ -87,6 +87,7 @@ import org.apache.openjpa.jdbc.schema.Index;
import org.apache.openjpa.jdbc.schema.NameSet; import org.apache.openjpa.jdbc.schema.NameSet;
import org.apache.openjpa.jdbc.schema.PrimaryKey; import org.apache.openjpa.jdbc.schema.PrimaryKey;
import org.apache.openjpa.jdbc.schema.Schema; 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.Sequence;
import org.apache.openjpa.jdbc.schema.Table; import org.apache.openjpa.jdbc.schema.Table;
import org.apache.openjpa.jdbc.schema.Unique; import org.apache.openjpa.jdbc.schema.Unique;
@ -3298,6 +3299,14 @@ public class DBDictionary
maxLen, checkForUniqueness); 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 * Return a series of SQL statements to create the given table, complete
* with columns. Indexes and constraints will be created separately. * with columns. Indexes and constraints will be created separately.
@ -4525,10 +4534,7 @@ public class DBDictionary
String query = lastGeneratedKeyQuery; String query = lastGeneratedKeyQuery;
if (query.indexOf('{') != -1) // only if the token is in the string if (query.indexOf('{') != -1) // only if the token is in the string
{ {
query = MessageFormat.format(query, new Object[]{ query = getGenKeySeqName(query, col);
toDBName(col.getIdentifier()), getFullName(col.getTable(), false),
getGeneratedKeySequenceName(col),
});
} }
PreparedStatement stmnt = prepareStatement(conn, query); 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 * Return the sequence name used by databases for the given autoassigned
* column. This is only used by databases that require an explicit name * column. This is only used by databases that require an explicit name
@ -5206,6 +5219,10 @@ public class DBDictionary
return false; return false;
} }
public boolean needsToCreateIndex(Index idx, Table table, Unique[] uniques) {
return needsToCreateIndex(idx, table);
}
public boolean needsToCreateIndex(Index idx, Table table) { public boolean needsToCreateIndex(Index idx, Table table) {
return true; return true;
} }

View File

@ -21,6 +21,7 @@ package org.apache.openjpa.jdbc.sql;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.sql.PreparedStatement; import java.sql.PreparedStatement;
import java.sql.SQLException; import java.sql.SQLException;
import java.text.MessageFormat;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
@ -28,13 +29,19 @@ import java.util.List;
import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.StringUtils;
import org.apache.openjpa.jdbc.identifier.DBIdentifier; import org.apache.openjpa.jdbc.identifier.DBIdentifier;
import org.apache.openjpa.jdbc.kernel.exps.FilterValue; 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.Column;
import org.apache.openjpa.jdbc.schema.Index; import org.apache.openjpa.jdbc.schema.Index;
import org.apache.openjpa.jdbc.schema.PrimaryKey; 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.Table;
import org.apache.openjpa.jdbc.schema.Unique; 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.lib.util.Localizer;
import org.apache.openjpa.meta.JavaTypes; import org.apache.openjpa.meta.JavaTypes;
import org.apache.openjpa.util.UserException;
/** /**
* Dictionary for SolidDB database. * Dictionary for SolidDB database.
@ -75,7 +82,7 @@ public class SolidDBDictionary
public boolean useTriggersForAutoAssign = true; 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; public String autoAssignSequenceName = null;
@ -85,6 +92,11 @@ public class SolidDBDictionary
*/ */
public boolean openjpa3GeneratedKeyNames = false; public boolean openjpa3GeneratedKeyNames = false;
/**
* Possible values for LockingMode are "PESSIMISTIC" and "OPTIMISTIC"
*/
public String lockingMode = null;
private static final Localizer _loc = Localizer.forPackage private static final Localizer _loc = Localizer.forPackage
(SolidDBDictionary.class); (SolidDBDictionary.class);
@ -111,6 +123,10 @@ public class SolidDBDictionary
currentDateFunction = "CURDATE()"; currentDateFunction = "CURDATE()";
currentTimeFunction = "CURTIME()"; currentTimeFunction = "CURTIME()";
currentTimestampFunction = "NOW()"; 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[]{ reservedWordSet.addAll(Arrays.asList(new String[]{
"BIGINT", "BINARY", "DATE", "TIME", "BIGINT", "BINARY", "DATE", "TIME",
@ -119,7 +135,15 @@ public class SolidDBDictionary
} }
@Override @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(); StringBuilder buf = new StringBuilder();
buf.append("CREATE TABLE ").append(getFullName(table, false)).append(" ("); buf.append("CREATE TABLE ").append(getFullName(table, false)).append(" (");
Column[] cols = table.getColumns(); Column[] cols = table.getColumns();
@ -151,7 +175,26 @@ public class SolidDBDictionary
else else
buf.append("DISK"); 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) if (!useTriggersForAutoAssign)
return create; return create;
@ -163,14 +206,11 @@ public class SolidDBDictionary
if (seqs == null) if (seqs == null)
seqs = new ArrayList(4); seqs = new ArrayList(4);
seq = autoAssignSequenceName; seq = getAutoGenSeqName(cols[i]);
if (seq == null) { if (sequenecExists(table.getSchemaIdentifier().getName(), seq, group))
if (openjpa3GeneratedKeyNames) seqs.add("DROP SEQUENCE " + seq);
seq = getOpenJPA3GeneratedKeySequenceName(cols[i]); seqs.add("CREATE SEQUENCE " + seq);
else
seq = getGeneratedKeySequenceName(cols[i]);
seqs.add("CREATE SEQUENCE " + seq);
}
if (openjpa3GeneratedKeyNames) if (openjpa3GeneratedKeyNames)
trig = getOpenJPA3GeneratedKeyTriggerName(cols[i]); trig = getOpenJPA3GeneratedKeyTriggerName(cols[i]);
else else
@ -189,6 +229,7 @@ public class SolidDBDictionary
+ " ON " + toDBName(table.getIdentifier()) + " ON " + toDBName(table.getIdentifier())
+ " BEFORE INSERT REFERENCING NEW " + toDBName(cols[i].getIdentifier()) + " BEFORE INSERT REFERENCING NEW " + toDBName(cols[i].getIdentifier())
+ " AS NEW_COL1 BEGIN EXEC SEQUENCE " + seq + " NEXT INTO NEW_COL1; END"); + " AS NEW_COL1 BEGIN EXEC SEQUENCE " + seq + " NEXT INTO NEW_COL1; END");
} }
if (seqs == null) if (seqs == null)
return create; return create;
@ -201,6 +242,23 @@ public class SolidDBDictionary
return sql; 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. * Trigger name for simulating auto-assign values on the given column.
*/ */
@ -230,6 +288,22 @@ public class SolidDBDictionary
getSchemaGroup(), maxTableNameLength, true)); 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 @Override
public String convertSchemaCase(DBIdentifier objectName) { public String convertSchemaCase(DBIdentifier objectName) {
if (objectName != null && objectName.getName() == null) if (objectName != null && objectName.getName() == null)
@ -302,6 +376,23 @@ public class SolidDBDictionary
return super.isSystemIndex(name, table) || startsWith$$; 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 @Override
public void setBigDecimal(PreparedStatement stmnt, int idx, BigDecimal val, public void setBigDecimal(PreparedStatement stmnt, int idx, BigDecimal val,
Column col) throws SQLException { Column col) throws SQLException {
@ -347,13 +438,90 @@ public class SolidDBDictionary
} }
@Override @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 // SolidDB will automatically create a unique index for the
// constraint, so don't create another index again // constraint, so don't create another index again
PrimaryKey pk = table.getPrimaryKey(); PrimaryKey pk = table.getPrimaryKey();
if (pk != null && idx.columnsMatch(pk.getColumns())) if (pk != null && idx.columnsMatch(pk.getColumns()))
return false; 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(")");
}
}
} }

View File

@ -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 \ 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 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. string defined by the property rather than the default string for DB2.
invalid-locking-mode: Invalid locking mode for SolidDB: "{0}"

View File

@ -26,6 +26,7 @@ import javax.persistence.Query;
import org.apache.openjpa.jdbc.conf.JDBCConfiguration; import org.apache.openjpa.jdbc.conf.JDBCConfiguration;
import org.apache.openjpa.jdbc.sql.DBDictionary; import org.apache.openjpa.jdbc.sql.DBDictionary;
import org.apache.openjpa.jdbc.sql.DerbyDictionary; 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.test.SingleEMTestCase;
import org.apache.openjpa.persistence.simple.AllFieldTypes; import org.apache.openjpa.persistence.simple.AllFieldTypes;
import org.apache.openjpa.persistence.ArgumentException; import org.apache.openjpa.persistence.ArgumentException;
@ -186,6 +187,10 @@ public abstract class GroupingTestCase
} }
public void testSubstringInGroupBy() { public void testSubstringInGroupBy() {
DBDictionary dict = ((JDBCConfiguration)emf.getConfiguration()).getDBDictionaryInstance();
if (dict instanceof SolidDBDictionary)
return;
// this is an extension of JPQL // this is an extension of JPQL
Query q = em.createQuery("select substring(o.stringField, 1, 1), " + Query q = em.createQuery("select substring(o.stringField, 1, 1), " +
"count(o) from AllFieldTypes o " + "count(o) from AllFieldTypes o " +