This is an automated email from the ASF dual-hosted git repository.
mbudiu 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 82cdcc78db [CALCITE-7251] SEARCH and WINDOW operations should carry
source position information
82cdcc78db is described below
commit 82cdcc78dbcb8df44827c1a9fe1126ccfffc21ac
Author: Mihai Budiu <[email protected]>
AuthorDate: Mon Oct 27 18:19:33 2025 -0700
[CALCITE-7251] SEARCH and WINDOW operations should carry source position
information
Signed-off-by: Mihai Budiu <[email protected]>
---
.../java/org/apache/calcite/rel/core/Window.java | 26 +++++++-
.../apache/calcite/rel/externalize/RelJson.java | 16 ++++-
.../apache/calcite/rel/logical/LogicalWindow.java | 1 +
.../rel/rules/ProjectWindowTransposeRule.java | 2 +-
.../calcite/rel/rules/ReduceExpressionsRule.java | 3 +-
.../java/org/apache/calcite/rex/RexBuilder.java | 35 ++++++++--
.../main/java/org/apache/calcite/rex/RexCall.java | 2 +-
.../java/org/apache/calcite/rex/RexCopier.java | 2 +-
.../main/java/org/apache/calcite/rex/RexOver.java | 34 +++++++++-
.../java/org/apache/calcite/rex/RexShuttle.java | 1 +
.../java/org/apache/calcite/rex/RexSimplify.java | 43 ++++++++----
.../main/java/org/apache/calcite/rex/RexUtil.java | 78 ++++++++++++++++------
.../apache/calcite/sql2rel/ConvertToChecked.java | 2 +-
.../apache/calcite/sql2rel/SqlToRelConverter.java | 3 +-
.../java/org/apache/calcite/tools/RelBuilder.java | 49 +++++++++-----
.../calcite/rel/externalize/RelJsonTest.java | 45 +++++++++++++
16 files changed, 276 insertions(+), 66 deletions(-)
diff --git a/core/src/main/java/org/apache/calcite/rel/core/Window.java
b/core/src/main/java/org/apache/calcite/rel/core/Window.java
index fd4770db6e..2adf54c4f3 100644
--- a/core/src/main/java/org/apache/calcite/rel/core/Window.java
+++ b/core/src/main/java/org/apache/calcite/rel/core/Window.java
@@ -41,6 +41,7 @@
import org.apache.calcite.rex.RexWindowBound;
import org.apache.calcite.rex.RexWindowExclusion;
import org.apache.calcite.sql.SqlAggFunction;
+import org.apache.calcite.sql.parser.SqlParserPos;
import org.apache.calcite.util.ImmutableBitSet;
import org.apache.calcite.util.ImmutableIntList;
import org.apache.calcite.util.Litmus;
@@ -425,7 +426,7 @@ public RexWinAggCall(
List<RexNode> operands,
int ordinal,
boolean distinct) {
- this(aggFun, type, operands, ordinal, distinct, false);
+ this(SqlParserPos.ZERO, aggFun, type, operands, ordinal, distinct,
false);
}
/**
@@ -436,6 +437,7 @@ public RexWinAggCall(
* @param operands Operands to call
* @param ordinal Ordinal within its partition
* @param distinct Eliminate duplicates before applying aggregate function
+ * @deprecated Use {@link RexWinAggCall#RexWinAggCall(SqlParserPos,
SqlAggFunction, RelDataType, List, int, boolean, boolean)}
*/
public RexWinAggCall(
SqlAggFunction aggFun,
@@ -444,7 +446,27 @@ public RexWinAggCall(
int ordinal,
boolean distinct,
boolean ignoreNulls) {
- super(type, aggFun, operands);
+ this(SqlParserPos.ZERO, aggFun, type, operands, ordinal, distinct,
ignoreNulls);
+ }
+
+ /**
+ * Creates a RexWinAggCall.
+ *
+ * @param aggFun Aggregate function
+ * @param type Result type
+ * @param operands Operands to call
+ * @param ordinal Ordinal within its partition
+ * @param distinct Eliminate duplicates before applying aggregate function
+ */
+ public RexWinAggCall(
+ SqlParserPos pos,
+ SqlAggFunction aggFun,
+ RelDataType type,
+ List<RexNode> operands,
+ int ordinal,
+ boolean distinct,
+ boolean ignoreNulls) {
+ super(pos, type, aggFun, operands);
this.ordinal = ordinal;
this.distinct = distinct;
this.ignoreNulls = ignoreNulls;
diff --git a/core/src/main/java/org/apache/calcite/rel/externalize/RelJson.java
b/core/src/main/java/org/apache/calcite/rel/externalize/RelJson.java
index 17a34411eb..2409ac6090 100644
--- a/core/src/main/java/org/apache/calcite/rel/externalize/RelJson.java
+++ b/core/src/main/java/org/apache/calcite/rel/externalize/RelJson.java
@@ -298,6 +298,15 @@ private static RexNode translateInput(RelJson relJson, int
input,
throw new RuntimeException("input field " + input + " is out of range");
}
+ public Object toJson(SqlParserPos pos) {
+ final Map<String, @Nullable Object> map = jsonBuilder().map();
+ map.put("line", pos.getLineNum());
+ map.put("column", pos.getColumnNum());
+ map.put("end_line", pos.getEndLineNum());
+ map.put("end_column", pos.getEndColumnNum());
+ return map;
+ }
+
public Object toJson(RelCollationImpl node) {
final List<Object> list = new ArrayList<>();
for (RelFieldCollation fieldCollation : node.getFieldCollations()) {
@@ -455,6 +464,8 @@ public Object toJson(AggregateCall node) {
|| value instanceof String
|| value instanceof Boolean) {
return value;
+ } else if (value instanceof SqlParserPos) {
+ return toJson((SqlParserPos) value);
} else if (value instanceof RexNode) {
return toJson((RexNode) value);
} else if (value instanceof RexWindow) {
@@ -641,6 +652,9 @@ public Object toJson(RexNode node) {
if (node instanceof RexCall) {
final RexCall call = (RexCall) node;
map = jsonBuilder().map();
+ if (call.getParserPosition() != SqlParserPos.ZERO) {
+ map.put("pos", toJson(call.getParserPosition()));
+ }
map.put("op", toJson(call.getOperator()));
final List<@Nullable Object> list = jsonBuilder().list();
for (RexNode operand : call.getOperands()) {
@@ -797,7 +811,7 @@ public RexNode toRex(RelOptCluster cluster, Object o) {
exclude = RexWindowExclusion.EXCLUDE_NO_OTHER;
}
final boolean distinct = get((Map<String, Object>) map, "distinct");
- return rexBuilder.makeOver(type, operator, rexOperands,
partitionKeys,
+ return rexBuilder.makeOver(SqlParserPos.ZERO, type, operator,
rexOperands, partitionKeys,
ImmutableList.copyOf(orderKeys),
requireNonNull(lowerBound, "lowerBound"),
requireNonNull(upperBound, "upperBound"),
diff --git
a/core/src/main/java/org/apache/calcite/rel/logical/LogicalWindow.java
b/core/src/main/java/org/apache/calcite/rel/logical/LogicalWindow.java
index 1dcff97b18..d17b8aef85 100644
--- a/core/src/main/java/org/apache/calcite/rel/logical/LogicalWindow.java
+++ b/core/src/main/java/org/apache/calcite/rel/logical/LogicalWindow.java
@@ -182,6 +182,7 @@ public static RelNode create(RelOptCluster cluster,
for (RexOver over : entry.getValue()) {
final RexWinAggCall aggCall =
new RexWinAggCall(
+ over.getParserPosition(),
over.getAggOperator(),
over.getType(),
toInputRefs(over.operands),
diff --git
a/core/src/main/java/org/apache/calcite/rel/rules/ProjectWindowTransposeRule.java
b/core/src/main/java/org/apache/calcite/rel/rules/ProjectWindowTransposeRule.java
index b2c366f14d..fac0f459ac 100644
---
a/core/src/main/java/org/apache/calcite/rel/rules/ProjectWindowTransposeRule.java
+++
b/core/src/main/java/org/apache/calcite/rel/rules/ProjectWindowTransposeRule.java
@@ -121,7 +121,7 @@ public ProjectWindowTransposeRule(RelBuilderFactory
relBuilderFactory) {
boolean[] update = {false};
final List<RexNode> clonedOperands = visitList(call.operands,
update);
if (update[0]) {
- return new Window.RexWinAggCall(
+ return new Window.RexWinAggCall(call.getParserPosition(),
(SqlAggFunction) call.getOperator(), call.getType(),
clonedOperands, aggCall.ordinal, aggCall.distinct,
aggCall.ignoreNulls);
diff --git
a/core/src/main/java/org/apache/calcite/rel/rules/ReduceExpressionsRule.java
b/core/src/main/java/org/apache/calcite/rel/rules/ReduceExpressionsRule.java
index 605f212a76..8a2e5b09b4 100644
--- a/core/src/main/java/org/apache/calcite/rel/rules/ReduceExpressionsRule.java
+++ b/core/src/main/java/org/apache/calcite/rel/rules/ReduceExpressionsRule.java
@@ -579,7 +579,8 @@ public WindowReduceExpressionsRule(Class<? extends Window>
windowClass,
final List<RexNode> expList = new ArrayList<>(aggCall.getOperands());
if (reduceExpressions(window, expList, predicates)) {
aggCall =
- new Window.RexWinAggCall((SqlAggFunction)
aggCall.getOperator(),
+ new Window.RexWinAggCall(aggCall.getParserPosition(),
+ (SqlAggFunction) aggCall.getOperator(),
aggCall.type, expList,
aggCall.ordinal, aggCall.distinct, aggCall.ignoreNulls);
reduced = true;
diff --git a/core/src/main/java/org/apache/calcite/rex/RexBuilder.java
b/core/src/main/java/org/apache/calcite/rex/RexBuilder.java
index d5492f0634..b77fe02830 100644
--- a/core/src/main/java/org/apache/calcite/rex/RexBuilder.java
+++ b/core/src/main/java/org/apache/calcite/rex/RexBuilder.java
@@ -459,8 +459,30 @@ public RexNode makeOver(
boolean nullWhenCountZero,
boolean distinct,
boolean ignoreNulls) {
- return makeOver(type, operator, exprs, partitionKeys, orderKeys,
lowerBound, upperBound,
- RexWindowExclusion.EXCLUDE_NO_OTHER, rows, allowPartial,
nullWhenCountZero, distinct,
+ return makeOver(SqlParserPos.ZERO, type, operator, exprs, partitionKeys,
orderKeys, lowerBound,
+ upperBound, RexWindowExclusion.EXCLUDE_NO_OTHER, rows, allowPartial,
nullWhenCountZero,
+ distinct, ignoreNulls);
+ }
+
+ /**
+ * Creates a call to a windowed agg.
+ */
+ public RexNode makeOver(
+ RelDataType type,
+ SqlAggFunction operator,
+ List<RexNode> exprs,
+ List<RexNode> partitionKeys,
+ ImmutableList<RexFieldCollation> orderKeys,
+ RexWindowBound lowerBound,
+ RexWindowBound upperBound,
+ RexWindowExclusion exclude,
+ boolean rows,
+ boolean allowPartial,
+ boolean nullWhenCountZero,
+ boolean distinct,
+ boolean ignoreNulls) {
+ return makeOver(SqlParserPos.ZERO, type, operator, exprs, partitionKeys,
orderKeys,
+ lowerBound, upperBound, exclude, rows, allowPartial,
nullWhenCountZero, distinct,
ignoreNulls);
}
@@ -468,6 +490,7 @@ public RexNode makeOver(
* Creates a call to a windowed agg.
*/
public RexNode makeOver(
+ SqlParserPos pos,
RelDataType type,
SqlAggFunction operator,
List<RexNode> exprs,
@@ -490,7 +513,7 @@ public RexNode makeOver(
rows,
exclude);
RexNode result =
- new RexOver(type, operator, exprs, window, distinct, ignoreNulls);
+ new RexOver(pos, type, operator, exprs, window, distinct, ignoreNulls);
// This should be correct but need time to go over test results.
// Also want to look at combing with section below.
@@ -500,12 +523,12 @@ public RexNode makeOver(
result =
makeCall(SqlStdOperatorTable.CASE,
makeCall(SqlStdOperatorTable.GREATER_THAN,
- new RexOver(bigintType, SqlStdOperatorTable.COUNT, exprs,
+ new RexOver(pos, bigintType, SqlStdOperatorTable.COUNT,
exprs,
window, distinct, ignoreNulls),
makeLiteral(BigDecimal.ZERO, bigintType,
SqlTypeName.DECIMAL)),
ensureType(type, // SUM0 is non-nullable, thus need a cast
- new RexOver(typeFactory.createTypeWithNullability(type,
false),
+ new RexOver(pos, typeFactory.createTypeWithNullability(type,
false),
operator, exprs, window, distinct, ignoreNulls),
false),
makeNullLiteral(type));
@@ -520,7 +543,7 @@ public RexNode makeOver(
SqlStdOperatorTable.CASE,
makeCall(
SqlStdOperatorTable.GREATER_THAN_OR_EQUAL,
- new RexOver(
+ new RexOver(pos,
bigintType,
SqlStdOperatorTable.COUNT,
ImmutableList.of(),
diff --git a/core/src/main/java/org/apache/calcite/rex/RexCall.java
b/core/src/main/java/org/apache/calcite/rex/RexCall.java
index ecd3cccb2c..5be1fead88 100644
--- a/core/src/main/java/org/apache/calcite/rex/RexCall.java
+++ b/core/src/main/java/org/apache/calcite/rex/RexCall.java
@@ -61,7 +61,7 @@ public class RexCall extends RexNode {
* the source position, so the backend can produce runtime error messages
* pointing to the original source position.
* For calls that are can never generate runtime failures, this field may
- * be ZERO. Note that some optimizations may "lost" position information. */
+ * be ZERO. Note that some optimizations may "lose" position information. */
public final SqlParserPos pos;
public final SqlOperator op;
public final ImmutableList<RexNode> operands;
diff --git a/core/src/main/java/org/apache/calcite/rex/RexCopier.java
b/core/src/main/java/org/apache/calcite/rex/RexCopier.java
index 1f5700e482..499d81b7b9 100644
--- a/core/src/main/java/org/apache/calcite/rex/RexCopier.java
+++ b/core/src/main/java/org/apache/calcite/rex/RexCopier.java
@@ -50,7 +50,7 @@ private RelDataType copy(RelDataType type) {
@Override public RexNode visitOver(RexOver over) {
final boolean[] update = null;
- return new RexOver(copy(over.getType()), over.getAggOperator(),
+ return new RexOver(over.getParserPosition(), copy(over.getType()),
over.getAggOperator(),
visitList(over.getOperands(), update), visitWindow(over.getWindow()),
over.isDistinct(), over.ignoreNulls());
}
diff --git a/core/src/main/java/org/apache/calcite/rex/RexOver.java
b/core/src/main/java/org/apache/calcite/rex/RexOver.java
index 63a263e6b7..0b01838b38 100644
--- a/core/src/main/java/org/apache/calcite/rex/RexOver.java
+++ b/core/src/main/java/org/apache/calcite/rex/RexOver.java
@@ -19,6 +19,7 @@
import org.apache.calcite.rel.type.RelDataType;
import org.apache.calcite.sql.SqlAggFunction;
import org.apache.calcite.sql.SqlWindow;
+import org.apache.calcite.sql.parser.SqlParserPos;
import org.apache.calcite.util.ControlFlowException;
import org.apache.calcite.util.Util;
@@ -58,6 +59,7 @@ public class RexOver extends RexCall {
* <li>window = {@link SqlWindow}(ROWS 3 PRECEDING)
* </ul>
*
+ * @param pos Parser position
* @param type Result type
* @param op Aggregate operator
* @param operands Operands list
@@ -65,19 +67,49 @@ public class RexOver extends RexCall {
* @param distinct Aggregate operator is applied on distinct elements
*/
RexOver(
+ SqlParserPos pos,
RelDataType type,
SqlAggFunction op,
List<RexNode> operands,
RexWindow window,
boolean distinct,
boolean ignoreNulls) {
- super(type, op, operands);
+ super(pos, type, op, operands);
checkArgument(op.isAggregator());
this.window = requireNonNull(window, "window");
this.distinct = distinct;
this.ignoreNulls = ignoreNulls;
}
+ /**
+ * Creates a RexOver.
+ *
+ * <p>For example, "SUM(DISTINCT x) OVER (ROWS 3 PRECEDING)" is represented
+ * as:
+ *
+ * <ul>
+ * <li>type = Integer,
+ * <li>op = {@link org.apache.calcite.sql.fun.SqlStdOperatorTable#SUM},
+ * <li>operands = { {@link RexFieldAccess}("x") }
+ * <li>window = {@link SqlWindow}(ROWS 3 PRECEDING)
+ * </ul>
+ *
+ * @param type Result type
+ * @param op Aggregate operator
+ * @param operands Operands list
+ * @param window Window specification
+ * @param distinct Aggregate operator is applied on distinct elements
+ */
+ RexOver(
+ RelDataType type,
+ SqlAggFunction op,
+ List<RexNode> operands,
+ RexWindow window,
+ boolean distinct,
+ boolean ignoreNulls) {
+ this(SqlParserPos.ZERO, type, op, operands, window, distinct, ignoreNulls);
+ }
+
//~ Methods ----------------------------------------------------------------
/**
diff --git a/core/src/main/java/org/apache/calcite/rex/RexShuttle.java
b/core/src/main/java/org/apache/calcite/rex/RexShuttle.java
index 2f6b4ec576..a3cf2b9d5e 100644
--- a/core/src/main/java/org/apache/calcite/rex/RexShuttle.java
+++ b/core/src/main/java/org/apache/calcite/rex/RexShuttle.java
@@ -50,6 +50,7 @@ public class RexShuttle implements RexVisitor<RexNode> {
// watch out for special operators like CAST and NEW where
// the type is embedded in the original call.
return new RexOver(
+ over.getParserPosition(),
over.getType(),
overAggregator,
clonedOperands,
diff --git a/core/src/main/java/org/apache/calcite/rex/RexSimplify.java
b/core/src/main/java/org/apache/calcite/rex/RexSimplify.java
index 9bacc060a1..4a9677c27d 100644
--- a/core/src/main/java/org/apache/calcite/rex/RexSimplify.java
+++ b/core/src/main/java/org/apache/calcite/rex/RexSimplify.java
@@ -28,6 +28,7 @@
import org.apache.calcite.sql.SqlKind;
import org.apache.calcite.sql.SqlOperator;
import org.apache.calcite.sql.fun.SqlStdOperatorTable;
+import org.apache.calcite.sql.parser.SqlParserPos;
import org.apache.calcite.sql.type.SqlTypeCoercionRule;
import org.apache.calcite.sql.type.SqlTypeFamily;
import org.apache.calcite.sql.type.SqlTypeName;
@@ -2380,7 +2381,7 @@ private RexNode simplifySearch(RexCall call, RexUnknownAs
unknownAs) {
RexLiteral literal = (RexLiteral) call.getOperands().get(1);
final Sarg sarg = castNonNull(literal.getValueAs(Sarg.class));
if (sarg.isAll() || sarg.isNone()) {
- RexNode rexNode = RexUtil.simpleSarg(rexBuilder, searchOperand, sarg,
unknownAs);
+ RexNode rexNode = RexUtil.simpleSarg(call.pos, rexBuilder,
searchOperand, sarg, unknownAs);
return simplify(rexNode, unknownAs);
}
// Remove null from sarg if the left-hand side is never null
@@ -2689,7 +2690,7 @@ private RexNode flattenAggregate(RexNode e) {
rexBuilder.makeWindow(ImmutableList.of(), ImmutableList.of(),
RexWindowBounds.CURRENT_ROW, RexWindowBounds.CURRENT_ROW,
true);
- return new RexOver(call.type, (SqlAggFunction) call.op,
call.operands,
+ return new RexOver(call.pos, call.type, (SqlAggFunction) call.op,
call.operands,
w, false, false);
}
return super.visitCall(call);
@@ -3208,12 +3209,12 @@ private boolean accept_(RexNode e, List<RexNode>
newTerms) {
case SEARCH:
case IS_NOT_DISTINCT_FROM:
case IS_DISTINCT_FROM:
- return accept2(((RexCall) e).operands.get(0),
+ return accept2(((RexCall) e).getParserPosition(), ((RexCall)
e).operands.get(0),
((RexCall) e).operands.get(1), e.getKind(), newTerms);
case IS_NULL:
case IS_NOT_NULL:
final RexNode arg = ((RexCall) e).operands.get(0);
- return accept1(arg, e.getKind(), newTerms);
+ return accept1(((RexCall) e).getParserPosition(), arg, e.getKind(),
newTerms);
default:
return false;
}
@@ -3231,13 +3232,13 @@ private boolean accept_(RexNode e, List<RexNode>
newTerms) {
* @param newTerms the list to which the Sarg will be added if accepted
* @return true if the operands can be converted to a Sarg, false otherwise
*/
- private boolean accept2(RexNode left, RexNode right, SqlKind kind,
+ private boolean accept2(SqlParserPos pos, RexNode left, RexNode right,
SqlKind kind,
List<RexNode> newTerms) {
if (right.isA(SqlKind.LITERAL) && RexUtil.isDeterministic(left)) {
- return accept2b(left, kind, (RexLiteral) right, newTerms);
+ return accept2b(pos, left, kind, (RexLiteral) right, newTerms);
}
if (left.isA(SqlKind.LITERAL) && RexUtil.isDeterministic(right)) {
- return accept2b(right, kind.reverse(), (RexLiteral) left, newTerms);
+ return accept2b(pos, right, kind.reverse(), (RexLiteral) left,
newTerms);
}
return false;
}
@@ -3255,10 +3256,10 @@ private static <E> E addFluent(List<? super E> list, E
e) {
* @param newTerms the list to which the Sarg is added
* @return true since the operand is always converted to a Sarg
*/
- private boolean accept1(RexNode e, SqlKind kind, List<RexNode> newTerms) {
+ private boolean accept1(SqlParserPos pos, RexNode e, SqlKind kind,
List<RexNode> newTerms) {
final RexSargBuilder b =
map.computeIfAbsent(e, e2 ->
- addFluent(newTerms, new RexSargBuilder(e2, rexBuilder, negate)));
+ addFluent(newTerms, new RexSargBuilder(pos, e2, rexBuilder,
negate)));
switch (negate ? kind.negate() : kind) {
case IS_NULL:
b.nullAs = b.nullAs.or(TRUE);
@@ -3283,7 +3284,7 @@ private boolean accept1(RexNode e, SqlKind kind,
List<RexNode> newTerms) {
* @param newTerms the list to which the Sarg is added if accepted
* @return false if the literal operand is null, true otherwise
*/
- private boolean accept2b(RexNode e, SqlKind kind,
+ private boolean accept2b(SqlParserPos pos, RexNode e, SqlKind kind,
RexLiteral literal, List<RexNode> newTerms) {
if (literal.getValue() == null) {
// Cannot include expressions 'x > NULL' in a Sarg. Comparing to a NULL
@@ -3293,7 +3294,8 @@ private boolean accept2b(RexNode e, SqlKind kind,
}
final RexSargBuilder b =
map.computeIfAbsent(e, e2 ->
- addFluent(newTerms, new RexSargBuilder(e2, rexBuilder, negate)));
+ addFluent(newTerms, new RexSargBuilder(pos, e2, rexBuilder,
negate)));
+ b.addPosition(pos);
if (negate) {
kind = kind.negateNullSafe();
}
@@ -3374,10 +3376,10 @@ static RexNode fix(RexBuilder rexBuilder, RexNode term,
if (isSmall && simpleSarg(sarg)) {
// Expand small sargs into comparisons in order to avoid plan changes
// and better readability.
- return RexUtil.sargRef(rexBuilder, sargBuilder.ref, sarg,
+ return RexUtil.sargRef(sargBuilder.pos, rexBuilder, sargBuilder.ref,
sarg,
term.getType(), unknownAs);
}
- return rexBuilder.makeCall(SqlStdOperatorTable.SEARCH, sargBuilder.ref,
+ return rexBuilder.makeCall(sargBuilder.pos,
SqlStdOperatorTable.SEARCH, sargBuilder.ref,
rexBuilder.makeSearchArgumentLiteral(sarg, term.getType()));
}
return term;
@@ -3406,6 +3408,10 @@ static RexNode fix(RexBuilder rexBuilder, RexNode term,
* {@code UNKNOWN OR FALSE OR UNKNOWN} returns {@code UNKNOWN};
* {@code FALSE OR FALSE} returns {@code FALSE}. */
private static class RexSargBuilder extends RexNode {
+ // The position is MUTABLE: it contains the SUM of the positions of
+ // all expressions that compose the search. This is not ideal, but it's
better
+ // than having no source position information at all.
+ SqlParserPos pos;
final RexNode ref;
final RexBuilder rexBuilder;
final boolean negate;
@@ -3415,7 +3421,8 @@ private static class RexSargBuilder extends RexNode {
boolean mergedSarg;
RexUnknownAs nullAs = FALSE;
- RexSargBuilder(RexNode ref, RexBuilder rexBuilder, boolean negate) {
+ RexSargBuilder(SqlParserPos pos, RexNode ref, RexBuilder rexBuilder,
boolean negate) {
+ this.pos = pos;
this.ref = requireNonNull(ref, "ref");
this.rexBuilder = requireNonNull(rexBuilder, "rexBuilder");
this.negate = negate;
@@ -3463,6 +3470,10 @@ <C extends Comparable<C>> Sarg<C> build(boolean negate) {
throw new UnsupportedOperationException();
}
+ public SqlParserPos getPos() {
+ return pos;
+ }
+
@Override public int hashCode() {
throw new UnsupportedOperationException();
}
@@ -3471,6 +3482,10 @@ void addAll() {
rangeSet.add(Range.all());
}
+ void addPosition(SqlParserPos pos) {
+ this.pos = SqlParserPos.sum(ImmutableList.of(this.pos, pos));
+ }
+
void addRange(Range<Comparable> range, RelDataType type) {
addRange(range, type, UNKNOWN);
}
diff --git a/core/src/main/java/org/apache/calcite/rex/RexUtil.java
b/core/src/main/java/org/apache/calcite/rex/RexUtil.java
index 2894b2fc7c..5904bba922 100644
--- a/core/src/main/java/org/apache/calcite/rex/RexUtil.java
+++ b/core/src/main/java/org/apache/calcite/rex/RexUtil.java
@@ -39,6 +39,7 @@
import org.apache.calcite.sql.SqlKind;
import org.apache.calcite.sql.SqlOperator;
import org.apache.calcite.sql.fun.SqlStdOperatorTable;
+import org.apache.calcite.sql.parser.SqlParserPos;
import org.apache.calcite.sql.type.SqlTypeFamily;
import org.apache.calcite.sql.type.SqlTypeName;
import org.apache.calcite.sql.type.SqlTypeUtil;
@@ -624,51 +625,70 @@ public static RexShuttle searchShuttle(RexBuilder
rexBuilder,
return new SearchExpandingShuttle(program, rexBuilder, maxComplexity);
}
- public static <C extends Comparable<C>> RexNode sargRef(RexBuilder
rexBuilder,
+ public static <C extends Comparable<C>> RexNode sargRef(SqlParserPos pos,
RexBuilder rexBuilder,
RexNode ref, Sarg<C> sarg, RelDataType type, RexUnknownAs unknownAs) {
if (sarg.isAll() || sarg.isNone()) {
- return simpleSarg(rexBuilder, ref, sarg, unknownAs);
+ return simpleSarg(pos, rexBuilder, ref, sarg, unknownAs);
}
final List<RexNode> orList = new ArrayList<>();
if (sarg.nullAs == RexUnknownAs.TRUE
&& unknownAs == RexUnknownAs.UNKNOWN) {
- orList.add(rexBuilder.makeCall(SqlStdOperatorTable.IS_NULL, ref));
+ orList.add(rexBuilder.makeCall(pos, SqlStdOperatorTable.IS_NULL, ref));
}
if (sarg.isPoints()) {
// Generate 'ref = value1 OR ... OR ref = valueN'
sarg.rangeSet.asRanges().forEach(range ->
orList.add(
- rexBuilder.makeCall(SqlStdOperatorTable.EQUALS, ref,
+ rexBuilder.makeCall(pos, SqlStdOperatorTable.EQUALS, ref,
rexBuilder.makeLiteral(range.lowerEndpoint(),
type, true, true))));
} else if (sarg.isComplementedPoints()) {
// Generate 'ref <> value1 AND ... AND ref <> valueN'
final List<RexNode> list = sarg.rangeSet.complement().asRanges().stream()
.map(range ->
- rexBuilder.makeCall(SqlStdOperatorTable.NOT_EQUALS, ref,
+ rexBuilder.makeCall(pos, SqlStdOperatorTable.NOT_EQUALS, ref,
rexBuilder.makeLiteral(range.lowerEndpoint(),
type, true, true)))
.collect(toImmutableList());
orList.add(composeConjunction(rexBuilder, list));
} else {
final RangeSets.Consumer<C> consumer =
- new RangeToRex<>(ref, orList, rexBuilder, type);
+ new RangeToRex<>(pos, ref, orList, rexBuilder, type);
RangeSets.forEach(sarg.rangeSet, consumer);
}
RexNode node = composeDisjunction(rexBuilder, orList);
if (sarg.nullAs == RexUnknownAs.FALSE
&& unknownAs == RexUnknownAs.UNKNOWN) {
node =
- rexBuilder.makeCall(SqlStdOperatorTable.AND,
- rexBuilder.makeCall(SqlStdOperatorTable.IS_NOT_NULL, ref),
+ rexBuilder.makeCall(pos, SqlStdOperatorTable.AND,
+ rexBuilder.makeCall(pos, SqlStdOperatorTable.IS_NOT_NULL, ref),
node);
}
return node;
}
- /** Expands an 'all' or 'none' sarg. */
+ /**
+ * Create a sargRef object.
+ *
+ * @deprecated Use
+ * {@link RexUtil#sargRef(SqlParserPos, RexBuilder, RexNode, Sarg,
RelDataType, RexUnknownAs)}. */
+ public static <C extends Comparable<C>> RexNode sargRef(RexBuilder
rexBuilder,
+ RexNode ref, Sarg<C> sarg, RelDataType type, RexUnknownAs unknownAs) {
+ return sargRef(SqlParserPos.ZERO, rexBuilder, ref, sarg, type, unknownAs);
+ }
+
+ /** Expands an 'all' or 'none' sarg.
+ *
+ * @deprecated Use
+ * {@link RexUtil#simpleSarg(SqlParserPos, RexBuilder, RexNode, Sarg,
RexUnknownAs)} */
public static <C extends Comparable<C>> RexNode simpleSarg(RexBuilder
rexBuilder,
RexNode ref, Sarg<C> sarg, RexUnknownAs unknownAs) {
+ return simpleSarg(SqlParserPos.ZERO, rexBuilder, ref, sarg, unknownAs);
+ }
+
+ /** Expands an 'all' or 'none' sarg. */
+ public static <C extends Comparable<C>> RexNode simpleSarg(SqlParserPos pos,
+ RexBuilder rexBuilder, RexNode ref, Sarg<C> sarg, RexUnknownAs
unknownAs) {
assert sarg.isAll() || sarg.isNone();
final RexUnknownAs nullAs =
sarg.nullAs == RexUnknownAs.UNKNOWN ? unknownAs
@@ -678,11 +698,11 @@ public static <C extends Comparable<C>> RexNode
simpleSarg(RexBuilder rexBuilder
case TRUE:
return rexBuilder.makeLiteral(true);
case FALSE:
- return rexBuilder.makeCall(SqlStdOperatorTable.IS_NOT_NULL, ref);
+ return rexBuilder.makeCall(pos, SqlStdOperatorTable.IS_NOT_NULL, ref);
case UNKNOWN:
// "x IS NOT NULL OR UNKNOWN"
- return rexBuilder.makeCall(SqlStdOperatorTable.OR,
- rexBuilder.makeCall(SqlStdOperatorTable.IS_NOT_NULL, ref),
+ return rexBuilder.makeCall(pos, SqlStdOperatorTable.OR,
+ rexBuilder.makeCall(pos, SqlStdOperatorTable.IS_NOT_NULL, ref),
rexBuilder.makeNullLiteral(
rexBuilder.typeFactory.createSqlType(SqlTypeName.BOOLEAN)));
}
@@ -690,12 +710,12 @@ public static <C extends Comparable<C>> RexNode
simpleSarg(RexBuilder rexBuilder
if (sarg.isNone()) {
switch (nullAs) {
case TRUE:
- return rexBuilder.makeCall(SqlStdOperatorTable.IS_NULL, ref);
+ return rexBuilder.makeCall(pos, SqlStdOperatorTable.IS_NULL, ref);
case FALSE:
return rexBuilder.makeLiteral(false);
case UNKNOWN:
// "CASE WHEN x IS NULL THEN UNKNOWN ELSE FALSE END", or "x <> x"
- return rexBuilder.makeCall(SqlStdOperatorTable.NOT_EQUALS, ref, ref);
+ return rexBuilder.makeCall(pos, SqlStdOperatorTable.NOT_EQUALS, ref,
ref);
}
}
throw new AssertionError();
@@ -1288,6 +1308,20 @@ public static RexNode composeConjunction(RexBuilder
rexBuilder,
return requireNonNull(e, "e");
}
+ /** Summarize the position of all the nodes as the sum of all the positions.
*/
+ static SqlParserPos summarizePosition(Iterable<? extends @Nullable RexNode>
nodes) {
+ List<SqlParserPos> validPositions = new ArrayList<>();
+ for (RexNode node : nodes) {
+ if (node instanceof RexCall) {
+ SqlParserPos position = ((RexCall) node).getParserPosition();
+ if (!position.equals(SqlParserPos.ZERO)) {
+ validPositions.add(position);
+ }
+ }
+ }
+ return SqlParserPos.sum(validPositions);
+ }
+
/**
* Converts a collection of expressions into an AND.
* If there are zero expressions, returns TRUE.
@@ -1310,7 +1344,8 @@ public static RexNode composeConjunction(RexBuilder
rexBuilder,
if (containsFalse(list)) {
return rexBuilder.makeLiteral(false);
}
- return rexBuilder.makeCall(SqlStdOperatorTable.AND, list);
+ final SqlParserPos pos = summarizePosition(nodes);
+ return rexBuilder.makeCall(pos, SqlStdOperatorTable.AND, list);
}
}
@@ -1380,7 +1415,8 @@ public static RexNode composeDisjunction(RexBuilder
rexBuilder,
if (containsTrue(list)) {
return rexBuilder.makeLiteral(true);
}
- return rexBuilder.makeCall(SqlStdOperatorTable.OR, list);
+ final SqlParserPos pos = summarizePosition(nodes);
+ return rexBuilder.makeCall(pos, SqlStdOperatorTable.OR, list);
}
}
@@ -3376,13 +3412,15 @@ public boolean anyContain(Iterable<? extends RexNode>
nodes) {
* @param <C> Value type */
private static class RangeToRex<C extends Comparable<C>>
implements RangeSets.Consumer<C> {
+ private final SqlParserPos pos;
private final List<RexNode> list;
private final RexBuilder rexBuilder;
private final RelDataType type;
private final RexNode ref;
- RangeToRex(RexNode ref, List<RexNode> list, RexBuilder rexBuilder,
+ RangeToRex(SqlParserPos pos, RexNode ref, List<RexNode> list, RexBuilder
rexBuilder,
RelDataType type) {
+ this.pos = requireNonNull(pos, "pos");
this.ref = requireNonNull(ref, "ref");
this.list = requireNonNull(list, "list");
this.rexBuilder = requireNonNull(rexBuilder, "rexBuilder");
@@ -3390,11 +3428,11 @@ private static class RangeToRex<C extends Comparable<C>>
}
private void addAnd(RexNode... nodes) {
- list.add(rexBuilder.makeCall(SqlStdOperatorTable.AND, nodes));
+ list.add(rexBuilder.makeCall(pos, SqlStdOperatorTable.AND, nodes));
}
private RexNode op(SqlOperator op, C value) {
- return rexBuilder.makeCall(op, ref,
+ return rexBuilder.makeCall(pos, op, ref,
rexBuilder.makeLiteral(value, type, true, true));
}
@@ -3485,7 +3523,7 @@ private static class SearchExpandingShuttle extends
RexShuttle {
(RexLiteral) deref(program, call.operands.get(1));
final Sarg sarg = requireNonNull(literal.getValueAs(Sarg.class),
"Sarg");
if (maxComplexity < 0 || sarg.complexity() < maxComplexity) {
- return sargRef(rexBuilder, ref, sarg, literal.getType(),
+ return sargRef(call.pos, rexBuilder, ref, sarg, literal.getType(),
RexUnknownAs.UNKNOWN);
}
// Sarg is complex (therefore useful); fall through
diff --git
a/core/src/main/java/org/apache/calcite/sql2rel/ConvertToChecked.java
b/core/src/main/java/org/apache/calcite/sql2rel/ConvertToChecked.java
index f2a44602d1..a5e658e54a 100644
--- a/core/src/main/java/org/apache/calcite/sql2rel/ConvertToChecked.java
+++ b/core/src/main/java/org/apache/calcite/sql2rel/ConvertToChecked.java
@@ -101,7 +101,7 @@ class ConvertRexToChecked extends RexShuttle {
} else {
result = call;
}
- return builder.makeCast(call.getType(), result);
+ return builder.makeCast(call.getParserPosition(), call.getType(),
result);
} else if (!SqlTypeName.EXACT_TYPES.contains(resultType)) {
// Do not rewrite operator if the type is e.g., DOUBLE or DATE
operator = call.getOperator();
diff --git
a/core/src/main/java/org/apache/calcite/sql2rel/SqlToRelConverter.java
b/core/src/main/java/org/apache/calcite/sql2rel/SqlToRelConverter.java
index 66d0ed8c72..7b1fc64bbb 100644
--- a/core/src/main/java/org/apache/calcite/sql2rel/SqlToRelConverter.java
+++ b/core/src/main/java/org/apache/calcite/sql2rel/SqlToRelConverter.java
@@ -2574,7 +2574,7 @@ private void convertUnnest(Blackboard bb, SqlCall call,
@Nullable List<String> f
Ord.forEach(nodes, (node, i) -> {
final RexNode e = bb.convertExpression(node);
final String alias = SqlValidatorUtil.alias(node, i);
- exprs.add(relBuilder.alias(e, alias));
+ exprs.add(relBuilder.alias(node.getParserPosition(), e, alias));
});
RelNode child =
(null != bb.root) ? bb.root : LogicalValues.createOneRow(cluster);
@@ -5780,6 +5780,7 @@ && isConvertedSubq(rex)) {
&& kind == SqlKind.EXISTS) {
fieldAccess =
rexBuilder.makeCall(
+ expr.getParserPosition(),
SqlStdOperatorTable.IS_NOT_NULL,
fieldAccess);
}
diff --git a/core/src/main/java/org/apache/calcite/tools/RelBuilder.java
b/core/src/main/java/org/apache/calcite/tools/RelBuilder.java
index 2aca3fc58a..44a7999727 100644
--- a/core/src/main/java/org/apache/calcite/tools/RelBuilder.java
+++ b/core/src/main/java/org/apache/calcite/tools/RelBuilder.java
@@ -741,6 +741,11 @@ public RexNode call(SqlOperator operator, RexNode...
operands) {
return call(operator, ImmutableList.copyOf(operands));
}
+ /** Creates a call to a scalar operator. */
+ public RexNode call(SqlParserPos pos, SqlOperator operator, RexNode...
operands) {
+ return call(pos, operator, ImmutableList.copyOf(operands));
+ }
+
/** Creates a call to a scalar operator. */
private RexCall call(SqlParserPos pos, SqlOperator operator, List<RexNode>
operandList) {
switch (operator.getKind()) {
@@ -1219,7 +1224,7 @@ public RexNode cast(SqlParserPos pos, RexNode expr,
SqlTypeName typeName, int pr
*
* @see #project
*/
- public RexNode alias(RexNode expr, String alias) {
+ public RexNode alias(SqlParserPos pos, RexNode expr, String alias) {
final RexNode aliasLiteral = literal(alias);
switch (expr.getKind()) {
case AS:
@@ -1231,10 +1236,14 @@ public RexNode alias(RexNode expr, String alias) {
expr = call.operands.get(0);
// strip current (incorrect) alias, and fall through
default:
- return call(SqlStdOperatorTable.AS, expr, aliasLiteral);
+ return call(pos, SqlStdOperatorTable.AS, expr, aliasLiteral);
}
}
+ public RexNode alias(RexNode expr, String alias) {
+ return alias(SqlParserPos.ZERO, expr, alias);
+ }
+
private RexNode aliasMaybe(RexNode node, @Nullable String name) {
return name == null ? node : alias(node, name);
}
@@ -4628,7 +4637,7 @@ private class AggCallImpl implements AggCallPlus {
}
@Override public OverCall over() {
- return new OverCallImpl(aggFunction, distinct, operands, ignoreNulls,
+ return new OverCallImpl(pos, aggFunction, distinct, operands,
ignoreNulls,
alias);
}
@@ -4702,7 +4711,7 @@ private class AggCallImpl2 implements AggCallPlus {
}
@Override public OverCall over() {
- return new OverCallImpl(aggregateCall.getAggregation(),
+ return new OverCallImpl(aggregateCall.getParserPosition(),
aggregateCall.getAggregation(),
aggregateCall.isDistinct(), operands, aggregateCall.ignoreNulls(),
aggregateCall.name);
}
@@ -4803,6 +4812,8 @@ private class AggCallImpl2 implements AggCallPlus {
* does the same but also assigns an column alias.
*/
public interface OverCall {
+ SqlParserPos getPosition();
+
/** Performs an action on this OverCall. */
default <R> R let(Function<OverCall, R> consumer) {
return consumer.apply(this);
@@ -4894,6 +4905,7 @@ default OverCall rangeTo(RexWindowBound upper) {
/** Implementation of {@link OverCall}. */
private class OverCallImpl implements OverCall {
+ private final SqlParserPos pos;
private final ImmutableList<RexNode> operands;
private final boolean ignoreNulls;
private final @Nullable String alias;
@@ -4908,12 +4920,13 @@ private class OverCallImpl implements OverCall {
private final SqlAggFunction op;
private final boolean distinct;
- private OverCallImpl(SqlAggFunction op, boolean distinct,
+ private OverCallImpl(SqlParserPos pos, SqlAggFunction op, boolean distinct,
ImmutableList<RexNode> operands, boolean ignoreNulls,
@Nullable String alias, ImmutableList<RexNode> partitionKeys,
ImmutableList<RexFieldCollation> sortKeys, boolean rows,
RexWindowBound lowerBound, RexWindowBound upperBound,
boolean nullWhenCountZero, boolean allowPartial, RexWindowExclusion
exclude) {
+ this.pos = pos;
this.op = op;
this.distinct = distinct;
this.operands = operands;
@@ -4930,14 +4943,18 @@ private OverCallImpl(SqlAggFunction op, boolean
distinct,
}
/** Creates an OverCallImpl with default settings. */
- OverCallImpl(SqlAggFunction op, boolean distinct,
+ OverCallImpl(SqlParserPos pos, SqlAggFunction op, boolean distinct,
ImmutableList<RexNode> operands, boolean ignoreNulls,
@Nullable String alias) {
- this(op, distinct, operands, ignoreNulls, alias, ImmutableList.of(),
+ this(pos, op, distinct, operands, ignoreNulls, alias, ImmutableList.of(),
ImmutableList.of(), true, RexWindowBounds.UNBOUNDED_PRECEDING,
RexWindowBounds.UNBOUNDED_FOLLOWING, false, true,
RexWindowExclusion.EXCLUDE_NO_OTHER);
}
+ @Override public SqlParserPos getPosition() {
+ return pos;
+ }
+
@Override public OverCall partitionBy(
Iterable<? extends RexNode> expressions) {
return partitionBy_(ImmutableList.copyOf(expressions));
@@ -4948,13 +4965,13 @@ private OverCallImpl(SqlAggFunction op, boolean
distinct,
}
private OverCall partitionBy_(ImmutableList<RexNode> partitionKeys) {
- return new OverCallImpl(op, distinct, operands, ignoreNulls, alias,
+ return new OverCallImpl(pos, op, distinct, operands, ignoreNulls, alias,
partitionKeys, sortKeys, rows, lowerBound, upperBound,
nullWhenCountZero, allowPartial, exclude);
}
private OverCall orderBy_(ImmutableList<RexFieldCollation> sortKeys) {
- return new OverCallImpl(op, distinct, operands, ignoreNulls, alias,
+ return new OverCallImpl(pos, op, distinct, operands, ignoreNulls, alias,
partitionKeys, sortKeys, rows, lowerBound, upperBound,
nullWhenCountZero, allowPartial, exclude);
}
@@ -4975,38 +4992,38 @@ private OverCall
orderBy_(ImmutableList<RexFieldCollation> sortKeys) {
@Override public OverCall rowsBetween(RexWindowBound lowerBound,
RexWindowBound upperBound) {
- return new OverCallImpl(op, distinct, operands, ignoreNulls, alias,
+ return new OverCallImpl(pos, op, distinct, operands, ignoreNulls, alias,
partitionKeys, sortKeys, true, lowerBound, upperBound,
nullWhenCountZero, allowPartial, exclude);
}
@Override public OverCall rangeBetween(RexWindowBound lowerBound,
RexWindowBound upperBound) {
- return new OverCallImpl(op, distinct, operands, ignoreNulls, alias,
+ return new OverCallImpl(pos, op, distinct, operands, ignoreNulls, alias,
partitionKeys, sortKeys, false, lowerBound, upperBound,
nullWhenCountZero, allowPartial, exclude);
}
@Override public OverCall exclude(RexWindowExclusion exclude) {
- return new OverCallImpl(op, distinct, operands, ignoreNulls, alias,
+ return new OverCallImpl(pos, op, distinct, operands, ignoreNulls, alias,
partitionKeys, sortKeys, rows, lowerBound, upperBound,
nullWhenCountZero, allowPartial, exclude);
}
@Override public OverCall allowPartial(boolean allowPartial) {
- return new OverCallImpl(op, distinct, operands, ignoreNulls, alias,
+ return new OverCallImpl(pos, op, distinct, operands, ignoreNulls, alias,
partitionKeys, sortKeys, rows, lowerBound, upperBound,
nullWhenCountZero, allowPartial, exclude);
}
@Override public OverCall nullWhenCountZero(boolean nullWhenCountZero) {
- return new OverCallImpl(op, distinct, operands, ignoreNulls, alias,
+ return new OverCallImpl(pos, op, distinct, operands, ignoreNulls, alias,
partitionKeys, sortKeys, rows, lowerBound, upperBound,
nullWhenCountZero, allowPartial, exclude);
}
@Override public RexNode as(String alias) {
- return new OverCallImpl(op, distinct, operands, ignoreNulls, alias,
+ return new OverCallImpl(pos, op, distinct, operands, ignoreNulls, alias,
partitionKeys, sortKeys, rows, lowerBound, upperBound,
nullWhenCountZero, allowPartial, exclude).toRex();
}
@@ -5021,7 +5038,7 @@ private OverCall
orderBy_(ImmutableList<RexFieldCollation> sortKeys) {
};
final RelDataType type = op.inferReturnType(bind);
final RexNode over = getRexBuilder()
- .makeOver(type, op, operands, partitionKeys, sortKeys,
+ .makeOver(pos, type, op, operands, partitionKeys, sortKeys,
lowerBound, upperBound, exclude, rows, allowPartial,
nullWhenCountZero,
distinct, ignoreNulls);
return aliasMaybe(over, alias);
diff --git
a/core/src/test/java/org/apache/calcite/rel/externalize/RelJsonTest.java
b/core/src/test/java/org/apache/calcite/rel/externalize/RelJsonTest.java
index 9bc8040b90..034365aede 100644
--- a/core/src/test/java/org/apache/calcite/rel/externalize/RelJsonTest.java
+++ b/core/src/test/java/org/apache/calcite/rel/externalize/RelJsonTest.java
@@ -104,6 +104,12 @@ plan, containsString("{\n"
+ " }\n"
+ " ],\n"
+ " \"expression\": {\n"
+ + " \"pos\": {\n"
+ + " \"line\": 1,\n"
+ + " \"column\": 32,\n"
+ + " \"end_line\": 1,\n"
+ + " \"end_column\": 36\n"
+ + " },\n"
+ " \"op\": {\n"
+ " \"name\": \">\",\n"
+ " \"kind\": \"GREATER_THAN\",\n"
@@ -129,4 +135,43 @@ plan, containsString("{\n"
+ " }\n"
+ " }"));
}
+
+ /** Test case for <a
href="https://issues.apache.org/jira/browse/CALCITE-7251">[CALCITE-7251]
+ * SEARCH and WINDOW operations should carry source position
information</a>. */
+ @Test void testSearchPosition()
+ throws SqlParseException, ValidationException, RelConversionException {
+ final String query = "SELECT val IN (1, 2, 3, 4)\n"
+ + "FROM (\n"
+ + " VALUES (10), (30), (20), (40)\n"
+ + ") AS t(val)";
+ SqlOperatorTable opTab = SqlLibraryOperatorTableFactory.INSTANCE
+ .getOperatorTable(EnumSet.of(SqlLibrary.STANDARD, SqlLibrary.SPARK));
+ final SchemaPlus rootSchema = Frameworks.createRootSchema(true);
+ final FrameworkConfig config = Frameworks.newConfigBuilder()
+ .parserConfig(SqlParser.Config.DEFAULT)
+ .operatorTable(opTab)
+ .defaultSchema(rootSchema)
+ .build();
+ Planner planner = Frameworks.getPlanner(config);
+ SqlNode n = planner.parse(query);
+ n = planner.validate(n);
+ RelNode root = planner.rel(n).project();
+ String plan =
+ RelOptUtil.dumpPlan("-- Plan", root,
+ SqlExplainFormat.JSON, SqlExplainLevel.DIGEST_ATTRIBUTES);
+ assertThat(
+ plan, containsString("\"exprs\": [\n"
+ + " {\n"
+ + " \"pos\": {\n"
+ + " \"line\": 1,\n"
+ + " \"column\": 8,\n"
+ + " \"end_line\": 1,\n"
+ + " \"end_column\": 25\n"
+ + " },\n"
+ + " \"op\": {\n"
+ + " \"name\": \"SEARCH\",\n"
+ + " \"kind\": \"SEARCH\",\n"
+ + " \"syntax\": \"INTERNAL\"\n"
+ + " },"));
+ }
}