Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ADD: DB2 supports select .. for update queries #3315

Merged
merged 1 commit into from
Jan 30, 2024

Conversation

nPraml
Copy link
Contributor

@nPraml nPraml commented Jan 29, 2024

Hello @rob-bygrave ,

I've implemented the select - for update structure for DB2.

IBM documentation of select - for update: https://www.ibm.com/docs/en/db2/11.5?topic=statement-update-clause
nowait & skip locked are only for update queries: https://www.ibm.com/docs/en/db2/11.5?topic=statements-update#sdx-synid_nowait

Can you please take a look at it?

Kind regards
Noemi

@nPraml
Copy link
Contributor Author

nPraml commented Jan 29, 2024

TestQueryFindPagedList.test_forUpdate with db2 is failing, because of:

error code: https://www.sqlerror.de/db2_sql_error_-511_sqlstate_42829.html (you can only use for update if the select clause uses one table)

jakarta.persistence.PersistenceException: Query threw SQLException:DB2 SQL Error: SQLCODE=-511, SQLSTATE=42829, SQLERRMC=null, DRIVER=4.29.24 Bind values:[] Query was:select t0.id, t0.status, t0.order_date, t0.ship_date, t1.name, t0.cretime, t0.updtime, t0.kcustomer_id from o_order t0 join o_customer t1 on t1.id = t0.kcustomer_id fetch next 2 rows only for update
	#1: DB2 SQL Error: SQLCODE=-727, SQLSTATE=56098, SQLERRMC=2;-511;42829;, DRIVER=4.29.24
	#2: DB2 SQL Error: SQLCODE=-727, SQLSTATE=56098, SQLERRMC=2;-511;42829;, DRIVER=4.29.24

	at io.ebean.config.dbplatform.SqlCodeTranslator.translate(SqlCodeTranslator.java:85)
	at io.ebean.config.dbplatform.DatabasePlatform.translate(DatabasePlatform.java:212)
	at io.ebeaninternal.server.query.CQueryEngine.translate(CQueryEngine.java:135)
	at io.ebeaninternal.server.query.DefaultOrmQueryEngine.translate(DefaultOrmQueryEngine.java:37)
	at io.ebeaninternal.server.core.OrmQueryRequest.translate(OrmQueryRequest.java:62)
	at io.ebeaninternal.server.query.CQuery.createPersistenceException(CQuery.java:647)
	at io.ebeaninternal.server.query.CQueryEngine.findMany(CQueryEngine.java:356)
	at io.ebeaninternal.server.query.DefaultOrmQueryEngine.findMany(DefaultOrmQueryEngine.java:122)
	at io.ebeaninternal.server.core.OrmQueryRequest.findList(OrmQueryRequest.java:429)
	at io.ebeaninternal.server.core.DefaultServer.findList(DefaultServer.java:1481)
	at io.ebeaninternal.server.core.DefaultServer.findList(DefaultServer.java:1457)
	at io.ebeaninternal.server.query.LimitOffsetPagedList.getList(LimitOffsetPagedList.java:60)
	at org.tests.query.TestQueryFindPagedList.test_forUpdate(TestQueryFindPagedList.java:281)
	at java.base/java.lang.reflect.Method.invoke(Method.java:568)
	at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
	at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
Caused by: com.ibm.db2.jcc.am.SqlSyntaxErrorException: DB2 SQL Error: SQLCODE=-511, SQLSTATE=42829, SQLERRMC=null, DRIVER=4.29.24
	at com.ibm.db2.jcc.am.b7.a(b7.java:810)
	at com.ibm.db2.jcc.am.b7.a(b7.java:66)
	at com.ibm.db2.jcc.am.b7.a(b7.java:140)
	at com.ibm.db2.jcc.am.k9.c(k9.java:2844)
	at com.ibm.db2.jcc.am.k9.d(k9.java:2828)
	at com.ibm.db2.jcc.am.k9.a(k9.java:2254)
	at com.ibm.db2.jcc.am.k_.a(k_.java:8277)
	at com.ibm.db2.jcc.t4.ab.i(ab.java:204)
	at com.ibm.db2.jcc.t4.ab.b(ab.java:94)
	at com.ibm.db2.jcc.t4.p.a(p.java:32)
	at com.ibm.db2.jcc.t4.av.i(av.java:150)
	at com.ibm.db2.jcc.am.k9.al(k9.java:2223)
	at com.ibm.db2.jcc.am.k_.bq(k_.java:3763)
	at com.ibm.db2.jcc.am.k_.a(k_.java:4642)
	at com.ibm.db2.jcc.am.k_.b(k_.java:4215)
	at com.ibm.db2.jcc.am.k_.bd(k_.java:785)
	at com.ibm.db2.jcc.am.k_.executeQuery(k_.java:750)
	at io.ebean.datasource.pool.ExtendedPreparedStatement.executeQuery(ExtendedPreparedStatement.java:113)
	at io.ebeaninternal.server.query.CQuery.prepareResultSet(CQuery.java:341)
	at io.ebeaninternal.server.query.CQuery.prepareBindExecuteQueryWithOption(CQuery.java:298)
	at io.ebeaninternal.server.query.CQuery.prepareBindExecuteQuery(CQuery.java:294)
	at io.ebeaninternal.server.query.CQueryEngine.findMany(CQueryEngine.java:338)
	... 9 more

@rbygrave rbygrave added this to the 13.26.1 milestone Jan 30, 2024
@rbygrave rbygrave merged commit 9787af7 into ebean-orm:master Jan 30, 2024
1 check passed
@Override
protected String withForUpdate(String sql, Query.LockWait lockWait, Query.LockType lockType) {
// NOWAIT and SKIP LOCKED not supported with Db2
return sql + " for update";
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@rob-bygrave

I have another question about this PR.

The test TestQueryFindPagedList.test_forUpdate throws an exception because a query with join cannot be executed.

"For update" cannot be executed in these constellations: https://www.sqlerror.de/db2_sql_error_-511_sqlstate_42829.html

Would it be a viable way to check the SQL to see if any of the "forbidden" keywords appear in the command and then decide whether for update is possible? (e.g.: join, distinct, order by, group by, etc.) Or does it work if ebean passes on the exception to the DB (like the existing solution)?

Something like this:

protected String withForUpdate(String sql, Query.LockWait lockWait, Query.LockType lockType) {
    // NOWAIT and SKIP LOCKED not supported with Db2
    if (checkKeywords(sql)) {
        return sql + " for update";
    } else {
        return super.withForUpdate(sql, lockWait, lockType);
    }
}

private boolean checkKeywords(String sql) { ... }

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

viable way to check the SQL to see if any of the "forbidden" keywords appear in the command and then decide whether for update is possible?

I think that it is better to throw the exception and let the develop decide how they want to deal with the limitations of the database (it's not just DB2 with limitations here). There is io.ebean.Database.platform() to allow developers to determine the current platform and potentially perform platform specific logic if required. I realise this can be a bit of a pain so I instead expect developers to use the "lowest common working functionality" across the databases being used instead.

So yes I'm happy with the exception being thrown, its nice and simple and honest to the developer.

Do you have some scenario where you think it would be better to do a keyword check? If you think there is a better approach then I'm happy to hear about it and see where it leads us.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the feedback, we don't have a usecase that forces to check the keywords. We are also agree with the merged solution.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants