ADDITIONAL SYSTEM INFORMATION :
H2 database used to reproduce the problem. Maven dependency provided with the program.
A DESCRIPTION OF THE PROBLEM :
rs.acceptChanges gets an exception.
The top level problem is the generated SQL
"SELECT SWINGSET_BASE_TEST_PK, SS_TEXT_FIELD FROM
FROM SUPPLIERS_AND_PARTS.PUBLIC.swingset_base_test_data; WHERE
SWINGSET_BASE_TEST_PK = ? AND SS_TEXT_FIELD = ? "
NOTE the ";" between the table name and the "WHERE".
This statement is built by `CachedRowSetWriter.updateOriginalRow()`
pstmt = con.prepareStatement(selectCmd + updateWhere,
ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_READ_ONLY);
around line 493.
The bug could be in `CachedeRowSetImpl.buildTableName()`, it returns a name
with an appended ";".
But I wonder about `CachedeRowSetImpl.getTableName`. If `setTableName()` has not
been called, then shouldn't the metadata be used if available.
Maybe the result of parsing the SELECT statement should be the last resort.
Note that the javadoc for `CachedeRowSetImpl.getTableName` references
"See Also: ResultSetMetaData.getTableName(int)"
Also, in `CachedRowSetWriter.initSQLStatements` plays a part.
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Compile/run the provided program.
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
No exception. database updated successfully. Test program outputs "### xxx" at completion.
Note that this problem does not occur if a JdbcRowSet is used (try it in the example by setting
"use_crs" to false.
ACTUAL -
Exceptions.
---------- BEGIN SOURCE ----------
/*****************************************
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>2.2.224</version>
</dependency>
*****************************************/
package play.syncerror;
import java.io.StringReader;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import javax.sql.RowSet;
import javax.sql.rowset.CachedRowSet;
import javax.sql.rowset.RowSetProvider;
import org.h2.tools.RunScript;
public class SyncError
{
static String initialDB =
"""
CREATE SEQUENCE IF NOT EXISTS swingset_base_test_seq START WITH 1000;
CREATE TABLE IF NOT EXISTS swingset_base_test_data
(
swingset_base_test_pk INTEGER DEFAULT nextval('swingset_base_test_seq') NOT NULL PRIMARY KEY,
ss_text_field VARCHAR(100)
);
MERGE INTO swingset_base_test_data VALUES (1,'This is TextField 1') ;
MERGE INTO swingset_base_test_data VALUES (2,'This is TextField 2') ;
""";
public static void main(String[] args)
throws ClassNotFoundException, SQLException
{
String url = "jdbc:h2:mem:suppliers_and_parts;TRACE_LEVEL_SYSTEM_OUT=3";
//String url = "jdbc:h2:mem:suppliers_and_parts";
boolean use_crs = true;
Class.forName("org.h2.Driver");
Connection conn = DriverManager.getConnection(url);
RunScript.execute(conn, new StringReader(initialDB));
System.err.printf("### use_crs: %s\n", use_crs);
RowSet rs;
if (use_crs) {
rs = RowSetProvider.newFactory().createCachedRowSet();
} else {
rs = RowSetProvider.newFactory().createJdbcRowSet(); // NO PROBLEM
}
rs.setUrl(url);
//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> | <<<<<<<<<<<<<
rs.setCommand("SELECT * FROM swingset_base_test_data;"); // NOTE THE TRAILING ';'
rs.execute();
rs.first();
Object o = rs.getObject(2);
System.err.printf("### %s\n", o);
rs.updateString(2, "xxx");
rs.updateRow();
if (use_crs)
((CachedRowSet)rs).acceptChanges(); // <<<<<<<<<<<<<<<< EXCEPTION
rs.execute();
rs.first();
o = rs.getObject(2);
System.err.printf("### %s\n", o);
}
}
---------- END SOURCE ----------
CUSTOMER SUBMITTED WORKAROUND :
Two workarounds
- Remove the trailing semicolon from the select statement.
- Use sr.setTableName().
FREQUENCY : always
H2 database used to reproduce the problem. Maven dependency provided with the program.
A DESCRIPTION OF THE PROBLEM :
rs.acceptChanges gets an exception.
The top level problem is the generated SQL
"SELECT SWINGSET_BASE_TEST_PK, SS_TEXT_FIELD FROM
FROM SUPPLIERS_AND_PARTS.PUBLIC.swingset_base_test_data; WHERE
SWINGSET_BASE_TEST_PK = ? AND SS_TEXT_FIELD = ? "
NOTE the ";" between the table name and the "WHERE".
This statement is built by `CachedRowSetWriter.updateOriginalRow()`
pstmt = con.prepareStatement(selectCmd + updateWhere,
ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_READ_ONLY);
around line 493.
The bug could be in `CachedeRowSetImpl.buildTableName()`, it returns a name
with an appended ";".
But I wonder about `CachedeRowSetImpl.getTableName`. If `setTableName()` has not
been called, then shouldn't the metadata be used if available.
Maybe the result of parsing the SELECT statement should be the last resort.
Note that the javadoc for `CachedeRowSetImpl.getTableName` references
"See Also: ResultSetMetaData.getTableName(int)"
Also, in `CachedRowSetWriter.initSQLStatements` plays a part.
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Compile/run the provided program.
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
No exception. database updated successfully. Test program outputs "### xxx" at completion.
Note that this problem does not occur if a JdbcRowSet is used (try it in the example by setting
"use_crs" to false.
ACTUAL -
Exceptions.
---------- BEGIN SOURCE ----------
/*****************************************
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>2.2.224</version>
</dependency>
*****************************************/
package play.syncerror;
import java.io.StringReader;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import javax.sql.RowSet;
import javax.sql.rowset.CachedRowSet;
import javax.sql.rowset.RowSetProvider;
import org.h2.tools.RunScript;
public class SyncError
{
static String initialDB =
"""
CREATE SEQUENCE IF NOT EXISTS swingset_base_test_seq START WITH 1000;
CREATE TABLE IF NOT EXISTS swingset_base_test_data
(
swingset_base_test_pk INTEGER DEFAULT nextval('swingset_base_test_seq') NOT NULL PRIMARY KEY,
ss_text_field VARCHAR(100)
);
MERGE INTO swingset_base_test_data VALUES (1,'This is TextField 1') ;
MERGE INTO swingset_base_test_data VALUES (2,'This is TextField 2') ;
""";
public static void main(String[] args)
throws ClassNotFoundException, SQLException
{
String url = "jdbc:h2:mem:suppliers_and_parts;TRACE_LEVEL_SYSTEM_OUT=3";
//String url = "jdbc:h2:mem:suppliers_and_parts";
boolean use_crs = true;
Class.forName("org.h2.Driver");
Connection conn = DriverManager.getConnection(url);
RunScript.execute(conn, new StringReader(initialDB));
System.err.printf("### use_crs: %s\n", use_crs);
RowSet rs;
if (use_crs) {
rs = RowSetProvider.newFactory().createCachedRowSet();
} else {
rs = RowSetProvider.newFactory().createJdbcRowSet(); // NO PROBLEM
}
rs.setUrl(url);
//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> | <<<<<<<<<<<<<
rs.setCommand("SELECT * FROM swingset_base_test_data;"); // NOTE THE TRAILING ';'
rs.execute();
rs.first();
Object o = rs.getObject(2);
System.err.printf("### %s\n", o);
rs.updateString(2, "xxx");
rs.updateRow();
if (use_crs)
((CachedRowSet)rs).acceptChanges(); // <<<<<<<<<<<<<<<< EXCEPTION
rs.execute();
rs.first();
o = rs.getObject(2);
System.err.printf("### %s\n", o);
}
}
---------- END SOURCE ----------
CUSTOMER SUBMITTED WORKAROUND :
Two workarounds
- Remove the trailing semicolon from the select statement.
- Use sr.setTableName().
FREQUENCY : always