Description:
The com.mysql.clusterj.ClusterJDatastoreException.getCode() method (see: https://dev.mysql.com/doc/ndbapi/en/mccj-clusterj-clusterjdatastoreexception.html#mccj-clu...) should get the code for an error, as listed at https://dev.mysql.com/doc/ndbapi/en/ndb-error-codes-single.html
However, we find that when a 630 should result, we instead see a 0. The message string still contains the error code, and we are currently checking the substring as a workaround, but it's hacky and unreliable (e.g. the string contains that number for a different reaso).
Other getCode() results appear to work, in particular we tested 159 and 121. We find that the message strings look different in the work / not work cases; log entries from local testing:
ClusterJDatastoreException getCode => 241 for message => Error in NdbJTie: returnCode -1, code 241, mysqlCode 159, status 2, classification 4, message Invalid schema object version .
ClusterJDatastoreException getCode => 0 for message => Datastore exception. Return code: -1 Error code: 630 MySQL code: 121 Status: 2 Classification: 3 Message: NdbRecordOperationImpl for table notification_metadata
ClusterJDatastoreException getCode => 893 for message => Error in NdbJTie: returnCode -1, code 893, mysqlCode 121, status 2, classification 3, message Constraint violation e.g. duplicate value in unique index ndb_tiledb/def/user_identification/external_user_idx$unique.
Though it sometimes works, we're extremely hesitant to consider any of this at all reliable. We're stuck between using a method that we have observed not functioning correctly, doing string manipulation, or some combination thereof.
How to repeat:
We're using NDB database and Jar versions 7.5.5 and mysql:mysql-connector-java:6.0.6 (https://mvnrepository.com/artifact/mysql/mysql-connector-java/6.0.6).
Given a simple schema like:
CREATE TABLE example_table (
mykey INT,
PRIMARY KEY(mykey)
) engine=ndb;
and its ClusterJ annotated interface
@PersistenceCapable(table = "example_table")
public interface IExample {
@PrimaryKey
@Column(name = "mykey")
int getMyKey();
void setMyKey(int myKey);
}
one can run the following code:
// clusterJSession setup first
final IExample example = clusterJSession.newInstance(IExample.class);
example.setMyKey(1);
clusterJSession.persist(example);
clusterJSession.persist(example); // this will cause a constraint violation when committed
try {
clusterJSession.flush();
} catch (ClusterJDatastoreException e) {
System.out.println(String.format("---\ncode: %s | mysqlCode: %s | status: %s | classification: %s\nMessage:\n%s\n---",
e.getCode(), e.getMysqlCode(), e.getStatus(), e.getClassification(), e.getMessage()));
}
and find that the first four getter methods that return ints all return 0 rather than the values they should, as are visible from the getMessage() call. In particular, I got the following output:
---
code: 0 | mysqlCode: 0 | status: 0 | classification: 0
Message:
Datastore exception. Return code: -1 Error code: 630 MySQL code: 121 Status: 2 Classification: 3 Message: NdbRecordOperationImpl for table example_table
Datastore exception. Return code: -1 Error code: 630 MySQL code: 121 Status: 2 Classification: 3 Message: NdbRecordOperationImpl for table example_table
---
but expected the first line to be "code: 630 | mysqlCode: 121 | status: 2 | classification: 3".
Thrown stack trace:
Caused by: com.mysql.clusterj.ClusterJDatastoreException: Datastore exception. Return code: -1 Error code: 630 MySQL code: 121 Status: 2 Classification: 3 Message: NdbRecordOperationImpl for table example_table
Datastore exception. Return code: -1 Error code: 630 MySQL code: 121 Status: 2 Classification: 3 Message: NdbRecordOperationImpl for table example_table
at com.mysql.clusterj.tie.ClusterTransactionImpl.performPostExecuteCallbacks(ClusterTransactionImpl.java:590)
at com.mysql.clusterj.tie.ClusterTransactionImpl.executeNoCommit(ClusterTransactionImpl.java:207)
at com.mysql.clusterj.core.SessionImpl.executeNoCommit(SessionImpl.java:1498)
at com.mysql.clusterj.core.SessionImpl.executeNoCommit(SessionImpl.java:1511)
at com.mysql.clusterj.core.SessionImpl.flush(SessionImpl.java:1424)
at com.mysql.clusterj.core.SessionImpl.flush(SessionImpl.java:1432)
at com.example.Example.repro(Example.java:30)
at com.example.Example.lambda$main$0(Example.java:16)
Suggested fix:
Remove the constructors:
ClusterJDatastoreException(String msg)
ClusterJDatastoreException(String msg, Throwable cause)
ClusterJDatastoreException(Throwable cause)
and make the relevant variables final so that they cannot accidentally not be initialized.