Skip to content

IGNITE-28352 Calcite. User defined sql function miss entries are written under the same tx lock#12936

Merged
zstan merged 8 commits intoapache:masterfrom
zstan:ignite-28352
Apr 8, 2026
Merged

IGNITE-28352 Calcite. User defined sql function miss entries are written under the same tx lock#12936
zstan merged 8 commits intoapache:masterfrom
zstan:ignite-28352

Conversation

@zstan
Copy link
Copy Markdown
Contributor

@zstan zstan commented Mar 25, 2026

No description provided.

@zstan zstan changed the title IGNITE-28352 Calcite. User defined sql fumction miss entries are written under the same tx lock IGNITE-28352 Calcite. User defined sql function miss entries are written under the same tx lock Mar 26, 2026
@zstan zstan requested review from alex-plekhanov and Copilot March 26, 2026 12:36
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR addresses tx-aware query correctness for Calcite user-defined SQL functions (UDFs) by propagating “modified entries” context across query execution threads so nested queries executed inside UDFs can see the proper transactional changes.

Changes:

  • Introduces a per-thread “modified entries” holder and wires it through ExecutionServiceImpl/ExecutionContext to reuse tx-change sets when no explicit user transaction is present.
  • Updates execution-context construction sites (prod + tests) to match the new constructor signature.
  • Adds a new integration test suite entry and a dedicated integration test covering tx-aware UDF behavior.

Reviewed changes

Copilot reviewed 10 out of 10 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/TxAwareModifiedEntriesHolder.java New ThreadLocal-based holder for tx-modified entries.
modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/ExecutionServiceImpl.java Uses the holder to supply tx entries when userTx == null; passes holder into local execution context.
modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/ExecutionContext.java Stores/detaches tx entries in the holder around task execution; accepts holder in ctor.
modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/ExchangeServiceImpl.java Updates ExecutionContext construction to new signature (adds null).
modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/planner/PlanExecutionTest.java Updates ExecutionContext construction to new signature (adds null).
modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/exec/rel/AbstractExecutionTest.java Updates ExecutionContext construction to new signature (adds null).
modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/exec/RuntimeSortedIndexTest.java Updates ExecutionContext construction to new signature (adds null).
modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/exec/LogicalRelImplementorTest.java Updates ExecutionContext construction to new signature (adds null).
modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/UserDefinedTxAwareFunctionsIntegrationTest.java New integration test for tx-aware UDF behavior (including nested queries).
modules/calcite/src/test/java/org/apache/ignite/testsuites/IntegrationTestSuite.java Registers the new integration test in the suite.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +53 to +55
/** */
@Parameterized.Parameter()
public SqlTransactionMode sqlTxMode;
Copy link

Copilot AI Mar 26, 2026

Choose a reason for hiding this comment

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

This test class redeclares @Parameterized.Parameter public SqlTransactionMode sqlTxMode, but AbstractBasicIntegrationTransactionalTest already defines the same @Parameterized.Parameter field. JUnit's Parameterized runner will see both (including inherited public fields), which can lead to parameter injection errors or ambiguous parameter indices. Remove the redeclared field and rely on the inherited sqlTxMode while keeping the custom parameters() method to restrict modes.

Suggested change
/** */
@Parameterized.Parameter()
public SqlTransactionMode sqlTxMode;

Copilot uses AI. Check for mistakes.
Commons.parametersMap(msg.parameters()),
msg.queryTransactionEntries()
msg.queryTransactionEntries(),
null
Copy link

Copilot AI Mar 26, 2026

Choose a reason for hiding this comment

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

The new TxAwareModifiedEntriesHolder parameter is passed as null when creating ExecutionContext for remote fragments. That means ExecutionContext.execute() on remote nodes will not populate the thread-local with msg.queryTransactionEntries(), so nested queries started from UDFs on those nodes won't be able to pick up tx-aware modified entries via mofiedEntriesHolder.retrieve(). Consider passing the node's mofiedEntriesHolder here instead of null so the behavior is consistent between local and remote fragment execution.

Suggested change
null
modifiedEntriesHolder

Copilot uses AI. Check for mistakes.
RootQuery<Row> qry,
MultiStepPlan plan
MultiStepPlan plan,
TxAwareModifiedEntriesHolder mofiedEntriesHolder
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

No need to pass this parameter, it's a non static method and always passed as this.modifiedEntriesHolder
Also typo in mofiedEntriesHolder

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

done

*
* @see TransactionConfiguration#isTxAwareQueriesEnabled()
*/
private TxAwareModifiedEntriesHolder modifiedEntriesHolder;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Why not use just static ThreadLocal in ExecutionContext? Looks like we don't need this variable outside of execution context.
When execution context created we can check if qryTxEntries == null and get qryTxEntries from thread local.
It's much simplier (just about 5-10 lines of code)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

done, thanks !

MultiDcQueryMappingTest.class,
TxWithExceptionalInterceptorTest.class
TxWithExceptionalInterceptorTest.class,
UserDefinedTxAwareFunctionsIntegrationTest.class
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Comma at the end of line please.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

done

/** Check tx aware udf execution results. */
@Test
public void testTxAwareUserDefinedFunc() {
assertTrue(nodeCount() > 1);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Override nodeCount() instead?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

it just a defence if someone try to change it in future (for example - startup speedup with only 1 node)

}

/** */
private static class City {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Let's reuse Employer entity from superclass.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

done


/** */
@QuerySqlFunction
public static List<List<?>> name(int id) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

nameTableFunc?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

done


/** */
@QuerySqlFunction
public List<List<?>> customTableFuncInner() {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Redundant, can be used customTableFunc

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

done


IgniteCache<Integer, Object> cache = client.cache(DEFAULT_CACHE_NAME);

/*The pool size should be greater than the maximum number of concurrent queries initiated by UDFs*/
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Spaces before/after comment and point at the end.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

done

Comment on lines +135 to +137
client.getOrCreateCache(cacheConfig());

IgniteCache<Integer, Object> cache = client.cache(DEFAULT_CACHE_NAME);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

IgniteCache<Integer, Object> cache = client.getOrCreateCache(cacheConfig());

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

done

Comment on lines +76 to +80
client.getOrCreateCache(cacheConfig());

List<List<Object>> refResults = new ArrayList<>();

IgniteCache<Integer, Object> cache = client.cache(DEFAULT_CACHE_NAME);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

IgniteCache<Integer, Object> cache = client.getOrCreateCache(cacheConfig());

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

done

@zstan zstan requested a review from alex-plekhanov April 3, 2026 09:17
@sonarqubecloud
Copy link
Copy Markdown

sonarqubecloud bot commented Apr 7, 2026

@zstan zstan merged commit 15f2122 into apache:master Apr 8, 2026
10 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants