Skip to content

[CALCITE-7592] Add expression support for FETCH#5004

Open
tkalkirill wants to merge 8 commits into
apache:mainfrom
tkalkirill:calcite-7592
Open

[CALCITE-7592] Add expression support for FETCH#5004
tkalkirill wants to merge 8 commits into
apache:mainfrom
tkalkirill:calcite-7592

Conversation

@tkalkirill

@tkalkirill tkalkirill commented Jun 7, 2026

Copy link
Copy Markdown

Jira Link

CALCITE-7592

Changes Proposed

This PR introduces support for arithmetic and scalar expressions for "FETCH", enabling queries to execute similarly to those in other popular DBMSs (PostgreSQL, Oracle, H2, and MS SQL):

SELECT * FROM person FETCH NEXT (? + 1) ROWS ONLY;
SELECT * FROM person FETCH NEXT (udf(?)) ROWS ONLY;
SELECT * FROM person FETCH NEXT (1 + NVL(?, 10000)) ROWS ONLY.

@xuzifu666

Copy link
Copy Markdown
Member

Please refer to other PR and modify the title accordingly. Also, please fix the CI bug to facilitate future reviews. @tkalkirill

@tkalkirill tkalkirill changed the title  CALCITE-7592 Add expression support for FETCH [CALCITE-7592] Add expression support for FETCH Jun 8, 2026
@xiedeyantu

Copy link
Copy Markdown
Member

Could you add some test cases to your Quidem test and compare the results with those from pgsql (e.g., using https://onecompiler.com/postgresql)?

@tkalkirill

Copy link
Copy Markdown
Author

@xuzifu666 I think I've fixed everything.
@xiedeyantu Added "fetch.iq" and compared it with (https://onecompiler.com/postgresql); it matches.

Could one of you take a look at the PR, or do you have any recommendations on who I should send it to for review?

@xiedeyantu xiedeyantu left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This PR does touch quite a few files. I took a quick look through it, and the tests seem good overall. I've left some comments for now—could you take a look?

new RexVisitorImpl<Void>(true) {
@Override public Void visitCall(RexCall call) {
if (call.getOperator().isDynamicFunction()) {
throw Util.FoundOne.NULL;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I see many checks for whether parameters are dynamic. Is it possible to converge to a single utility method?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Good point. There are two identical recursive checks for dynamic parameters. I will move this logic to RexUtil.containsDynamicParam and reuse it.
The remaining instanceof RexDynamicParam checks are intentionally local because they distinguish a dynamic parameter as the root node from a parameter nested inside an expression, so replacing all of them with one predicate would change their semantics.

The remaining checks have different semantics: some detect a root dynamic parameter, while others search recursively or decide whether an expression can be evaluated during planning. Converging all of them would require a broader API and changes outside the scope of CALCITE-7592. I suggest keeping those checks explicit and considering a broader cleanup separately.

"select * from (values (1), (2), (3), (4)) as t(x)\n";
checkPreparedFetch(connection, values + "fetch next (?) rows only",
2, "X=1\nX=2\n");
checkPreparedFetch(connection, values + "fetch next (? + 1) rows only",

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Will it support ? + abs(2) and fetch next (select 2) or fetch next (select max(col)) from t)?

@xuzifu666 xuzifu666 Jun 10, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I also have questions about this; perhaps should add some details on how to address this.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Sorry for the delayed reply; I was away on a short vacation.

I tested the queries in the current implementation:

select * from person fetch next (? + abs(2)) rows only; --will **work**.
select * from person fetch next ? + abs(2) rows only; --will **fail** with a parsing error.
--
select * from person fetch next (select 2) rows only; --will **fail** with a parsing error.
select * from person fetch next (select ?) rows only; --will **fail** with a parsing error.
select * from person fetch next (select max(id) from peson) rows only; --will **fail** with a parsing error.

I tested queries containing SELECT subqueries in Postgres, and they all executed successfully. In my current change, I proposed supporting only arithmetic operations and scalar expressions—excluding subqueries, even scalar ones.

I suggest adding support for them as a separate task later on, should the need arise.

I am currently adding tests for the aforementioned queries.

@xiedeyantu @xuzifu666 What do you think?

@sonarqubecloud

Copy link
Copy Markdown

try {
result = decimal.longValueExact();
} catch (ArithmeticException e) {
throw new IllegalArgumentException("FETCH value " + value

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

In Calcite, the LIMIT and OFFSET values ​​are not restricted to the LONG type; they can be arbitrarily large. As this link, we will use BigDecimal. Is the restriction to the long type here due to issues with Enumerable execution? The int above has the same issue.

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