mirror of https://github.com/apache/openjpa.git
OPENJPA-1935 Updated Informix dictionary to examine nested SQL exceptions when determining whether a generic exception is a lock exception. Added Informix JDBC profile to jdbc and locking poms.
git-svn-id: https://svn.apache.org/repos/asf/openjpa/trunk@1068472 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
85a408aca7
commit
d1ec54c691
|
@ -27,6 +27,8 @@ import java.sql.Statement;
|
|||
import java.sql.Types;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.openjpa.jdbc.identifier.DBIdentifier;
|
||||
import org.apache.openjpa.jdbc.identifier.DBIdentifier.DBIdentifierType;
|
||||
|
@ -393,12 +395,61 @@ public class InformixDictionary
|
|||
// SQL State of IX000 is a general purpose Informix error code
|
||||
// category, so only return Boolean.TRUE if we match SQL Codes
|
||||
// recoverable = Boolean.FALSE;
|
||||
if ((subtype == StoreException.LOCK && ex.getErrorCode() == -154)
|
||||
if ((subtype == StoreException.LOCK && checkNestedErrorCodes(ex, "IX000", -154))
|
||||
||(subtype == StoreException.QUERY && ex.getErrorCode() == -213)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return super.isFatalException(subtype, ex);
|
||||
}
|
||||
|
||||
/**
|
||||
* Specialized matchErrorState method for Informix. Informix exceptions are
|
||||
* typically nested multiple levels deep. Correct determination of the exception type requires
|
||||
* inspection of nested exceptions to determine the root cause. A list of Informix (IDS v10) error codes
|
||||
* can be found here:
|
||||
*
|
||||
* http://publib.boulder.ibm.com/infocenter/idshelp/v10/index.jsp?topic=/com.ibm.em.doc/errors_ids100.html
|
||||
*
|
||||
* @param errorStates classification of SQL error states by their specific nature. The keys of the
|
||||
* map represent one of the constants defined in {@link StoreException}. The value corresponding to
|
||||
* a key represent the set of SQL Error States representing specific category of database error.
|
||||
* This supplied map is sourced from <code>sql-error-state-codes.xml</xml> and filtered the
|
||||
* error states for the current database.
|
||||
*
|
||||
* @param ex original SQL Exception as raised by the database driver.
|
||||
*
|
||||
* @return A constant indicating the category of error as defined in {@link StoreException}.
|
||||
*/
|
||||
protected int matchErrorState(Map<Integer,Set<String>> errorStates, SQLException ex) {
|
||||
// Informix SQLState IX000 is a general SQLState that applies to many possible conditions
|
||||
// If the underlying cause is also an IX000 with error code:
|
||||
// -107 ISAM error: record is locked. || -154 ISAM error: Lock Timeout Expired.
|
||||
// the exception type is LOCK.
|
||||
if (checkNestedErrorCodes(ex, "IX000", -107, -154)) {
|
||||
return StoreException.LOCK;
|
||||
}
|
||||
return super.matchErrorState(errorStates, ex);
|
||||
}
|
||||
|
||||
private boolean checkNestedErrorCodes(SQLException ex, String sqlState, int...errorCodes) {
|
||||
SQLException cause = ex;
|
||||
int level = 0;
|
||||
// Query at most 5 exceptions deep to prevent infinite iteration exception loops
|
||||
// Typically, the root exception is at level 3.
|
||||
while (cause != null && level < 5) {
|
||||
String errorState = cause.getSQLState();
|
||||
if (sqlState == null || sqlState.equals(errorState)) {
|
||||
for (int ec : errorCodes) {
|
||||
if (cause.getErrorCode() == ec) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
cause = cause.getNextException();
|
||||
level++;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -627,6 +627,74 @@
|
|||
</repositories>
|
||||
</profile>
|
||||
|
||||
<!-- Profile for testing Informix with the Informix JDBC Driver -->
|
||||
<profile>
|
||||
<!--
|
||||
Example Informix profile. You can use this profile if you:
|
||||
1) have the Informix JDBC artifacts installed in a local repo and
|
||||
supply the URL:
|
||||
-Dids.maven.repo=http://my.local.repo
|
||||
2) have a copy of the Informix driver and run the following
|
||||
commands :
|
||||
mvn install:install-file -Dfile=${path to ifxjdbc.jar} \
|
||||
-DgroupId=com.informix \
|
||||
-DartifactId=informix-driver \
|
||||
-Dversion=3.70 \
|
||||
-Dpackaging=jar
|
||||
|
||||
You must also set the following properties:
|
||||
-Dopenjpa.ids.url=jdbc:informix-sqli://<HOST>:<PORT>:informixserver=<INFORMIXSERVER>;database=<DBNAME>
|
||||
-Dopenjpa.ids.username=<ids_uid>
|
||||
-Dopenjpa.ids.password=<ids_pwd>
|
||||
|
||||
Optionally, you can override the default Informix groupId,
|
||||
artifactIds and version by also supplying the following
|
||||
properties:
|
||||
-Dids.groupid=com.informix
|
||||
-Dids.driver.artifactid=informix-driver
|
||||
-Dids.version=3.70
|
||||
-->
|
||||
<id>test-ids-informix</id>
|
||||
<activation>
|
||||
<property>
|
||||
<name>test-ids-informix</name>
|
||||
</property>
|
||||
</activation>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>${ids.groupid}</groupId>
|
||||
<artifactId>${ids.driver.artifactid}</artifactId>
|
||||
<version>${ids.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<properties>
|
||||
<ids.maven.repo>http://not.a.real.repository</ids.maven.repo>
|
||||
<ids.groupid>com.informix</ids.groupid>
|
||||
<ids.driver.artifactid>informix-driver</ids.driver.artifactid>
|
||||
<ids.version>3.70</ids.version>
|
||||
<connection.driver.name>com.informix.jdbc.IfxDriver</connection.driver.name>
|
||||
<connection.url>${openjpa.ids.url}</connection.url>
|
||||
<connection.username>${openjpa.ids.username}</connection.username>
|
||||
<connection.password>${openjpa.ids.password}</connection.password>
|
||||
</properties>
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>ids.repository</id>
|
||||
<name>Informix Repository</name>
|
||||
<url>${ids.maven.repo}</url>
|
||||
<layout>default</layout>
|
||||
<snapshots>
|
||||
<enabled>false</enabled>
|
||||
</snapshots>
|
||||
<releases>
|
||||
<enabled>true</enabled>
|
||||
<checksumPolicy>ignore</checksumPolicy>
|
||||
</releases>
|
||||
</repository>
|
||||
</repositories>
|
||||
</profile>
|
||||
|
||||
<!-- Profile for testing with Oracle DB -->
|
||||
<profile>
|
||||
<!--
|
||||
|
|
|
@ -222,6 +222,16 @@ public abstract class SQLListenerTestCase
|
|||
return buf.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the last SQL executed or the empty string if the list is
|
||||
* empty.
|
||||
*/
|
||||
public String getLastSQL(List<String> list) {
|
||||
if (list != null && list.size() > 0)
|
||||
return list.get(list.size() -1);
|
||||
return "";
|
||||
}
|
||||
|
||||
public enum SQLAssertType {
|
||||
SQL, NotSQL, ContainsSQL, AllSQLInOrder, AllExactSQLInOrder,
|
||||
AllSQLAnyOrder, NoneSQLAnyOrder, AnySQLAnyOrder
|
||||
|
|
|
@ -594,6 +594,74 @@
|
|||
</repositories>
|
||||
</profile>
|
||||
|
||||
<!-- Profile for testing Informix with the Informix JDBC Driver -->
|
||||
<profile>
|
||||
<!--
|
||||
Example Informix profile. You can use this profile if you:
|
||||
1) have the Informix JDBC artifacts installed in a local repo and
|
||||
supply the URL:
|
||||
-Dids.maven.repo=http://my.local.repo
|
||||
2) have a copy of the Informix driver and run the following
|
||||
commands :
|
||||
mvn install:install-file -Dfile=${path to ifxjdbc.jar} \
|
||||
-DgroupId=com.informix \
|
||||
-DartifactId=informix-driver \
|
||||
-Dversion=3.70 \
|
||||
-Dpackaging=jar
|
||||
|
||||
You must also set the following properties:
|
||||
-Dopenjpa.ids.url=jdbc:informix-sqli://<HOST>:<PORT>:informixserver=<INFORMIXSERVER>;database=<DBNAME>
|
||||
-Dopenjpa.ids.username=<ids_uid>
|
||||
-Dopenjpa.ids.password=<ids_pwd>
|
||||
|
||||
Optionally, you can override the default Informix groupId,
|
||||
artifactIds and version by also supplying the following
|
||||
properties:
|
||||
-Dids.groupid=com.informix
|
||||
-Dids.driver.artifactid=informix-driver
|
||||
-Dids.version=3.70
|
||||
-->
|
||||
<id>test-ids-informix</id>
|
||||
<activation>
|
||||
<property>
|
||||
<name>test-ids-informix</name>
|
||||
</property>
|
||||
</activation>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>${ids.groupid}</groupId>
|
||||
<artifactId>${ids.driver.artifactid}</artifactId>
|
||||
<version>${ids.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<properties>
|
||||
<ids.maven.repo>http://not.a.real.repository</ids.maven.repo>
|
||||
<ids.groupid>com.informix</ids.groupid>
|
||||
<ids.driver.artifactid>informix-driver</ids.driver.artifactid>
|
||||
<ids.version>3.70</ids.version>
|
||||
<connection.driver.name>com.informix.jdbc.IfxDriver</connection.driver.name>
|
||||
<connection.url>${openjpa.ids.url}</connection.url>
|
||||
<connection.username>${openjpa.ids.username}</connection.username>
|
||||
<connection.password>${openjpa.ids.password}</connection.password>
|
||||
</properties>
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>ids.repository</id>
|
||||
<name>Informix Repository</name>
|
||||
<url>${ids.maven.repo}</url>
|
||||
<layout>default</layout>
|
||||
<snapshots>
|
||||
<enabled>false</enabled>
|
||||
</snapshots>
|
||||
<releases>
|
||||
<enabled>true</enabled>
|
||||
<checksumPolicy>ignore</checksumPolicy>
|
||||
</releases>
|
||||
</repository>
|
||||
</repositories>
|
||||
</profile>
|
||||
|
||||
<!-- Profile for testing with Oracle DB -->
|
||||
<profile>
|
||||
<!--
|
||||
|
|
|
@ -40,6 +40,7 @@ import org.apache.openjpa.jdbc.conf.JDBCConfiguration;
|
|||
import org.apache.openjpa.jdbc.sql.DB2Dictionary;
|
||||
import org.apache.openjpa.jdbc.sql.DBDictionary;
|
||||
import org.apache.openjpa.jdbc.sql.DerbyDictionary;
|
||||
import org.apache.openjpa.jdbc.sql.InformixDictionary;
|
||||
import org.apache.openjpa.lib.log.Log;
|
||||
import org.apache.openjpa.persistence.LockTimeoutException;
|
||||
import org.apache.openjpa.persistence.OpenJPAEntityManagerFactorySPI;
|
||||
|
@ -396,14 +397,14 @@ public class TestPessimisticLocks extends SQLListenerTestCase {
|
|||
String firstName1 = (String) q1.getSingleResult();
|
||||
//Expected sql for Derby is:
|
||||
//SELECT t0.firstName FROM Employee t0 WHERE (t0.id = CAST(? AS BIGINT)) FOR UPDATE WITH RR
|
||||
String SQL1 = toString(sql);
|
||||
String SQL1 = getLastSQL(sql);
|
||||
|
||||
// run the second time
|
||||
resetSQL();
|
||||
Query q2 = em.createQuery(jpql);
|
||||
q2.setLockMode(LockModeType.PESSIMISTIC_WRITE);
|
||||
String firstName2 = (String) q2.getSingleResult();
|
||||
String SQL2 = toString(sql);
|
||||
String SQL2 = getLastSQL(sql);
|
||||
assertEquals(SQL1, SQL2);
|
||||
em.getTransaction().commit();
|
||||
}
|
||||
|
@ -422,10 +423,18 @@ public class TestPessimisticLocks extends SQLListenerTestCase {
|
|||
// Only run this test on DB2 and Derby for now. It could cause
|
||||
// the test to hang on other platforms.
|
||||
if (!(dict instanceof DerbyDictionary ||
|
||||
dict instanceof DB2Dictionary)) {
|
||||
dict instanceof DB2Dictionary ||
|
||||
dict instanceof InformixDictionary)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Informix currently requires the lock timeout to be set directly on the dictionary
|
||||
if (dict instanceof InformixDictionary) {
|
||||
InformixDictionary ifxDict = (InformixDictionary)((JDBCConfiguration)emf.getConfiguration()).getDBDictionaryInstance();
|
||||
ifxDict.lockModeEnabled = true;
|
||||
ifxDict.lockWaitSeconds = 5;
|
||||
}
|
||||
|
||||
EntityManager em = emf.createEntityManager();
|
||||
|
||||
resetSQL();
|
||||
|
@ -459,8 +468,11 @@ public class TestPessimisticLocks extends SQLListenerTestCase {
|
|||
Map<String,Object> props = new HashMap<String,Object>();
|
||||
// This property does not have any effect on Derby for the locking
|
||||
// condition produced by this test. Instead, Derby uses the
|
||||
// lock timeout value specified in the config (pom.xml)
|
||||
props.put("javax.persistence.lock.timeout", 5000);
|
||||
// lock timeout value specified in the config (pom.xml). On Informix,
|
||||
// the dictionary level timeout (set above) will be used.
|
||||
if (!(dict instanceof InformixDictionary)) {
|
||||
props.put("javax.persistence.lock.timeout", 5000);
|
||||
}
|
||||
em.getTransaction().begin();
|
||||
getLog().trace("Main: refresh with force increment");
|
||||
em.refresh(ve, LockModeType.PESSIMISTIC_FORCE_INCREMENT, props);
|
||||
|
|
Loading…
Reference in New Issue