This is an automated email from the ASF dual-hosted git repository.
xuzifu666 pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/calcite.git
The following commit(s) were added to refs/heads/main by this push:
new 07bd13a71f [CALCITE-4985] Elasticsearch Adapter fails to execute a
prepared statement with a bind variable
07bd13a71f is described below
commit 07bd13a71f20e39331c2527953bd60543b93715b
Author: Yu Xu <[email protected]>
AuthorDate: Sat Oct 31 22:50:10 2026 +0800
[CALCITE-4985] Elasticsearch Adapter fails to execute a prepared statement
with a bind variable
---
.../adapter/elasticsearch/ElasticsearchRules.java | 10 ++++++
.../adapter/elasticsearch/PredicateAnalyzer.java | 10 ++++++
.../elasticsearch/AggregationAndSortTest.java | 39 ++++++++++++++++++++++
3 files changed, 59 insertions(+)
diff --git
a/elasticsearch/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchRules.java
b/elasticsearch/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchRules.java
index b4c78dc471..bec1b48785 100644
---
a/elasticsearch/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchRules.java
+++
b/elasticsearch/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchRules.java
@@ -38,6 +38,7 @@
import org.apache.calcite.rex.RexInputRef;
import org.apache.calcite.rex.RexLiteral;
import org.apache.calcite.rex.RexNode;
+import org.apache.calcite.rex.RexUtil;
import org.apache.calcite.rex.RexVisitorImpl;
import org.apache.calcite.sql.SqlKind;
import org.apache.calcite.sql.fun.SqlStdOperatorTable;
@@ -292,6 +293,15 @@ protected ElasticsearchFilterRule(Config config) {
super(config);
}
+ @Override public boolean matches(RelOptRuleCall call) {
+ final LogicalFilter filter = call.rel(0);
+ // Bound variables of a prepared statement are not known at planning
+ // time, so a condition that references them cannot be translated into
+ // an Elasticsearch query. Keep such filters above the adapter and let
+ // Calcite evaluate them once the parameters are bound.
+ return !RexUtil.containsDynamicParam(filter.getCondition());
+ }
+
@Override public RelNode convert(RelNode relNode) {
final LogicalFilter filter = (LogicalFilter) relNode;
final RelTraitSet traitSet = filter.getTraitSet().replace(out);
diff --git
a/elasticsearch/src/main/java/org/apache/calcite/adapter/elasticsearch/PredicateAnalyzer.java
b/elasticsearch/src/main/java/org/apache/calcite/adapter/elasticsearch/PredicateAnalyzer.java
index de51a90013..6ca9f92630 100644
---
a/elasticsearch/src/main/java/org/apache/calcite/adapter/elasticsearch/PredicateAnalyzer.java
+++
b/elasticsearch/src/main/java/org/apache/calcite/adapter/elasticsearch/PredicateAnalyzer.java
@@ -21,6 +21,7 @@
import
org.apache.calcite.adapter.elasticsearch.QueryBuilders.RangeQueryBuilder;
import org.apache.calcite.rel.type.RelDataType;
import org.apache.calcite.rex.RexCall;
+import org.apache.calcite.rex.RexDynamicParam;
import org.apache.calcite.rex.RexInputRef;
import org.apache.calcite.rex.RexLiteral;
import org.apache.calcite.rex.RexNode;
@@ -140,6 +141,15 @@ private Visitor() {
return new LiteralExpression(literal);
}
+ @Override public Expression visitDynamicParam(RexDynamicParam
dynamicParam) {
+ // Bound variables of a prepared statement have no value at planning
+ // time and thus cannot be translated into an Elasticsearch query.
+ // Normally such filters are not pushed down at all
+ // (see ElasticsearchFilterRule#matches), so this is a safety net.
+ throw new PredicateAnalyzerException(
+ "Dynamic parameter is not supported: " + dynamicParam);
+ }
+
private static boolean supportedRexCall(RexCall call) {
final SqlSyntax syntax = call.getOperator().getSyntax();
switch (syntax) {
diff --git
a/elasticsearch/src/test/java/org/apache/calcite/adapter/elasticsearch/AggregationAndSortTest.java
b/elasticsearch/src/test/java/org/apache/calcite/adapter/elasticsearch/AggregationAndSortTest.java
index 20bcef9d83..5b9b88741c 100644
---
a/elasticsearch/src/test/java/org/apache/calcite/adapter/elasticsearch/AggregationAndSortTest.java
+++
b/elasticsearch/src/test/java/org/apache/calcite/adapter/elasticsearch/AggregationAndSortTest.java
@@ -567,6 +567,45 @@ private static Connection
createConnectionWithConformance(String lex, String con
.returns("EXPR$0=3\n");
}
+ /** Test case for
+ * <a
href="https://issues.apache.org/jira/browse/CALCITE-4985">[CALCITE-4985]
+ * Elasticsearch adapter fails to prepare a statement with dynamic
+ * parameters in a filter</a>.
+ */
+ @Test void testPrepareWithDynamicParam() {
+ // Bound variables are not known at planning time, so the filter is not
+ // pushed down to Elasticsearch; it is evaluated by Calcite at runtime
+ // once the parameter is bound.
+ CalciteAssert.that()
+ .with(AggregationAndSortTest::createConnection)
+ .query("select cat1 from view where cat5 = ?")
+ .consumesPreparedStatement(p -> p.setInt(1, 1))
+ .returns("cat1=a\n");
+
+ CalciteAssert.that()
+ .with(AggregationAndSortTest::createConnection)
+ .query("select cat1 from view where cat5 = ?")
+ .consumesPreparedStatement(p -> p.setInt(1, 2))
+ .returns("cat1=b\n");
+
+ // multiple dynamic parameters
+ CalciteAssert.that()
+ .with(AggregationAndSortTest::createConnection)
+ .query("select cat1 from view where cat5 = ? and cat2 = ?")
+ .consumesPreparedStatement(p -> {
+ p.setInt(1, 1);
+ p.setString(2, "g");
+ })
+ .returns("cat1=a\n");
+
+ // parameter that matches no document
+ CalciteAssert.that()
+ .with(AggregationAndSortTest::createConnection)
+ .query("select cat1 from view where cat5 = ?")
+ .consumesPreparedStatement(p -> p.setInt(1, 42))
+ .returnsCount(0);
+ }
+
/** Test case for
* <a
href="https://issues.apache.org/jira/browse/CALCITE-4232">[CALCITE-4232]
* Elasticsearch IN Query is not supported</a>.