OPENJPA-360 SQL FOR UPDATE OF incorrectly generated for DB2 UDB version8.1 or earlier and DB2 ISeries V5R3 or earlier.

These  DB2 version also require the "optimize for <n> row" clause appear before FOR UPDATE clause.
Due to this requirement, the OPTIMIZE clause will  appear before FOR UPDATE clause for all DB2 versions.
Also fixed the affected testcases.


git-svn-id: https://svn.apache.org/repos/asf/openjpa/trunk@574464 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Catalina Wei 2007-09-11 05:32:37 +00:00
parent 3d8748c427
commit a4bf964c08
6 changed files with 88 additions and 113 deletions

View File

@ -50,12 +50,12 @@ public class DB2Dictionary
public String optimizeClause = "optimize for"; public String optimizeClause = "optimize for";
public String rowClause = "row"; public String rowClause = "row";
protected int db2ServerType = 0; protected int db2ServerType = 0;
protected static final int db2ISeriesV5R3OrEarlier = 1; public static final int db2ISeriesV5R3OrEarlier = 1;
protected static final int db2UDBV81OrEarlier = 2; public static final int db2UDBV81OrEarlier = 2;
protected static final int db2ZOSV8xOrLater = 3; public static final int db2ZOSV8xOrLater = 3;
protected static final int db2UDBV82OrLater = 4; public static final int db2UDBV82OrLater = 4;
protected static final int db2ISeriesV5R4OrLater = 5; public static final int db2ISeriesV5R4OrLater = 5;
private static final String forUpdateOfClause = "FOR UPDATE OF"; private static final String forUpdate = "FOR UPDATE";
private static final String withRSClause = "WITH RS"; private static final String withRSClause = "WITH RS";
private static final String withRRClause = "WITH RR"; private static final String withRRClause = "WITH RR";
private static final String useKeepUpdateLockClause private static final String useKeepUpdateLockClause
@ -313,9 +313,12 @@ public class DB2Dictionary
* updateClause and isolationLevel hints * updateClause and isolationLevel hints
*/ */
protected String getForUpdateClause(JDBCFetchConfiguration fetch, protected String getForUpdateClause(JDBCFetchConfiguration fetch,
boolean forUpdate) { boolean isForUpdate) {
int isolationLevel; int isolationLevel;
StringBuffer forUpdateString = new StringBuffer(); // For db2UDBV81OrEarlier and db2ISeriesV5R3OrEarlier:
// "optimize for" clause appears before "for update" clause.
StringBuffer forUpdateString = new StringBuffer(
getOptimizeClause(fetch));
try { try {
// Determine the isolationLevel; the fetch // Determine the isolationLevel; the fetch
// configuration data overrides the persistence.xml value // configuration data overrides the persistence.xml value
@ -324,16 +327,15 @@ public class DB2Dictionary
else else
isolationLevel = conf.getTransactionIsolationConstant(); isolationLevel = conf.getTransactionIsolationConstant();
if (forUpdate) { if (isForUpdate) {
switch(db2ServerType) { switch(db2ServerType) {
case db2ISeriesV5R3OrEarlier: case db2ISeriesV5R3OrEarlier:
case db2UDBV81OrEarlier: case db2UDBV81OrEarlier:
if (isolationLevel == if (isolationLevel == Connection.TRANSACTION_SERIALIZABLE)
Connection.TRANSACTION_READ_UNCOMMITTED) { forUpdateString.append(" ").append(forUpdateClause);
forUpdateString.append(" ").append(withRSClause) else
.append(" ").append(forUpdateOfClause); forUpdateString.append(" ").append(forUpdate)
} else .append(" ").append(withRSClause);
forUpdateString.append(" ").append(forUpdateOfClause);
break; break;
case db2ZOSV8xOrLater: case db2ZOSV8xOrLater:
case db2UDBV82OrLater: case db2UDBV82OrLater:
@ -368,7 +370,7 @@ public class DB2Dictionary
return forUpdateString.toString(); return forUpdateString.toString();
} }
public boolean isDB2UDBV82OrLater() throws SQLException { public boolean isDB2UDBV82OrLater() {
boolean match = false; boolean match = false;
if ((databaseProductVersion.indexOf("SQL") != -1 if ((databaseProductVersion.indexOf("SQL") != -1
|| databaseProductName.indexOf("DB2/") != -1) || databaseProductName.indexOf("DB2/") != -1)
@ -377,8 +379,7 @@ public class DB2Dictionary
return match; return match;
} }
public boolean isDB2ZOSV8xOrLater() public boolean isDB2ZOSV8xOrLater() {
throws SQLException {
boolean match = false; boolean match = false;
if ((databaseProductVersion.indexOf("DSN") != -1 if ((databaseProductVersion.indexOf("DSN") != -1
|| databaseProductName.indexOf("DB2/") == -1) || databaseProductName.indexOf("DB2/") == -1)
@ -387,8 +388,7 @@ public class DB2Dictionary
return match; return match;
} }
public boolean isDB2ISeriesV5R3OrEarlier() public boolean isDB2ISeriesV5R3OrEarlier() {
throws SQLException {
boolean match = false; boolean match = false;
if (databaseProductName.indexOf("AS") != -1 if (databaseProductName.indexOf("AS") != -1
&& ((maj == 5 && min <=3) || maj < 5)) && ((maj == 5 && min <=3) || maj < 5))
@ -396,8 +396,7 @@ public class DB2Dictionary
return match; return match;
} }
public boolean isDB2ISeriesV5R4OrLater() public boolean isDB2ISeriesV5R4OrLater() {
throws SQLException {
boolean match = false; boolean match = false;
if (databaseProductName.indexOf("AS") != -1 if (databaseProductName.indexOf("AS") != -1
&& (maj >=6 || (maj == 5 && min >=4))) && (maj >=6 || (maj == 5 && min >=4)))
@ -405,7 +404,7 @@ public class DB2Dictionary
return match; return match;
} }
public boolean isDB2UDBV81OrEarlier() throws SQLException { public boolean isDB2UDBV81OrEarlier() {
boolean match = false; boolean match = false;
if ((databaseProductVersion.indexOf("SQL") != -1 if ((databaseProductVersion.indexOf("SQL") != -1
|| databaseProductName.indexOf("DB2/") != -1) && || databaseProductName.indexOf("DB2/") != -1) &&
@ -465,17 +464,16 @@ public class DB2Dictionary
} }
} }
public SQLBuffer toSelect(Select sel, boolean forUpdate, protected String getOptimizeClause(JDBCFetchConfiguration fetch) {
JDBCFetchConfiguration fetch) { if (sel != null && sel.getExpectedResultCount() > 0) {
SQLBuffer buf = super.toSelect(sel, forUpdate, fetch); StringBuffer buf = new StringBuffer();
if (sel.getExpectedResultCount() > 0) {
buf.append(" ").append(optimizeClause).append(" ") buf.append(" ").append(optimizeClause).append(" ")
.append(String.valueOf(sel.getExpectedResultCount())) .append(String.valueOf(sel.getExpectedResultCount()))
.append(" ").append(rowClause); .append(" ").append(rowClause);
return buf.toString();
} }
return buf; return "";
} }
public OpenJPAException newStoreException(String msg, SQLException[] causes, public OpenJPAException newStoreException(String msg, SQLException[] causes,

View File

@ -325,6 +325,7 @@ public class DBDictionary
private Method _setBytes = null; private Method _setBytes = null;
private Method _setString = null; private Method _setString = null;
private Method _setCharStream = null; private Method _setCharStream = null;
protected transient Select sel = null;
public DBDictionary() { public DBDictionary() {
fixedSizeTypeNameSet.addAll(Arrays.asList(new String[]{ fixedSizeTypeNameSet.addAll(Arrays.asList(new String[]{
@ -1988,6 +1989,7 @@ public class DBDictionary
*/ */
public SQLBuffer toSelect(Select sel, boolean forUpdate, public SQLBuffer toSelect(Select sel, boolean forUpdate,
JDBCFetchConfiguration fetch) { JDBCFetchConfiguration fetch) {
this.sel = sel;
sel.addJoinClassConditions(); sel.addJoinClassConditions();
boolean update = forUpdate && sel.getFromSelect() == null; boolean update = forUpdate && sel.getFromSelect() == null;
SQLBuffer select = getSelects(sel, false, update); SQLBuffer select = getSelects(sel, false, update);
@ -2198,11 +2200,11 @@ public class DBDictionary
* updateClause and isolationLevel hints * updateClause and isolationLevel hints
*/ */
protected String getForUpdateClause(JDBCFetchConfiguration fetch, protected String getForUpdateClause(JDBCFetchConfiguration fetch,
boolean forUpdate) { boolean isForUpdate) {
if (fetch != null && fetch.getIsolation() != -1) { if (fetch != null && fetch.getIsolation() != -1) {
throw new InvalidStateException(_loc.get( throw new InvalidStateException(_loc.get(
"isolation-level-config-not-supported", getClass().getName())); "isolation-level-config-not-supported", getClass().getName()));
} else if (forUpdate && !simulateLocking) { } else if (isForUpdate && !simulateLocking) {
assertSupport(supportsSelectForUpdate, "SupportsSelectForUpdate"); assertSupport(supportsSelectForUpdate, "SupportsSelectForUpdate");
return forUpdateClause; return forUpdateClause;
} else { } else {

View File

@ -81,37 +81,23 @@ public class TestIsolationLevelOverride
q.getResultList(); q.getResultList();
if (dict instanceof DB2Dictionary) { if (dict instanceof DB2Dictionary) {
if ((((DB2Dictionary) dict).getDb2ServerType() == 1) int db2server = ((DB2Dictionary) dict).getDb2ServerType();
|| (((DB2Dictionary) dict).getDb2ServerType()== 2)) { if (db2server == DB2Dictionary.db2ISeriesV5R3OrEarlier
|| db2server == DB2Dictionary.db2UDBV81OrEarlier) {
assertEquals(1, sql.size()); assertEquals(1, sql.size());
assertSQL("SELECT t0.id, t0.booleanField, t0.byteField," assertContainsSQL(" FOR UPDATE");
+ " t0.charField, t0.dateField, t0.doubleField,"
+ " t0.floatField, t0.intField, t0.longField, "
+ "t0.shortField, t0.stringField FROM "
+ "AllFieldTypes t0 WHERE \\(t0.intField = \\?\\) "
+ " FOR UPDATE OF");
} }
// it is DB2 v82 or later // it is DB2 v82 or later
else if ((((DB2Dictionary) dict).getDb2ServerType() == 3) else if (db2server == DB2Dictionary.db2ZOSV8xOrLater
|| (((DB2Dictionary) dict).getDb2ServerType() == 4)) { || db2server == DB2Dictionary.db2UDBV82OrLater) {
assertEquals(1, sql.size()); assertEquals(1, sql.size());
assertSQL("SELECT t0.id, t0.booleanField, t0.byteField," assertContainsSQL(" FOR READ ONLY WITH RR USE AND KEEP "
+ " t0.charField, t0.dateField, t0.doubleField,"
+ " t0.floatField, t0.intField, t0.longField, "
+ "t0.shortField, t0.stringField FROM "
+ "AllFieldTypes t0 WHERE \\(t0.intField = \\?\\) "
+ " FOR READ ONLY WITH RR USE AND KEEP "
+ "UPDATE LOCKS"); + "UPDATE LOCKS");
} }
else if (((DB2Dictionary) dict).getDb2ServerType() == 5) { else if (db2server == DB2Dictionary.db2ISeriesV5R4OrLater) {
assertEquals(1, sql.size()); assertEquals(1, sql.size());
assertSQL("SELECT t0.id, t0.booleanField, t0.byteField," assertContainsSQL(" FOR READ ONLY WITH RR USE AND KEEP"
+ " t0.charField, t0.dateField, t0.doubleField," + " EXCLUSIVE LOCKS");
+ " t0.floatField, t0.intField, t0.longField, "
+ "t0.shortField, t0.stringField FROM "
+ "AllFieldTypes t0 WHERE \\(t0.intField = \\?\\) "
+ " FOR READ ONLY WITH RR USE AND KEEP EXCLUSIVE "
+ "LOCKS");
} }
else { else {
fail("OpenJPA currently only supports " fail("OpenJPA currently only supports "
@ -124,37 +110,25 @@ public class TestIsolationLevelOverride
.setIsolation(IsolationLevel.SERIALIZABLE); .setIsolation(IsolationLevel.SERIALIZABLE);
em.find(AllFieldTypes.class, 0); em.find(AllFieldTypes.class, 0);
if (dict instanceof DB2Dictionary ) { if (dict instanceof DB2Dictionary ) {
if ((((DB2Dictionary) dict).getDb2ServerType() == 1) int db2server = ((DB2Dictionary) dict).getDb2ServerType();
|| (((DB2Dictionary) dict).getDb2ServerType()== 2)) { if (db2server == DB2Dictionary.db2ISeriesV5R3OrEarlier
|| db2server == DB2Dictionary.db2UDBV81OrEarlier) {
assertEquals(1, sql.size()); assertEquals(1, sql.size());
assertSQL("SELECT t0.booleanField, t0.byteField, " assertContainsSQL(" optimize for 1 row FOR UPDATE");
+ "t0.charField, t0.dateField, t0.doubleField,"
+ " t0.floatField, t0.intField, t0.longField,"
+ " t0.shortField, t0.stringField FROM "
+ "AllFieldTypes t0 WHERE t0.id = \\? "
+ " FOR UPDATE OF optimize for 1 row");
} }
// it is DB2 v82 or later // it is DB2 v82 or later
else if ((((DB2Dictionary) dict).getDb2ServerType() == 3) else if (db2server == DB2Dictionary.db2ZOSV8xOrLater
|| (((DB2Dictionary) dict).getDb2ServerType() == 4)) { || db2server == DB2Dictionary.db2UDBV82OrLater) {
assertEquals(1, sql.size()); assertEquals(1, sql.size());
assertSQL("SELECT t0.booleanField, t0.byteField, " assertContainsSQL(" optimize for 1 row"
+ "t0.charField, t0.dateField, t0.doubleField,"
+ " t0.floatField, t0.intField, t0.longField,"
+ " t0.shortField, t0.stringField FROM "
+ "AllFieldTypes t0 WHERE t0.id = \\? "
+ " FOR READ ONLY WITH RR USE AND KEEP UPDATE LOCKS" + " FOR READ ONLY WITH RR USE AND KEEP UPDATE LOCKS"
+ " optimize for 1 row"); );
} }
else if (((DB2Dictionary) dict).getDb2ServerType() == 5) { else if (db2server == DB2Dictionary.db2ISeriesV5R4OrLater) {
assertEquals(1, sql.size()); assertEquals(1, sql.size());
assertSQL("SELECT t0.booleanField, t0.byteField, " assertContainsSQL(" optimize for 1 row"
+ "t0.charField, t0.dateField, t0.doubleField,"
+ " t0.floatField, t0.intField, t0.longField,"
+ " t0.shortField, t0.stringField FROM "
+ "AllFieldTypes t0 WHERE t0.id = \\? "
+ " FOR READ ONLY WITH RR USE AND KEEP EXCLUSIVE" + " FOR READ ONLY WITH RR USE AND KEEP EXCLUSIVE"
+ " LOCKS optimize for 1 row"); + " LOCKS");
} }
else { else {
fail("OpenJPA currently only supports per-query" fail("OpenJPA currently only supports per-query"

View File

@ -81,22 +81,14 @@ public class TestOptimizeForClause
} }
if (dict instanceof DB2Dictionary) { if (dict instanceof DB2Dictionary) {
assertEquals(1, sql.size()); assertEquals(1, sql.size());
assertSQL("SELECT t0.id, t0.booleanField, t0.byteField," + assertContainsSQL(" optimize for 8 row");
" t0.charField, t0.dateField, t0.doubleField, " +
"t0.floatField, t0.intField, t0.longField, " +
"t0.shortField, t0.stringField FROM AllFieldTypes " +
"t0 WHERE \\(t0.intField = \\?\\) optimize for 8 row");
} }
} }
else { else {
em.find(AllFieldTypes.class, 0); em.find(AllFieldTypes.class, 0);
if (dict instanceof DB2Dictionary ) { if (dict instanceof DB2Dictionary ) {
assertEquals(1, sql.size()); assertEquals(1, sql.size());
assertSQL("SELECT t0.booleanField, t0.byteField, " + assertContainsSQL(" optimize for 1 row");
"t0.charField, t0.dateField, t0.doubleField, " +
"t0.floatField, t0.intField, t0.longField, " +
"t0.shortField, t0.stringField FROM AllFieldTypes" +
" t0 WHERE t0.id = \\? optimize for 1 row");
} }
} }

View File

@ -33,7 +33,14 @@ public class TestSelectForUpdateOverride
extends SQLListenerTestCase { extends SQLListenerTestCase {
public void setUp() { public void setUp() {
setUp(AllFieldTypes.class, setUp(
// "openjpa.ConnectionDriverName",
// "org.apache.commons.dbcp.BasicDataSource",
"openjpa.ConnectionProperties",
"DriverClassName=com.ibm.db2.jcc.DB2Driver,Url=jdbc:db2:demodb"
,"openjpa.jdbc.SynchronizeMappings",
AllFieldTypes.class,
"openjpa.Optimistic", "false", "openjpa.Optimistic", "false",
"openjpa.LockManager", "pessimistic", "openjpa.LockManager", "pessimistic",
"openjpa.ReadLockLevel", "none"); "openjpa.ReadLockLevel", "none");
@ -57,37 +64,24 @@ public class TestSelectForUpdateOverride
em.find(AllFieldTypes.class, 0); em.find(AllFieldTypes.class, 0);
assertEquals(1, sql.size()); assertEquals(1, sql.size());
if (dict instanceof DB2Dictionary) { if (dict instanceof DB2Dictionary) {
if ((((DB2Dictionary) dict).getDb2ServerType() == 1) int db2server = ((DB2Dictionary) dict).getDb2ServerType();
|| (((DB2Dictionary) dict).getDb2ServerType()== 2)) { if (db2server == DB2Dictionary.db2ISeriesV5R3OrEarlier
|| db2server == DB2Dictionary.db2UDBV81OrEarlier) {
assertEquals(1, sql.size()); assertEquals(1, sql.size());
assertSQL("SELECT t0.booleanField, t0.byteField, " assertContainsSQL(" optimize for 1 row FOR UPDATE");
+ "t0.charField, t0.dateField, t0.doubleField,"
+ " t0.floatField, t0.intField, t0.longField,"
+ " t0.shortField, t0.stringField FROM "
+ "AllFieldTypes t0 WHERE t0.id = \\? "
+ " FOR UPDATE OF optimize for 1 row");
} }
// it is DB2 v82 or later // it is DB2 v82 or later
else if ((((DB2Dictionary) dict).getDb2ServerType() == 3) else if (db2server == DB2Dictionary.db2ZOSV8xOrLater
|| (((DB2Dictionary) dict).getDb2ServerType() == 4)) { || db2server == DB2Dictionary.db2UDBV82OrLater) {
assertEquals(1, sql.size()); assertEquals(1, sql.size());
assertSQL("SELECT t0.booleanField, t0.byteField, " assertContainsSQL(" optimize for 1 row"
+ "t0.charField, t0.dateField, t0.doubleField," + " FOR READ ONLY WITH RS USE AND KEEP UPDATE LOCKS");
+ " t0.floatField, t0.intField, t0.longField,"
+ " t0.shortField, t0.stringField FROM "
+ "AllFieldTypes t0 WHERE t0.id = \\? "
+ " FOR READ ONLY WITH RS USE AND KEEP UPDATE LOCKS"
+ " optimize for 1 row");
} }
else if (((DB2Dictionary) dict).getDb2ServerType() == 5) { else if (db2server == DB2Dictionary.db2ISeriesV5R4OrLater) {
assertEquals(1, sql.size()); assertEquals(1, sql.size());
assertSQL("SELECT t0.booleanField, t0.byteField, " assertContainsSQL(" optimize for 1 row"
+ "t0.charField, t0.dateField, t0.doubleField,"
+ " t0.floatField, t0.intField, t0.longField,"
+ " t0.shortField, t0.stringField FROM "
+ "AllFieldTypes t0 WHERE t0.id = \\? "
+ " FOR READ ONLY WITH RS USE AND KEEP EXCLUSIVE LOCKS" + " FOR READ ONLY WITH RS USE AND KEEP EXCLUSIVE LOCKS"
+ " optimize for 1 row"); );
} }
else { else {
fail("OpenJPA currently only supports per-query isolation " fail("OpenJPA currently only supports per-query isolation "

View File

@ -78,6 +78,21 @@ public abstract class SQLListenerTestCase
+ " should not have been executed in SQL statements: " + sql); + " should not have been executed in SQL statements: " + sql);
} }
/**
* Confirm that the executed SQL String contains the specified sqlExp.
*
* @param sqlExp the SQL expression. E.g., "SELECT BADCOLUMN .*"
*/
public void assertContainsSQL(String sqlExp) {
for (String statement : sql) {
if (statement.contains(sqlExp))
return;
}
fail("Expected regular expression <" + sqlExp + "> to be"
+ " contained in SQL statements: " + sql);
}
public class Listener public class Listener
extends AbstractJDBCListener { extends AbstractJDBCListener {