You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@calcite.apache.org by mb...@apache.org on 2024/09/20 20:39:35 UTC

(calcite) branch main updated: [CALCITE-6071] RexCall should carry source position information for runtime error reporting

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 98b252b36c [CALCITE-6071] RexCall should carry source position information for runtime error reporting
98b252b36c is described below

commit 98b252b36c84c4715c5bbe8c8a65355c103b3f9e
Author: Mihai Budiu <mb...@feldera.com>
AuthorDate: Fri Sep 20 10:27:45 2024 -0700

    [CALCITE-6071] RexCall should carry source position information for runtime error reporting
    
    Signed-off-by: Mihai Budiu <mb...@feldera.com>
---
 .../calcite/adapter/enumerable/RexImpTable.java    |   2 +-
 .../apache/calcite/plan/SubstitutionVisitor.java   |  15 +-
 .../org/apache/calcite/rel/core/AggregateCall.java |  75 +++++--
 .../java/org/apache/calcite/rel/core/Window.java   |   2 +-
 .../apache/calcite/rel/rel2sql/SqlImplementor.java |   2 +-
 .../rel/rules/AggregateCaseToFilterRule.java       |  11 +-
 .../AggregateExpandDistinctAggregatesRule.java     |  17 +-
 .../rules/AggregateExpandWithinDistinctRule.java   |   6 +-
 .../rel/rules/AggregateFilterTransposeRule.java    |   3 +-
 .../rel/rules/AggregateReduceFunctionsRule.java    | 120 +++++-----
 .../calcite/rel/rules/AggregateRemoveRule.java     |   5 +-
 .../calcite/rel/rules/AggregateStarTableRule.java  |   4 +-
 .../rel/rules/AggregateUnionTransposeRule.java     |   2 +-
 .../rel/rules/ProjectAggregateMergeRule.java       |   2 +-
 .../calcite/rel/rules/ReduceDecimalsRule.java      | 187 +++++++++-------
 .../materialize/MaterializedViewAggregateRule.java |   4 +-
 .../java/org/apache/calcite/rex/RexBuilder.java    | 241 +++++++++++++++++++--
 .../main/java/org/apache/calcite/rex/RexCall.java  |  26 ++-
 .../java/org/apache/calcite/rex/RexCopier.java     |   2 +-
 .../java/org/apache/calcite/rex/RexSimplify.java   |  57 ++---
 .../main/java/org/apache/calcite/rex/RexUtil.java  |   9 +-
 .../calcite/sql/SqlSplittableAggFunction.java      |  23 +-
 .../org/apache/calcite/sql2rel/AggConverter.java   |   1 +
 .../apache/calcite/sql2rel/RelDecorrelator.java    |   1 +
 .../sql2rel/RelStructuredTypeFlattener.java        |   8 +-
 .../apache/calcite/sql2rel/SqlToRelConverter.java  |  40 ++--
 .../calcite/sql2rel/StandardConvertletTable.java   | 220 ++++++++++---------
 .../java/org/apache/calcite/tools/RelBuilder.java  | 145 +++++++++++--
 .../materialize/NormalizationTrimFieldTest.java    |   2 +-
 .../apache/calcite/test/fuzzer/RexShrinker.java    |   2 +-
 30 files changed, 838 insertions(+), 396 deletions(-)

diff --git a/core/src/main/java/org/apache/calcite/adapter/enumerable/RexImpTable.java b/core/src/main/java/org/apache/calcite/adapter/enumerable/RexImpTable.java
index e8cb71dfaf..1fa5cbff68 100644
--- a/core/src/main/java/org/apache/calcite/adapter/enumerable/RexImpTable.java
+++ b/core/src/main/java/org/apache/calcite/adapter/enumerable/RexImpTable.java
@@ -4781,7 +4781,7 @@ public class RexImpTable {
       final ParameterExpression lambdaArg =
           Expressions.parameter(translator.typeFactory.getJavaClass(rightComponentType), "el");
       final RexCall binaryImplementorRexCall =
-          (RexCall) translator.builder.makeCall(binaryOperator, leftRex,
+          (RexCall) translator.builder.makeCall(call.getParserPosition(), binaryOperator, leftRex,
               translator.builder.makeDynamicParam(rightComponentType, 0));
       final List<RexToLixTranslator.Result> binaryImplementorArgs =
           ImmutableList.of(
diff --git a/core/src/main/java/org/apache/calcite/plan/SubstitutionVisitor.java b/core/src/main/java/org/apache/calcite/plan/SubstitutionVisitor.java
index e8e4a46541..20d4f75dc9 100644
--- a/core/src/main/java/org/apache/calcite/plan/SubstitutionVisitor.java
+++ b/core/src/main/java/org/apache/calcite/plan/SubstitutionVisitor.java
@@ -350,7 +350,7 @@ public class SubstitutionVisitor {
       if (newOperands.size() < 2) {
         return newOperands.values().iterator().next();
       }
-      return rexBuilder.makeCall(call.getOperator(),
+      return rexBuilder.makeCall(call.getParserPosition(), call.getOperator(),
           ImmutableList.copyOf(newOperands.values()));
     }
     case EQUALS:
@@ -362,7 +362,8 @@ public class SubstitutionVisitor {
       RexCall call = (RexCall) condition;
       RexNode left = canonizeNode(rexBuilder, call.getOperands().get(0));
       RexNode right = canonizeNode(rexBuilder, call.getOperands().get(1));
-      call = (RexCall) rexBuilder.makeCall(call.getOperator(), left, right);
+      call =
+          (RexCall) rexBuilder.makeCall(call.getParserPosition(), call.getOperator(), left, right);
 
       if (left.toString().compareTo(right.toString()) <= 0) {
         return call;
@@ -384,10 +385,11 @@ public class SubstitutionVisitor {
       RexNode right = canonizeNode(rexBuilder, call.getOperands().get(1));
 
       if (left.toString().compareTo(right.toString()) <= 0) {
-        return rexBuilder.makeCall(call.getOperator(), left, right);
+        return rexBuilder.makeCall(call.getParserPosition(), call.getOperator(), left, right);
       }
 
-      RexNode newCall = rexBuilder.makeCall(call.getOperator(), right, left);
+      RexNode newCall =
+          rexBuilder.makeCall(call.getParserPosition(), call.getOperator(), right, left);
       // new call should not be used if its inferred type is not same as old
       if (!newCall.getType().equals(call.getType())) {
         return call;
@@ -1973,7 +1975,8 @@ public class SubstitutionVisitor {
             final SqlAggFunction aggFunction = aggregateCall.getAggregation().getRollup();
             if (aggFunction != null) {
               newAggCall =
-                  AggregateCall.create(aggFunction, aggregateCall.isDistinct(),
+                  AggregateCall.create(aggregateCall.getParserPosition(),
+                      aggFunction, aggregateCall.isDistinct(),
                       aggregateCall.isApproximate(), aggregateCall.ignoreNulls(),
                       aggregateCall.rexList,
                       ImmutableList.of(target.groupSet.cardinality() + i), -1,
@@ -2044,7 +2047,7 @@ public class SubstitutionVisitor {
     if (!isAllowBuild) {
       return null;
     }
-    return AggregateCall.create(aggregation,
+    return AggregateCall.create(queryAggCall.getParserPosition(), aggregation,
         queryAggCall.isDistinct(), queryAggCall.isApproximate(),
         queryAggCall.ignoreNulls(), queryAggCall.rexList,
         newArgList, -1, queryAggCall.distinctKeys,
diff --git a/core/src/main/java/org/apache/calcite/rel/core/AggregateCall.java b/core/src/main/java/org/apache/calcite/rel/core/AggregateCall.java
index 687e41c7e7..ddd91b1bed 100644
--- a/core/src/main/java/org/apache/calcite/rel/core/AggregateCall.java
+++ b/core/src/main/java/org/apache/calcite/rel/core/AggregateCall.java
@@ -26,6 +26,7 @@ import org.apache.calcite.rex.RexNode;
 import org.apache.calcite.rex.RexUtil;
 import org.apache.calcite.sql.SqlAggFunction;
 import org.apache.calcite.sql.SqlKind;
+import org.apache.calcite.sql.parser.SqlParserPos;
 import org.apache.calcite.sql.type.SqlTypeUtil;
 import org.apache.calcite.util.ImmutableBitSet;
 import org.apache.calcite.util.Optionality;
@@ -50,6 +51,14 @@ import static java.util.Objects.requireNonNull;
 public class AggregateCall {
   //~ Instance fields --------------------------------------------------------
 
+  /**
+   * Some aggregate calls may produce runtime errors.  For these
+   * we need to keep around the original source position information
+   * so that the runtime can produce error messages pointing to
+   * the offending source operation.  For "safe" aggregations
+   * this field may be ZERO.
+   */
+  private final SqlParserPos pos;
   private final SqlAggFunction aggFunction;
 
   private final boolean distinct;
@@ -84,7 +93,7 @@ public class AggregateCall {
       List<Integer> argList,
       RelDataType type,
       String name) {
-    this(aggFunction, distinct, false, false,
+    this(SqlParserPos.ZERO, aggFunction, distinct, false, false,
         ImmutableList.of(), argList, -1, null,
         RelCollations.EMPTY, type, name);
   }
@@ -92,6 +101,9 @@ public class AggregateCall {
   /**
    * Creates an AggregateCall.
    *
+   * @param pos         Source position for this aggregate.
+   *                    Ideally it should only be ZERO when the aggregate
+   *                    can never fail at runtime.
    * @param aggFunction Aggregate function
    * @param distinct    Whether distinct
    * @param approximate Whether approximate
@@ -106,11 +118,12 @@ public class AggregateCall {
    * @param type        Result type
    * @param name        Name (may be null)
    */
-  private AggregateCall(SqlAggFunction aggFunction, boolean distinct,
+  private AggregateCall(SqlParserPos pos, SqlAggFunction aggFunction, boolean distinct,
       boolean approximate, boolean ignoreNulls,
       List<RexNode> rexList, List<Integer> argList,
       int filterArg, @Nullable ImmutableBitSet distinctKeys,
       RelCollation collation, RelDataType type, @Nullable String name) {
+    this.pos = pos;
     this.type = requireNonNull(type, "type");
     this.name = name;
     this.aggFunction = requireNonNull(aggFunction, "aggFunction");
@@ -187,6 +200,17 @@ public class AggregateCall {
       @Nullable ImmutableBitSet distinctKeys, RelCollation collation,
       int groupCount,
       RelNode input, @Nullable RelDataType type, @Nullable String name) {
+    return create(SqlParserPos.ZERO, aggFunction, distinct, approximate,
+        ignoreNulls, rexList, argList, filterArg, distinctKeys, collation, groupCount,
+        input, type, name);
+  }
+
+  public static AggregateCall create(SqlParserPos pos, SqlAggFunction aggFunction,
+      boolean distinct, boolean approximate, boolean ignoreNulls,
+      List<RexNode> rexList, List<Integer> argList, int filterArg,
+      @Nullable ImmutableBitSet distinctKeys, RelCollation collation,
+      int groupCount,
+      RelNode input, @Nullable RelDataType type, @Nullable String name) {
     if (type == null) {
       final RelDataTypeFactory typeFactory =
           input.getCluster().getTypeFactory();
@@ -198,7 +222,7 @@ public class AggregateCall {
               types, groupCount, filterArg >= 0);
       type = aggFunction.inferReturnType(callBinding);
     }
-    return create(aggFunction, distinct, approximate, ignoreNulls,
+    return create(pos, aggFunction, distinct, approximate, ignoreNulls,
         rexList, argList, filterArg, distinctKeys, collation, type, name);
   }
 
@@ -245,9 +269,18 @@ public class AggregateCall {
       List<RexNode> rexList, List<Integer> argList, int filterArg,
       @Nullable ImmutableBitSet distinctKeys, RelCollation collation,
       RelDataType type, @Nullable String name) {
+    return create(SqlParserPos.ZERO, aggFunction, distinct, approximate,
+        ignoreNulls, rexList, argList, filterArg, distinctKeys, collation, type, name);
+  }
+
+  public static AggregateCall create(SqlParserPos pos, SqlAggFunction aggFunction,
+      boolean distinct, boolean approximate, boolean ignoreNulls,
+      List<RexNode> rexList, List<Integer> argList, int filterArg,
+      @Nullable ImmutableBitSet distinctKeys, RelCollation collation,
+      RelDataType type, @Nullable String name) {
     final boolean distinct2 = distinct
         && (aggFunction.getDistinctOptionality() != Optionality.IGNORED);
-    return new AggregateCall(aggFunction, distinct2, approximate, ignoreNulls,
+    return new AggregateCall(pos, aggFunction, distinct2, approximate, ignoreNulls,
         rexList, argList, filterArg, distinctKeys, collation, type, name);
   }
 
@@ -264,7 +297,7 @@ public class AggregateCall {
   /** Withs {@link #isDistinct()}. */
   public AggregateCall withDistinct(boolean distinct) {
     return distinct == this.distinct ? this
-        : new AggregateCall(aggFunction, distinct, approximate, ignoreNulls,
+        : new AggregateCall(pos, aggFunction, distinct, approximate, ignoreNulls,
             rexList, argList, filterArg, distinctKeys, collation, type, name);
   }
 
@@ -281,7 +314,7 @@ public class AggregateCall {
   /** Withs {@link #isApproximate()}. */
   public AggregateCall withApproximate(boolean approximate) {
     return approximate == this.approximate ? this
-        : new AggregateCall(aggFunction, distinct, approximate, ignoreNulls,
+        : new AggregateCall(pos, aggFunction, distinct, approximate, ignoreNulls,
             rexList, argList, filterArg, distinctKeys, collation, type, name);
   }
 
@@ -297,7 +330,7 @@ public class AggregateCall {
   /** Withs {@link #ignoreNulls()}. */
   public AggregateCall withIgnoreNulls(boolean ignoreNulls) {
     return ignoreNulls == this.ignoreNulls ? this
-        : new AggregateCall(aggFunction, distinct, approximate, ignoreNulls,
+        : new AggregateCall(pos, aggFunction, distinct, approximate, ignoreNulls,
             rexList, argList, filterArg, distinctKeys, collation, type, name);
   }
 
@@ -323,7 +356,7 @@ public class AggregateCall {
   /** Withs {@link #getCollation()}. */
   public AggregateCall withCollation(RelCollation collation) {
     return collation.equals(this.collation) ? this
-        : new AggregateCall(aggFunction, distinct, approximate, ignoreNulls,
+        : new AggregateCall(pos, aggFunction, distinct, approximate, ignoreNulls,
             rexList, argList, filterArg, distinctKeys, collation, type, name);
   }
 
@@ -341,7 +374,7 @@ public class AggregateCall {
   /** Withs {@link #getArgList()}. */
   public AggregateCall withArgList(List<Integer> argList) {
     return argList.equals(this.argList) ? this
-        : new AggregateCall(aggFunction, distinct, approximate, ignoreNulls,
+        : new AggregateCall(pos, aggFunction, distinct, approximate, ignoreNulls,
             rexList, argList, filterArg, distinctKeys, collation, type, name);
   }
 
@@ -349,7 +382,7 @@ public class AggregateCall {
   public AggregateCall withDistinctKeys(
       @Nullable ImmutableBitSet distinctKeys) {
     return Objects.equals(distinctKeys, this.distinctKeys) ? this
-        : new AggregateCall(aggFunction, distinct, approximate, ignoreNulls,
+        : new AggregateCall(pos, aggFunction, distinct, approximate, ignoreNulls,
             rexList, argList, filterArg, distinctKeys, collation, type, name);
   }
 
@@ -374,7 +407,7 @@ public class AggregateCall {
   /** Withs {@link #name}. */
   public AggregateCall withName(@Nullable String name) {
     return Objects.equals(name, this.name) ? this
-        : new AggregateCall(aggFunction, distinct, approximate, ignoreNulls,
+        : new AggregateCall(pos, aggFunction, distinct, approximate, ignoreNulls,
             rexList, argList, filterArg, distinctKeys, collation, type, name);
   }
 
@@ -435,11 +468,16 @@ public class AggregateCall {
   /** Withs {@link #filterArg}. */
   public AggregateCall withFilter(int filterArg) {
     return filterArg == this.filterArg ? this
-        : new AggregateCall(aggFunction, distinct, approximate, ignoreNulls,
+        : new AggregateCall(pos, aggFunction, distinct, approximate, ignoreNulls,
             rexList, argList, filterArg, distinctKeys, collation, type, name);
   }
 
+  public SqlParserPos getParserPosition() {
+    return this.pos;
+  }
+
   @Override public boolean equals(@Nullable Object o) {
+    // Intentionally ignore the position
     return o == this
         || o instanceof AggregateCall
         && aggFunction.equals(((AggregateCall) o).aggFunction)
@@ -453,6 +491,7 @@ public class AggregateCall {
   }
 
   @Override public int hashCode() {
+    // Ignore the position!
     return Objects.hash(aggFunction, distinct, approximate, ignoreNulls,
         rexList, argList, filterArg, distinctKeys, collation);
   }
@@ -492,7 +531,7 @@ public class AggregateCall {
   @Deprecated // to be removed before 2.0
   public AggregateCall copy(List<Integer> argList, int filterArg,
       @Nullable ImmutableBitSet distinctKeys, RelCollation collation) {
-    return new AggregateCall(aggFunction, distinct, approximate, ignoreNulls,
+    return new AggregateCall(pos, aggFunction, distinct, approximate, ignoreNulls,
         rexList, argList, filterArg, distinctKeys, collation, type, name);
   }
 
@@ -500,21 +539,21 @@ public class AggregateCall {
   public AggregateCall copy(List<Integer> argList, int filterArg,
       RelCollation collation) {
     // ignoring distinctKeys is error-prone
-    return new AggregateCall(aggFunction, distinct, approximate, ignoreNulls,
+    return new AggregateCall(pos, aggFunction, distinct, approximate, ignoreNulls,
         rexList, argList, filterArg, distinctKeys, collation, type, name);
   }
 
   @Deprecated // to be removed before 2.0
   public AggregateCall copy(List<Integer> argList, int filterArg) {
     // ignoring distinctKeys, collation is error-prone
-    return new AggregateCall(aggFunction, distinct, approximate, ignoreNulls,
+    return new AggregateCall(pos, aggFunction, distinct, approximate, ignoreNulls,
         rexList, argList, filterArg, distinctKeys, collation, type, name);
   }
 
   @Deprecated // to be removed before 2.0
   public AggregateCall copy(List<Integer> argList) {
     // ignoring filterArg, distinctKeys, collation is error-prone
-    return new AggregateCall(aggFunction, distinct, approximate, ignoreNulls,
+    return new AggregateCall(pos, aggFunction, distinct, approximate, ignoreNulls,
         rexList, argList, filterArg, distinctKeys, collation, type, name);
   }
 
@@ -539,7 +578,7 @@ public class AggregateCall {
             && filterArg == this.filterArg
             ? type
             : null;
-    return create(aggFunction, distinct, approximate, ignoreNulls,
+    return create(pos, aggFunction, distinct, approximate, ignoreNulls,
         rexList, argList, filterArg, distinctKeys, collation,
         newGroupKeyCount, input, newType, getName());
   }
@@ -547,7 +586,7 @@ public class AggregateCall {
   /** Creates a copy of this aggregate call, applying a mapping to its
    * arguments. */
   public AggregateCall transform(Mappings.TargetMapping mapping) {
-    return new AggregateCall(aggFunction, distinct, approximate, ignoreNulls,
+    return new AggregateCall(pos, aggFunction, distinct, approximate, ignoreNulls,
         rexList, Mappings.apply2((Mapping) mapping, argList),
         hasFilter() ? Mappings.apply(mapping, filterArg) : -1,
         distinctKeys == null ? null : distinctKeys.permute(mapping),
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 f1a0418127..7c07bb6f8d 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
@@ -384,7 +384,7 @@ public abstract class Window extends SingleRel implements Hintable {
         @Override public AggregateCall get(int index) {
           final RexWinAggCall aggCall = aggCalls.get(index);
           final SqlAggFunction op = (SqlAggFunction) aggCall.getOperator();
-          return AggregateCall.create(op, aggCall.distinct, false,
+          return AggregateCall.create(aggCall.getParserPosition(), op, aggCall.distinct, false,
               aggCall.ignoreNulls, ImmutableList.of(),
               getProjectOrdinals(aggCall.getOperands()),
               -1, null, RelCollations.EMPTY,
diff --git a/core/src/main/java/org/apache/calcite/rel/rel2sql/SqlImplementor.java b/core/src/main/java/org/apache/calcite/rel/rel2sql/SqlImplementor.java
index 225b1dc98e..8b26d90bf7 100644
--- a/core/src/main/java/org/apache/calcite/rel/rel2sql/SqlImplementor.java
+++ b/core/src/main/java/org/apache/calcite/rel/rel2sql/SqlImplementor.java
@@ -1697,7 +1697,7 @@ public abstract class SqlImplementor {
             && ((RexInputRef) op0).getIndex() >= leftContext.fieldCount) {
           // Arguments were of form 'op1 = op0'
           final SqlOperator op2 = requireNonNull(call.getOperator().reverse());
-          return (RexCall) rexBuilder.makeCall(op2, op1, op0);
+          return (RexCall) rexBuilder.makeCall(call.getParserPosition(), op2, op1, op0);
         }
         // fall through
       default:
diff --git a/core/src/main/java/org/apache/calcite/rel/rules/AggregateCaseToFilterRule.java b/core/src/main/java/org/apache/calcite/rel/rules/AggregateCaseToFilterRule.java
index 303b2d2c78..5922d05032 100644
--- a/core/src/main/java/org/apache/calcite/rel/rules/AggregateCaseToFilterRule.java
+++ b/core/src/main/java/org/apache/calcite/rel/rules/AggregateCaseToFilterRule.java
@@ -32,6 +32,7 @@ import org.apache.calcite.rex.RexNode;
 import org.apache.calcite.sql.SqlKind;
 import org.apache.calcite.sql.SqlPostfixOperator;
 import org.apache.calcite.sql.fun.SqlStdOperatorTable;
+import org.apache.calcite.sql.parser.SqlParserPos;
 import org.apache.calcite.sql.type.SqlTypeName;
 import org.apache.calcite.tools.RelBuilder;
 import org.apache.calcite.tools.RelBuilderFactory;
@@ -186,7 +187,8 @@ public class AggregateCaseToFilterRule
           && RexLiteral.isNullLiteral(arg2)) {
         newProjects.add(arg1);
         newProjects.add(filter);
-        return AggregateCall.create(SqlStdOperatorTable.COUNT, true, false,
+        return AggregateCall.create(
+            call.getParserPosition(), SqlStdOperatorTable.COUNT, true, false,
             false, call.rexList, ImmutableList.of(newProjects.size() - 2),
             newProjects.size() - 1, null, RelCollations.EMPTY,
             call.getType(), call.getName());
@@ -205,12 +207,13 @@ public class AggregateCaseToFilterRule
     // C: COUNT(CASE WHEN x = 'foo' THEN 'dummy' END)
     //   => COUNT() FILTER (x = 'foo')
 
+    final SqlParserPos pos = call.getParserPosition();
     if (kind == SqlKind.COUNT // Case C
         && arg1.isA(SqlKind.LITERAL)
         && !RexLiteral.isNullLiteral(arg1)
         && RexLiteral.isNullLiteral(arg2)) {
       newProjects.add(filter);
-      return AggregateCall.create(SqlStdOperatorTable.COUNT, false, false,
+      return AggregateCall.create(pos, SqlStdOperatorTable.COUNT, false, false,
           false, call.rexList, ImmutableList.of(), newProjects.size() - 1, null,
           RelCollations.EMPTY, call.getType(),
           call.getName());
@@ -223,7 +226,7 @@ public class AggregateCaseToFilterRule
       final RelDataType dataType =
           typeFactory.createTypeWithNullability(
               typeFactory.createSqlType(SqlTypeName.BIGINT), false);
-      return AggregateCall.create(SqlStdOperatorTable.COUNT, false, false,
+      return AggregateCall.create(pos, SqlStdOperatorTable.COUNT, false, false,
           false, call.rexList, ImmutableList.of(), newProjects.size() - 1, null,
           RelCollations.EMPTY, dataType, call.getName());
     } else if ((RexLiteral.isNullLiteral(arg2) // Case A1
@@ -232,7 +235,7 @@ public class AggregateCaseToFilterRule
             && isIntLiteral(arg2, BigDecimal.ZERO))) {
       newProjects.add(arg1);
       newProjects.add(filter);
-      return AggregateCall.create(call.getAggregation(), false,
+      return AggregateCall.create(pos, call.getAggregation(), false,
           false, false, call.rexList, ImmutableList.of(newProjects.size() - 2),
           newProjects.size() - 1, null, RelCollations.EMPTY,
           call.getType(), call.getName());
diff --git a/core/src/main/java/org/apache/calcite/rel/rules/AggregateExpandDistinctAggregatesRule.java b/core/src/main/java/org/apache/calcite/rel/rules/AggregateExpandDistinctAggregatesRule.java
index 865f047a4f..cfd990a315 100644
--- a/core/src/main/java/org/apache/calcite/rel/rules/AggregateExpandDistinctAggregatesRule.java
+++ b/core/src/main/java/org/apache/calcite/rel/rules/AggregateExpandDistinctAggregatesRule.java
@@ -334,7 +334,7 @@ public final class AggregateExpandDistinctAggregatesRule
       // as-is all the non-distinct aggregates
       if (!aggCall.isDistinct()) {
         final AggregateCall newCall =
-            AggregateCall.create(aggCall.getAggregation(), false,
+            AggregateCall.create(aggCall.getParserPosition(), aggCall.getAggregation(), false,
                 aggCall.isApproximate(), aggCall.ignoreNulls(), aggCall.rexList,
                 aggCall.getArgList(), -1, aggCall.distinctKeys,
                 aggCall.collation, bottomGroupSet.cardinality(),
@@ -360,7 +360,8 @@ public final class AggregateExpandDistinctAggregatesRule
           newArgList.add(bottomGroups.headSet(arg, false).size());
         }
         newCall =
-            AggregateCall.create(aggCall.getAggregation(),
+            AggregateCall.create(aggCall.getParserPosition(),
+                aggCall.getAggregation(),
                 false,
                 aggCall.isApproximate(),
                 aggCall.ignoreNulls(),
@@ -380,14 +381,16 @@ public final class AggregateExpandDistinctAggregatesRule
         final List<Integer> newArgs = ImmutableList.of(arg);
         if (aggCall.getAggregation().getKind() == SqlKind.COUNT) {
           newCall =
-              AggregateCall.create(new SqlSumEmptyIsZeroAggFunction(), false,
+              AggregateCall.create(aggCall.getParserPosition(),
+                  new SqlSumEmptyIsZeroAggFunction(), false,
                   aggCall.isApproximate(), aggCall.ignoreNulls(),
                   aggCall.rexList, newArgs, -1, aggCall.distinctKeys,
                   aggCall.collation, originalGroupSet.cardinality(),
                   relBuilder.peek(), null, aggCall.getName());
         } else {
           newCall =
-              AggregateCall.create(aggCall.getAggregation(), false,
+              AggregateCall.create(aggCall.getParserPosition(),
+                  aggCall.getAggregation(), false,
                   aggCall.isApproximate(), aggCall.ignoreNulls(),
                   aggCall.rexList, newArgs, -1, aggCall.distinctKeys,
                   aggCall.collation, originalGroupSet.cardinality(),
@@ -540,7 +543,7 @@ public final class AggregateExpandDistinctAggregatesRule
                 "filters.get(of(newGroupSet, aggCall.filterArg))");
       }
       final AggregateCall newCall =
-          AggregateCall.create(aggregation, false,
+          AggregateCall.create(aggCall.getParserPosition(), aggregation, false,
               aggCall.isApproximate(), aggCall.ignoreNulls(),
               aggCall.rexList, newArgList, newFilterArg,
               aggCall.distinctKeys, aggCall.collation,
@@ -755,7 +758,7 @@ public final class AggregateExpandDistinctAggregatesRule
         newArgs.add(requireNonNull(sourceOf.get(arg), () -> "sourceOf.get(" + arg + ")"));
       }
       final AggregateCall newAggCall =
-          AggregateCall.create(aggCall.getAggregation(), false,
+          AggregateCall.create(aggCall.getParserPosition(), aggCall.getAggregation(), false,
               aggCall.isApproximate(), aggCall.ignoreNulls(), aggCall.rexList,
               newArgs, -1, aggCall.distinctKeys, aggCall.collation,
               aggCall.getType(), aggCall.getName());
@@ -843,7 +846,7 @@ public final class AggregateExpandDistinctAggregatesRule
                 () -> "sourceOf.get(" + arg + ")"));
       }
       final AggregateCall newAggCall =
-          AggregateCall.create(aggCall.getAggregation(), false,
+          AggregateCall.create(aggCall.getParserPosition(), aggCall.getAggregation(), false,
               aggCall.isApproximate(), aggCall.ignoreNulls(),
               aggCall.rexList, newArgs, -1,
               aggCall.distinctKeys, aggCall.collation,
diff --git a/core/src/main/java/org/apache/calcite/rel/rules/AggregateExpandWithinDistinctRule.java b/core/src/main/java/org/apache/calcite/rel/rules/AggregateExpandWithinDistinctRule.java
index 50f43771db..a47a68ca93 100644
--- a/core/src/main/java/org/apache/calcite/rel/rules/AggregateExpandWithinDistinctRule.java
+++ b/core/src/main/java/org/apache/calcite/rel/rules/AggregateExpandWithinDistinctRule.java
@@ -347,7 +347,7 @@ public class AggregateExpandWithinDistinctRule
     Ord.forEach(aggCallList, (c, i) -> {
       if (c.distinctKeys == null) {
         RelBuilder.AggCall aggCall =
-            b.aggregateCall(c.getAggregation(), b.fields(c.getArgList()));
+            b.aggregateCall(c.getParserPosition(), c.getAggregation(), b.fields(c.getArgList()));
         registrar.registerAgg(i,
             c.hasFilter()
                 ? aggCall.filter(b.field(c.filterArg))
@@ -411,7 +411,7 @@ public class AggregateExpandWithinDistinctRule
       RelBuilder.AggCall aggCall;
       if (c.distinctKeys == null) {
         aggCall =
-            b.aggregateCall(SqlStdOperatorTable.MIN,
+            b.aggregateCall(c.getParserPosition(), SqlStdOperatorTable.MIN,
                 b.field(registrar.getAgg(i)));
       } else {
         // The inputs to this aggregate are outputs from MIN() calls from the
@@ -423,7 +423,7 @@ public class AggregateExpandWithinDistinctRule
         // ignore null inputs, we add a filter based on a COUNT() in the inner
         // aggregate.
         aggCall =
-            b.aggregateCall(c.getAggregation(),
+            b.aggregateCall(c.getParserPosition(), c.getAggregation(),
                 b.fields(registrar.fields(c.getArgList(), c.filterArg)));
 
         if (mustBeCounted(c)) {
diff --git a/core/src/main/java/org/apache/calcite/rel/rules/AggregateFilterTransposeRule.java b/core/src/main/java/org/apache/calcite/rel/rules/AggregateFilterTransposeRule.java
index 43524a2900..105e4096dc 100644
--- a/core/src/main/java/org/apache/calcite/rel/rules/AggregateFilterTransposeRule.java
+++ b/core/src/main/java/org/apache/calcite/rel/rules/AggregateFilterTransposeRule.java
@@ -145,7 +145,8 @@ public class AggregateFilterTransposeRule
           return;
         }
         topAggCallList.add(
-            AggregateCall.create(rollup, aggregateCall.isDistinct(),
+            AggregateCall.create(aggregateCall.getParserPosition(),
+                rollup, aggregateCall.isDistinct(),
                 aggregateCall.isApproximate(), aggregateCall.ignoreNulls(),
                 aggregateCall.rexList, ImmutableList.of(i++), -1,
                 aggregateCall.distinctKeys, aggregateCall.collation,
diff --git a/core/src/main/java/org/apache/calcite/rel/rules/AggregateReduceFunctionsRule.java b/core/src/main/java/org/apache/calcite/rel/rules/AggregateReduceFunctionsRule.java
index ec40844ab2..741164e342 100644
--- a/core/src/main/java/org/apache/calcite/rel/rules/AggregateReduceFunctionsRule.java
+++ b/core/src/main/java/org/apache/calcite/rel/rules/AggregateReduceFunctionsRule.java
@@ -34,6 +34,7 @@ import org.apache.calcite.rex.RexNode;
 import org.apache.calcite.sql.SqlAggFunction;
 import org.apache.calcite.sql.SqlKind;
 import org.apache.calcite.sql.fun.SqlStdOperatorTable;
+import org.apache.calcite.sql.parser.SqlParserPos;
 import org.apache.calcite.tools.RelBuilder;
 import org.apache.calcite.tools.RelBuilderFactory;
 import org.apache.calcite.util.CompositeList;
@@ -375,7 +376,8 @@ public class AggregateReduceFunctionsRule
         new Aggregate.AggCallBinding(typeFactory, aggFunction,
             ImmutableList.of(), ImmutableList.of(operandType),
             oldAggRel.getGroupCount(), filter >= 0);
-    return AggregateCall.create(aggFunction,
+    return AggregateCall.create(oldCall.getParserPosition(),
+        aggFunction,
         oldCall.isDistinct(),
         oldCall.isApproximate(),
         oldCall.ignoreNulls(),
@@ -396,8 +398,9 @@ public class AggregateReduceFunctionsRule
       @SuppressWarnings("unused") List<RexNode> inputExprs) {
     final int nGroups = oldAggRel.getGroupCount();
     final RexBuilder rexBuilder = oldAggRel.getCluster().getRexBuilder();
+    final SqlParserPos pos = oldCall.getParserPosition();
     final AggregateCall sumCall =
-        AggregateCall.create(SqlStdOperatorTable.SUM,
+        AggregateCall.create(pos, SqlStdOperatorTable.SUM,
             oldCall.isDistinct(),
             oldCall.isApproximate(),
             oldCall.ignoreNulls(),
@@ -411,7 +414,7 @@ public class AggregateReduceFunctionsRule
             null,
             null);
     final AggregateCall countCall =
-        AggregateCall.create(SqlStdOperatorTable.COUNT,
+        AggregateCall.create(pos, SqlStdOperatorTable.COUNT,
             oldCall.isDistinct(),
             oldCall.isApproximate(),
             oldCall.ignoreNulls(),
@@ -444,10 +447,10 @@ public class AggregateReduceFunctionsRule
     final RelDataType avgType =
         typeFactory.createTypeWithNullability(oldCall.getType(),
             numeratorRef.getType().isNullable());
-    numeratorRef = rexBuilder.ensureType(avgType, numeratorRef, true);
+    numeratorRef = rexBuilder.ensureType(pos, avgType, numeratorRef, true);
     final RexNode divideRef =
-        rexBuilder.makeCall(SqlStdOperatorTable.DIVIDE, numeratorRef, denominatorRef);
-    return rexBuilder.makeCast(oldCall.getType(), divideRef);
+        rexBuilder.makeCall(pos, SqlStdOperatorTable.DIVIDE, numeratorRef, denominatorRef);
+    return rexBuilder.makeCast(pos, oldCall.getType(), divideRef);
   }
 
   private static RexNode reduceSum(
@@ -456,16 +459,17 @@ public class AggregateReduceFunctionsRule
       List<AggregateCall> newCalls,
       Map<AggregateCall, RexNode> aggCallMapping) {
     final int nGroups = oldAggRel.getGroupCount();
+    final SqlParserPos pos = oldCall.getParserPosition();
     RexBuilder rexBuilder = oldAggRel.getCluster().getRexBuilder();
 
     final AggregateCall sumZeroCall =
-        AggregateCall.create(SqlStdOperatorTable.SUM0, oldCall.isDistinct(),
+        AggregateCall.create(pos, SqlStdOperatorTable.SUM0, oldCall.isDistinct(),
             oldCall.isApproximate(), oldCall.ignoreNulls(), oldCall.rexList,
             oldCall.getArgList(), oldCall.filterArg, oldCall.distinctKeys,
             oldCall.collation, oldAggRel.getGroupCount(), oldAggRel.getInput(),
             null, oldCall.name);
     final AggregateCall countCall =
-        AggregateCall.create(SqlStdOperatorTable.COUNT,
+        AggregateCall.create(pos, SqlStdOperatorTable.COUNT,
             oldCall.isDistinct(),
             oldCall.isApproximate(),
             oldCall.ignoreNulls(),
@@ -499,8 +503,8 @@ public class AggregateReduceFunctionsRule
             newCalls,
             aggCallMapping,
             oldAggRel.getInput()::fieldIsNullable);
-    return rexBuilder.makeCall(SqlStdOperatorTable.CASE,
-        rexBuilder.makeCall(SqlStdOperatorTable.EQUALS,
+    return rexBuilder.makeCall(pos, SqlStdOperatorTable.CASE,
+        rexBuilder.makeCall(pos, SqlStdOperatorTable.EQUALS,
             countRef, rexBuilder.makeExactLiteral(BigDecimal.ZERO)),
         rexBuilder.makeNullLiteral(sumZeroRef.getType()),
         sumZeroRef);
@@ -529,6 +533,7 @@ public class AggregateReduceFunctionsRule
     final RelOptCluster cluster = oldAggRel.getCluster();
     final RexBuilder rexBuilder = cluster.getRexBuilder();
     final RelDataTypeFactory typeFactory = cluster.getTypeFactory();
+    final SqlParserPos pos = oldCall.getParserPosition();
 
     assert oldCall.getArgList().size() == 1 : oldCall.getArgList();
     final int argOrdinal = oldCall.getArgList().get(0);
@@ -538,10 +543,10 @@ public class AggregateReduceFunctionsRule
             fieldIsNullable.test(argOrdinal));
 
     final RexNode argRef =
-        rexBuilder.ensureType(oldCallType, inputExprs.get(argOrdinal), true);
+        rexBuilder.ensureType(pos, oldCallType, inputExprs.get(argOrdinal), true);
 
     final RexNode argSquared =
-        rexBuilder.makeCall(SqlStdOperatorTable.MULTIPLY, argRef, argRef);
+        rexBuilder.makeCall(pos, SqlStdOperatorTable.MULTIPLY, argRef, argRef);
     final int argSquaredOrdinal = lookupOrAdd(inputExprs, argSquared);
 
     final AggregateCall sumArgSquaredAggCall =
@@ -556,7 +561,7 @@ public class AggregateReduceFunctionsRule
             oldAggRel.getInput()::fieldIsNullable);
 
     final AggregateCall sumArgAggCall =
-        AggregateCall.create(SqlStdOperatorTable.SUM,
+        AggregateCall.create(pos, SqlStdOperatorTable.SUM,
             oldCall.isDistinct(),
             oldCall.isApproximate(),
             oldCall.ignoreNulls(),
@@ -576,13 +581,13 @@ public class AggregateReduceFunctionsRule
             newCalls,
             aggCallMapping,
             oldAggRel.getInput()::fieldIsNullable);
-    final RexNode sumArgCast = rexBuilder.ensureType(oldCallType, sumArg, true);
+    final RexNode sumArgCast = rexBuilder.ensureType(pos, oldCallType, sumArg, true);
     final RexNode sumSquaredArg =
-        rexBuilder.makeCall(
+        rexBuilder.makeCall(pos,
             SqlStdOperatorTable.MULTIPLY, sumArgCast, sumArgCast);
 
     final AggregateCall countArgAggCall =
-        AggregateCall.create(SqlStdOperatorTable.COUNT,
+        AggregateCall.create(pos, SqlStdOperatorTable.COUNT,
             oldCall.isDistinct(),
             oldCall.isApproximate(),
             oldCall.ignoreNulls(),
@@ -604,20 +609,20 @@ public class AggregateReduceFunctionsRule
             oldAggRel.getInput()::fieldIsNullable);
 
     final RexNode div =
-        divide(biased, rexBuilder, sumArgSquared, sumSquaredArg, countArg);
+        divide(pos, biased, rexBuilder, sumArgSquared, sumSquaredArg, countArg);
 
     final RexNode result;
     if (sqrt) {
       final RexNode half =
           rexBuilder.makeExactLiteral(new BigDecimal("0.5"));
       result =
-          rexBuilder.makeCall(
+          rexBuilder.makeCall(pos,
               SqlStdOperatorTable.POWER, div, half);
     } else {
       result = div;
     }
 
-    return rexBuilder.makeCast(
+    return rexBuilder.makeCast(pos,
         oldCall.getType(), result);
   }
 
@@ -635,7 +640,7 @@ public class AggregateReduceFunctionsRule
     if (refByGroup.getType().equals(oldCall.getType())) {
       return refByGroup;
     } else {
-      return rexBuilder.makeCast(oldCall.getType(), refByGroup);
+      return rexBuilder.makeCast(oldCall.getParserPosition(), oldCall.getType(), refByGroup);
     }
   }
 
@@ -647,7 +652,7 @@ public class AggregateReduceFunctionsRule
       int argOrdinal,
       int filterArg) {
     final AggregateCall aggregateCall =
-        AggregateCall.create(SqlStdOperatorTable.SUM,
+        AggregateCall.create(oldCall.getParserPosition(), SqlStdOperatorTable.SUM,
             oldCall.isDistinct(),
             oldCall.isApproximate(),
             oldCall.ignoreNulls(),
@@ -693,7 +698,8 @@ public class AggregateReduceFunctionsRule
       ImmutableIntList argOrdinals,
       int filterArg) {
     final AggregateCall countArgAggCall =
-        AggregateCall.create(SqlStdOperatorTable.REGR_COUNT,
+        AggregateCall.create(oldCall.getParserPosition(),
+            SqlStdOperatorTable.REGR_COUNT,
             oldCall.isDistinct(),
             oldCall.isApproximate(),
             oldCall.ignoreNulls(),
@@ -727,6 +733,7 @@ public class AggregateReduceFunctionsRule
     //    sum(y * y, x) - sum(y, x) * sum(y, x) / regr_count(x, y)
     //
 
+    final SqlParserPos pos = oldCall.getParserPosition();
     final RelOptCluster cluster = oldAggRel.getCluster();
     final RexBuilder rexBuilder = cluster.getRexBuilder();
     final RelDataTypeFactory typeFactory = cluster.getTypeFactory();
@@ -739,28 +746,28 @@ public class AggregateReduceFunctionsRule
                 || fieldIsNullable.test(nullFilterIndex));
 
     final RexNode argX =
-        rexBuilder.ensureType(oldCallType, inputExprs.get(xIndex), true);
+        rexBuilder.ensureType(pos, oldCallType, inputExprs.get(xIndex), true);
     final RexNode argY =
-        rexBuilder.ensureType(oldCallType, inputExprs.get(yIndex), true);
+        rexBuilder.ensureType(pos, oldCallType, inputExprs.get(yIndex), true);
     final RexNode argNullFilter =
-        rexBuilder.ensureType(oldCallType, inputExprs.get(nullFilterIndex), true);
+        rexBuilder.ensureType(pos, oldCallType, inputExprs.get(nullFilterIndex), true);
 
-    final RexNode argXArgY = rexBuilder.makeCall(SqlStdOperatorTable.MULTIPLY, argX, argY);
+    final RexNode argXArgY = rexBuilder.makeCall(pos, SqlStdOperatorTable.MULTIPLY, argX, argY);
     final int argSquaredOrdinal = lookupOrAdd(inputExprs, argXArgY);
 
     final RexNode argXAndYNotNullFilter =
-        rexBuilder.makeCall(SqlStdOperatorTable.AND,
-            rexBuilder.makeCall(SqlStdOperatorTable.AND,
-                rexBuilder.makeCall(SqlStdOperatorTable.IS_NOT_NULL, argX),
-                rexBuilder.makeCall(SqlStdOperatorTable.IS_NOT_NULL, argY)),
-        rexBuilder.makeCall(SqlStdOperatorTable.IS_NOT_NULL, argNullFilter));
+        rexBuilder.makeCall(pos, SqlStdOperatorTable.AND,
+            rexBuilder.makeCall(pos, SqlStdOperatorTable.AND,
+                rexBuilder.makeCall(pos, SqlStdOperatorTable.IS_NOT_NULL, argX),
+                rexBuilder.makeCall(pos, SqlStdOperatorTable.IS_NOT_NULL, argY)),
+        rexBuilder.makeCall(pos, SqlStdOperatorTable.IS_NOT_NULL, argNullFilter));
     final int argXAndYNotNullFilterOrdinal =
         lookupOrAdd(inputExprs, argXAndYNotNullFilter);
     final RexNode sumXY =
         getSumAggregatedRexNodeWithBinding(oldAggRel, oldCall, newCalls,
             aggCallMapping, argXArgY.getType(),
             argSquaredOrdinal, argXAndYNotNullFilterOrdinal);
-    final RexNode sumXYCast = rexBuilder.ensureType(oldCallType, sumXY, true);
+    final RexNode sumXYCast = rexBuilder.ensureType(pos, oldCallType, sumXY, true);
 
     final RexNode sumX =
         getSumAggregatedRexNode(oldAggRel, oldCall, newCalls, aggCallMapping,
@@ -771,7 +778,7 @@ public class AggregateReduceFunctionsRule
             aggCallMapping, rexBuilder, yIndex, argXAndYNotNullFilterOrdinal);
 
     final RexNode sumXSumY =
-        rexBuilder.makeCall(SqlStdOperatorTable.MULTIPLY, sumX, sumY);
+        rexBuilder.makeCall(pos, SqlStdOperatorTable.MULTIPLY, sumX, sumY);
 
     final RexNode countArg =
         getRegrCountRexNode(oldAggRel, oldCall, newCalls, aggCallMapping,
@@ -780,15 +787,15 @@ public class AggregateReduceFunctionsRule
     RexLiteral zero = rexBuilder.makeExactLiteral(BigDecimal.ZERO);
     RexNode nul = rexBuilder.makeNullLiteral(zero.getType());
     final RexNode avgSumXSumY =
-        rexBuilder.makeCall(SqlStdOperatorTable.CASE,
-            rexBuilder.makeCall(SqlStdOperatorTable.EQUALS, countArg, zero),
+        rexBuilder.makeCall(pos, SqlStdOperatorTable.CASE,
+            rexBuilder.makeCall(pos, SqlStdOperatorTable.EQUALS, countArg, zero),
             nul,
-            rexBuilder.makeCall(SqlStdOperatorTable.DIVIDE, sumXSumY, countArg));
+            rexBuilder.makeCall(pos, SqlStdOperatorTable.DIVIDE, sumXSumY, countArg));
     final RexNode avgSumXSumYCast =
-        rexBuilder.ensureType(oldCallType, avgSumXSumY, true);
+        rexBuilder.ensureType(pos, oldCallType, avgSumXSumY, true);
     final RexNode result =
-        rexBuilder.makeCall(SqlStdOperatorTable.MINUS, sumXYCast, avgSumXSumYCast);
-    return rexBuilder.makeCast(oldCall.getType(), result);
+        rexBuilder.makeCall(pos, SqlStdOperatorTable.MINUS, sumXYCast, avgSumXSumYCast);
+    return rexBuilder.makeCast(pos, oldCall.getType(), result);
   }
 
   private static RexNode reduceCovariance(
@@ -805,6 +812,7 @@ public class AggregateReduceFunctionsRule
     // covar_samp(x, y) ==>
     //     (sum(x * y) - sum(x) * sum(y) / regr_count(x, y))
     //     / regr_count(count(x, y) - 1, 0)
+    final SqlParserPos pos = oldCall.getParserPosition();
     final RelOptCluster cluster = oldAggRel.getCluster();
     final RexBuilder rexBuilder = cluster.getRexBuilder();
     final RelDataTypeFactory typeFactory = cluster.getTypeFactory();
@@ -816,16 +824,18 @@ public class AggregateReduceFunctionsRule
         typeFactory.createTypeWithNullability(oldCall.getType(),
             fieldIsNullable.test(argXOrdinal)
                 || fieldIsNullable.test(argYOrdinal));
-    final RexNode argX = rexBuilder.ensureType(oldCallType, inputExprs.get(argXOrdinal), true);
-    final RexNode argY = rexBuilder.ensureType(oldCallType, inputExprs.get(argYOrdinal), true);
+    final RexNode argX =
+        rexBuilder.ensureType(pos, oldCallType, inputExprs.get(argXOrdinal), true);
+    final RexNode argY =
+        rexBuilder.ensureType(pos, oldCallType, inputExprs.get(argYOrdinal), true);
     final RexNode argXAndYNotNullFilter =
-        rexBuilder.makeCall(SqlStdOperatorTable.AND,
-            rexBuilder.makeCall(SqlStdOperatorTable.IS_NOT_NULL, argX),
-            rexBuilder.makeCall(SqlStdOperatorTable.IS_NOT_NULL, argY));
+        rexBuilder.makeCall(pos, SqlStdOperatorTable.AND,
+            rexBuilder.makeCall(pos, SqlStdOperatorTable.IS_NOT_NULL, argX),
+            rexBuilder.makeCall(pos, SqlStdOperatorTable.IS_NOT_NULL, argY));
     final int argXAndYNotNullFilterOrdinal =
         lookupOrAdd(inputExprs, argXAndYNotNullFilter);
     final RexNode argXY =
-        rexBuilder.makeCall(SqlStdOperatorTable.MULTIPLY, argX, argY);
+        rexBuilder.makeCall(pos, SqlStdOperatorTable.MULTIPLY, argX, argY);
     final int argXYOrdinal = lookupOrAdd(inputExprs, argXY);
     final RexNode sumXY =
         getSumAggregatedRexNodeWithBinding(oldAggRel, oldCall, newCalls,
@@ -840,22 +850,22 @@ public class AggregateReduceFunctionsRule
             aggCallMapping, rexBuilder, argYOrdinal,
             argXAndYNotNullFilterOrdinal);
     final RexNode sumXSumY =
-        rexBuilder.makeCall(SqlStdOperatorTable.MULTIPLY, sumX, sumY);
+        rexBuilder.makeCall(pos, SqlStdOperatorTable.MULTIPLY, sumX, sumY);
     final RexNode countArg =
         getRegrCountRexNode(oldAggRel, oldCall, newCalls,
             aggCallMapping, ImmutableIntList.of(argXOrdinal, argYOrdinal),
             argXAndYNotNullFilterOrdinal);
     final RexNode result =
-        divide(biased, rexBuilder, sumXY, sumXSumY, countArg);
-    return rexBuilder.makeCast(oldCall.getType(), result);
+        divide(pos, biased, rexBuilder, sumXY, sumXSumY, countArg);
+    return rexBuilder.makeCast(pos, oldCall.getType(), result);
   }
 
-  private static RexNode divide(boolean biased, RexBuilder rexBuilder,
+  private static RexNode divide(SqlParserPos pos, boolean biased, RexBuilder rexBuilder,
       RexNode sumXY, RexNode sumXSumY, RexNode countArg) {
     final RexNode avgSumSquaredArg =
-         rexBuilder.makeCall(SqlStdOperatorTable.DIVIDE, sumXSumY, countArg);
+         rexBuilder.makeCall(pos, SqlStdOperatorTable.DIVIDE, sumXSumY, countArg);
     final RexNode diff =
-        rexBuilder.makeCall(SqlStdOperatorTable.MINUS, sumXY, avgSumSquaredArg);
+        rexBuilder.makeCall(pos, SqlStdOperatorTable.MINUS, sumXY, avgSumSquaredArg);
     final RexNode denominator;
     if (biased) {
       denominator = countArg;
@@ -863,14 +873,14 @@ public class AggregateReduceFunctionsRule
       final RexLiteral one = rexBuilder.makeExactLiteral(BigDecimal.ONE);
       final RexNode nul = rexBuilder.makeNullLiteral(countArg.getType());
       final RexNode countMinusOne =
-          rexBuilder.makeCall(SqlStdOperatorTable.MINUS, countArg, one);
+          rexBuilder.makeCall(pos, SqlStdOperatorTable.MINUS, countArg, one);
       final RexNode countEqOne =
-          rexBuilder.makeCall(SqlStdOperatorTable.EQUALS, countArg, one);
+          rexBuilder.makeCall(pos, SqlStdOperatorTable.EQUALS, countArg, one);
       denominator =
-          rexBuilder.makeCall(SqlStdOperatorTable.CASE, countEqOne, nul,
+          rexBuilder.makeCall(pos, SqlStdOperatorTable.CASE, countEqOne, nul,
               countMinusOne);
     }
-    return rexBuilder.makeCall(SqlStdOperatorTable.DIVIDE, diff, denominator);
+    return rexBuilder.makeCall(pos, SqlStdOperatorTable.DIVIDE, diff, denominator);
   }
 
   /**
diff --git a/core/src/main/java/org/apache/calcite/rel/rules/AggregateRemoveRule.java b/core/src/main/java/org/apache/calcite/rel/rules/AggregateRemoveRule.java
index 64186116bb..c4d406af21 100644
--- a/core/src/main/java/org/apache/calcite/rel/rules/AggregateRemoveRule.java
+++ b/core/src/main/java/org/apache/calcite/rel/rules/AggregateRemoveRule.java
@@ -133,7 +133,7 @@ public class AggregateRemoveRule
                 aggregate.getGroupSet(), aggregate.groupSets, aggCall);
         if (constant != null) {
           final RexNode cast =
-              rexBuilder.ensureType(aggCall.type, constant, false);
+              rexBuilder.ensureType(aggCall.getParserPosition(), aggCall.type, constant, false);
           projects.add(cast);
           continue;
         }
@@ -143,7 +143,8 @@ public class AggregateRemoveRule
       final RexNode singleton =
           splitter.singleton(rexBuilder, input.getRowType(), aggCall);
       final RexNode cast =
-          rexBuilder.ensureType(aggCall.type, singleton, false);
+          rexBuilder.ensureType(
+              aggCall.getParserPosition(), aggCall.type, singleton, false);
       projects.add(cast);
     }
 
diff --git a/core/src/main/java/org/apache/calcite/rel/rules/AggregateStarTableRule.java b/core/src/main/java/org/apache/calcite/rel/rules/AggregateStarTableRule.java
index 523c00bb8a..5136937f6e 100644
--- a/core/src/main/java/org/apache/calcite/rel/rules/AggregateStarTableRule.java
+++ b/core/src/main/java/org/apache/calcite/rel/rules/AggregateStarTableRule.java
@@ -214,7 +214,7 @@ public class AggregateStarTableRule
       if (roll == null) {
         break tryRoll;
       }
-      return AggregateCall.create(roll, false, call.isApproximate(),
+      return AggregateCall.create(call.getParserPosition(), roll, false, call.isApproximate(),
           call.ignoreNulls(), call.rexList, ImmutableList.of(offset + i), -1,
           call.distinctKeys, call.collation,
           groupCount, relBuilder.peek(), null, call.name);
@@ -231,7 +231,7 @@ public class AggregateStarTableRule
         }
         newArgs.add(z);
       }
-      return AggregateCall.create(aggregation, false,
+      return AggregateCall.create(call.getParserPosition(), aggregation, false,
           call.isApproximate(), call.ignoreNulls(), call.rexList,
           newArgs, -1, call.distinctKeys, call.collation,
           groupCount, relBuilder.peek(), null, call.name);
diff --git a/core/src/main/java/org/apache/calcite/rel/rules/AggregateUnionTransposeRule.java b/core/src/main/java/org/apache/calcite/rel/rules/AggregateUnionTransposeRule.java
index 32df27a641..4721d6997c 100644
--- a/core/src/main/java/org/apache/calcite/rel/rules/AggregateUnionTransposeRule.java
+++ b/core/src/main/java/org/apache/calcite/rel/rules/AggregateUnionTransposeRule.java
@@ -213,7 +213,7 @@ public class AggregateUnionTransposeRule
         aggType = origCall.getType();
       }
       AggregateCall newCall =
-          AggregateCall.create(aggFun, origCall.isDistinct(),
+          AggregateCall.create(ord.e.getParserPosition(), aggFun, origCall.isDistinct(),
               origCall.isApproximate(), origCall.ignoreNulls(),
               origCall.rexList, ImmutableList.of(groupCount + ord.i), -1,
               origCall.distinctKeys, origCall.collation,
diff --git a/core/src/main/java/org/apache/calcite/rel/rules/ProjectAggregateMergeRule.java b/core/src/main/java/org/apache/calcite/rel/rules/ProjectAggregateMergeRule.java
index 3c2f5e404c..42b209fa2d 100644
--- a/core/src/main/java/org/apache/calcite/rel/rules/ProjectAggregateMergeRule.java
+++ b/core/src/main/java/org/apache/calcite/rel/rules/ProjectAggregateMergeRule.java
@@ -164,7 +164,7 @@ public class ProjectAggregateMergeRule
   private static int findSum0(RelDataTypeFactory typeFactory, AggregateCall sum,
       List<AggregateCall> aggCallList) {
     final AggregateCall sum0 =
-        AggregateCall.create(SqlStdOperatorTable.SUM0, sum.isDistinct(),
+        AggregateCall.create(sum.getParserPosition(), SqlStdOperatorTable.SUM0, sum.isDistinct(),
             sum.isApproximate(), sum.ignoreNulls(), sum.rexList,
             sum.getArgList(), sum.filterArg, sum.distinctKeys, sum.collation,
             typeFactory.createTypeWithNullability(sum.type, false), null);
diff --git a/core/src/main/java/org/apache/calcite/rel/rules/ReduceDecimalsRule.java b/core/src/main/java/org/apache/calcite/rel/rules/ReduceDecimalsRule.java
index e68cc2902b..28cb1cc02e 100644
--- a/core/src/main/java/org/apache/calcite/rel/rules/ReduceDecimalsRule.java
+++ b/core/src/main/java/org/apache/calcite/rel/rules/ReduceDecimalsRule.java
@@ -34,6 +34,7 @@ import org.apache.calcite.rex.RexUtil;
 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.SqlTypeName;
 import org.apache.calcite.sql.type.SqlTypeUtil;
 import org.apache.calcite.tools.RelBuilderFactory;
@@ -414,7 +415,7 @@ public class ReduceDecimalsRule
      * @param scale a value from zero to max precision - 1
      * @return value * 10^scale as an exact numeric value
      */
-    protected RexNode scaleUp(RexNode value, int scale) {
+    protected RexNode scaleUp(SqlParserPos pos, RexNode value, int scale) {
       assert scale >= 0;
       assert scale
           < builder.getTypeFactory().getTypeSystem().getMaxNumericPrecision();
@@ -422,6 +423,7 @@ public class ReduceDecimalsRule
         return value;
       }
       return builder.makeCall(
+          pos,
           SqlStdOperatorTable.MULTIPLY,
           value,
           makeScaleFactor(scale));
@@ -438,7 +440,7 @@ public class ReduceDecimalsRule
      * @return value/10^scale, rounded away from zero and returned as an
      * exact numeric value
      */
-    protected RexNode scaleDown(RexNode value, int scale) {
+    protected RexNode scaleDown(SqlParserPos pos, RexNode value, int scale) {
       final int maxPrecision =
           builder.getTypeFactory().getTypeSystem().getMaxNumericPrecision();
       assert scale >= 0 && scale <= maxPrecision;
@@ -448,12 +450,12 @@ public class ReduceDecimalsRule
       if (scale == maxPrecision) {
         long half = BigInteger.TEN.pow(scale - 1).longValue() * 5;
         return makeCase(
-            builder.makeCall(
+            builder.makeCall(pos,
                 SqlStdOperatorTable.GREATER_THAN_OR_EQUAL,
                 value,
                 makeExactLiteral(half)),
             makeExactLiteral(1),
-            builder.makeCall(
+            builder.makeCall(pos,
                 SqlStdOperatorTable.LESS_THAN_OR_EQUAL,
                 value,
                 makeExactLiteral(-half)),
@@ -463,13 +465,13 @@ public class ReduceDecimalsRule
       RexNode roundFactor = makeRoundFactor(scale);
       RexNode roundValue =
           makeCase(
-              builder.makeCall(
+              builder.makeCall(pos,
                   SqlStdOperatorTable.GREATER_THAN,
                   value,
                   makeExactLiteral(0)),
-              makePlus(value, roundFactor),
-              makeMinus(value, roundFactor));
-      return makeDivide(
+              makePlus(pos, value, roundFactor),
+              makeMinus(pos, value, roundFactor));
+      return makeDivide(pos,
           roundValue,
           makeScaleFactor(scale));
     }
@@ -483,15 +485,16 @@ public class ReduceDecimalsRule
      * @param scale a value from zero to max precision
      * @return value/10^scale as a double precision value
      */
-    protected RexNode scaleDownDouble(RexNode value, int scale) {
+    protected RexNode scaleDownDouble(SqlParserPos pos, RexNode value, int scale) {
       assert scale >= 0;
       assert scale
           <= builder.getTypeFactory().getTypeSystem().getMaxNumericPrecision();
-      RexNode cast = ensureType(real8, value);
+      RexNode cast = ensureType(pos, real8, value);
       if (scale == 0) {
         return cast;
       }
       return makeDivide(
+          pos,
           cast,
           makeApproxScaleFactor(scale));
     }
@@ -513,7 +516,7 @@ public class ReduceDecimalsRule
      * @return value * 10^scale, returned as an exact or approximate value
      * corresponding to the input value
      */
-    protected RexNode ensureScale(RexNode value, int scale, int required) {
+    protected RexNode ensureScale(SqlParserPos pos, RexNode value, int scale, int required) {
       final RelDataTypeSystem typeSystem =
           builder.getTypeFactory().getTypeSystem();
       final int maxPrecision = typeSystem.getMaxNumericPrecision();
@@ -525,6 +528,7 @@ public class ReduceDecimalsRule
       int scaleDiff = required - scale;
       if (SqlTypeUtil.isApproximateNumeric(value.getType())) {
         return makeMultiply(
+            pos,
             value,
             makeApproxScaleFactor(scaleDiff));
       }
@@ -537,7 +541,7 @@ public class ReduceDecimalsRule
             + "source type is too large to be encoded by the "
             + "target type");
       }
-      return scaleUp(value, scaleDiff);
+      return scaleUp(pos, value, scaleDiff);
     }
 
     /**
@@ -546,9 +550,9 @@ public class ReduceDecimalsRule
      * @param decimalNode the decimal value as an opaque type
      * @return an integer representation of the decimal value
      */
-    protected RexNode decodeValue(RexNode decimalNode) {
+    protected RexNode decodeValue(SqlParserPos pos, RexNode decimalNode) {
       assert SqlTypeUtil.isDecimal(decimalNode.getType());
-      return builder.decodeIntervalOrDecimal(decimalNode);
+      return builder.decodeIntervalOrDecimal(pos, decimalNode);
     }
 
     /**
@@ -562,7 +566,7 @@ public class ReduceDecimalsRule
     protected RexNode accessValue(RexNode node) {
       assert SqlTypeUtil.isNumeric(node.getType());
       if (SqlTypeUtil.isDecimal(node.getType())) {
-        return decodeValue(node);
+        return decodeValue(SqlParserPos.ZERO, node);
       }
       return node;
     }
@@ -577,8 +581,8 @@ public class ReduceDecimalsRule
      * @param decimalType type integer will be reinterpreted as
      * @return the integer representation reinterpreted as a decimal type
      */
-    protected RexNode encodeValue(RexNode value, RelDataType decimalType) {
-      return encodeValue(value, decimalType, false);
+    protected RexNode encodeValue(SqlParserPos pos, RexNode value, RelDataType decimalType) {
+      return encodeValue(pos, value, decimalType, false);
     }
 
     /**
@@ -598,11 +602,12 @@ public class ReduceDecimalsRule
      * @return the integer reinterpreted as an opaque decimal type
      */
     protected RexNode encodeValue(
+        SqlParserPos pos,
         RexNode value,
         RelDataType decimalType,
         boolean checkOverflow) {
       return builder.encodeIntervalOrDecimal(
-          value, decimalType, checkOverflow);
+          pos, value, decimalType, checkOverflow);
     }
 
     /**
@@ -616,8 +621,8 @@ public class ReduceDecimalsRule
      * @param node expression
      * @return a casted expression or the original expression
      */
-    protected RexNode ensureType(RelDataType type, RexNode node) {
-      return ensureType(type, node, true);
+    protected RexNode ensureType(SqlParserPos pos, RelDataType type, RexNode node) {
+      return ensureType(pos, type, node, true);
     }
 
     /**
@@ -633,10 +638,11 @@ public class ReduceDecimalsRule
      * @return a casted expression or the original expression
      */
     protected RexNode ensureType(
+        SqlParserPos pos,
         RelDataType type,
         RexNode node,
         boolean matchNullability) {
-      return builder.ensureType(type, node, matchNullability);
+      return builder.ensureType(pos, type, node, matchNullability);
     }
 
     protected RexNode makeCase(
@@ -666,36 +672,44 @@ public class ReduceDecimalsRule
     }
 
     protected RexNode makePlus(
+        SqlParserPos pos,
         RexNode a,
         RexNode b) {
       return builder.makeCall(
+          pos,
           SqlStdOperatorTable.PLUS,
           a,
           b);
     }
 
     protected RexNode makeMinus(
+        SqlParserPos pos,
         RexNode a,
         RexNode b) {
       return builder.makeCall(
+          pos,
           SqlStdOperatorTable.MINUS,
           a,
           b);
     }
 
     protected RexNode makeDivide(
+        SqlParserPos pos,
         RexNode a,
         RexNode b) {
       return builder.makeCall(
+          pos,
           SqlStdOperatorTable.DIVIDE_INTEGER,
           a,
           b);
     }
 
     protected RexNode makeMultiply(
+        SqlParserPos pos,
         RexNode a,
         RexNode b) {
       return builder.makeCall(
+          pos,
           SqlStdOperatorTable.MULTIPLY,
           a,
           b);
@@ -739,26 +753,27 @@ public class ReduceDecimalsRule
       assert SqlTypeUtil.isDecimal(fromType)
           || SqlTypeUtil.isDecimal(toType);
 
+      SqlParserPos pos = call.getParserPosition();
       if (SqlTypeUtil.isIntType(toType)) {
         // decimal to int
-        return ensureType(
+        return ensureType(pos,
             toType,
-            scaleDown(
-                decodeValue(operand),
+            scaleDown(pos,
+                decodeValue(pos, operand),
                 fromType.getScale()),
             false);
       } else if (SqlTypeUtil.isApproximateNumeric(toType)) {
         // decimal to floating point
-        return ensureType(
+        return ensureType(pos,
             toType,
-            scaleDownDouble(
-                decodeValue(operand),
+            scaleDownDouble(pos,
+                decodeValue(pos, operand),
                 fromType.getScale()),
             false);
       } else if (SqlTypeUtil.isApproximateNumeric(fromType)) {
         // real to decimal
-        return encodeValue(
-            ensureScale(
+        return encodeValue(pos,
+            ensureScale(pos,
                 operand,
                 0,
                 toType.getScale()),
@@ -784,8 +799,8 @@ public class ReduceDecimalsRule
 
       if (SqlTypeUtil.isIntType(fromType)) {
         // int to decimal
-        return encodeValue(
-            ensureScale(
+        return encodeValue(pos,
+            ensureScale(pos,
                 operand,
                 0,
                 toType.getScale()),
@@ -795,20 +810,20 @@ public class ReduceDecimalsRule
           SqlTypeUtil.isDecimal(fromType)
               && SqlTypeUtil.isDecimal(toType)) {
         // decimal to decimal
-        RexNode value = decodeValue(operand);
+        RexNode value = decodeValue(pos, operand);
         RexNode scaled;
         if (fromScale <= toScale) {
-          scaled = ensureScale(value, fromScale, toScale);
+          scaled = ensureScale(pos, value, fromScale, toScale);
         } else {
           if (toDigits == fromDigits) {
             // rounding away from zero may cause an overflow
             // for example: cast(9.99 as decimal(2,1))
             checkOverflow = true;
           }
-          scaled = scaleDown(value, fromScale - toScale);
+          scaled = scaleDown(pos, value, fromScale - toScale);
         }
 
-        return encodeValue(scaled, toType, checkOverflow);
+        return encodeValue(pos, scaled, toType, checkOverflow);
       } else {
         throw Util.needToImplement(
             "Reduce decimal cast from " + fromType + " to " + toType);
@@ -830,6 +845,7 @@ public class ReduceDecimalsRule
     }
 
     @Override public RexNode expand(RexCall call) {
+      final SqlParserPos pos = call.getParserPosition();
       List<RexNode> operands = call.operands;
       assert operands.size() == 2;
       RelDataType typeA = operands.get(0).getType();
@@ -843,13 +859,13 @@ public class ReduceDecimalsRule
         if (SqlTypeUtil.isApproximateNumeric(typeA)) {
           newOperands =
               ImmutableList.of(operands.get(0),
-                  ensureType(real8, operands.get(1)));
+                  ensureType(pos, real8, operands.get(1)));
         } else {
           newOperands =
-              ImmutableList.of(ensureType(real8, operands.get(0)),
+              ImmutableList.of(ensureType(pos, real8, operands.get(0)),
                   operands.get(1));
         }
-        return builder.makeCall(
+        return builder.makeCall(pos,
             call.getOperator(),
             newOperands);
       }
@@ -896,15 +912,16 @@ public class ReduceDecimalsRule
 
     private RexNode expandPlusMinus(RexCall call, List<RexNode> operands) {
       RelDataType outType = call.getType();
+      final SqlParserPos pos = call.getParserPosition();
       int outScale = outType.getScale();
-      return encodeValue(
-          builder.makeCall(
+      return encodeValue(pos,
+          builder.makeCall(pos,
               call.getOperator(),
-              ensureScale(
+              ensureScale(pos,
                   accessValue(operands.get(0)),
                   scaleA,
                   outScale),
-              ensureScale(
+              ensureScale(pos,
                   accessValue(operands.get(1)),
                   scaleB,
                   outScale)),
@@ -913,22 +930,24 @@ public class ReduceDecimalsRule
 
     private RexNode expandDivide(RexCall call, List<RexNode> operands) {
       RelDataType outType = call.getType();
+      final SqlParserPos pos = call.getParserPosition();
       RexNode dividend =
-          builder.makeCall(
+          builder.makeCall(pos,
               call.getOperator(),
-              ensureType(
+              ensureType(pos,
                   real8,
                   accessValue(operands.get(0))),
               ensureType(
+                  pos,
                   real8,
                   accessValue(operands.get(1))));
       int scaleDifference = outType.getScale() - scaleA + scaleB;
       RexNode rescale =
-          builder.makeCall(
+          builder.makeCall(pos,
               SqlStdOperatorTable.MULTIPLY,
               dividend,
               makeApproxScaleFactor(scaleDifference));
-      return encodeValue(rescale, outType);
+      return encodeValue(pos, rescale, outType);
     }
 
     private RexNode expandTimes(RexCall call, List<RexNode> operands) {
@@ -936,6 +955,7 @@ public class ReduceDecimalsRule
       // a number with scale = scaleA + scaleB. If the result type has
       // a lower scale, then the number should be scaled down.
       int divisor = scaleA + scaleB - call.getType().getScale();
+      final SqlParserPos pos = call.getParserPosition();
 
       if (builder.getTypeFactory().getTypeSystem().shouldUseDoubleMultiplication(
           builder.getTypeFactory(),
@@ -945,17 +965,17 @@ public class ReduceDecimalsRule
         // cast (a as double) * cast (b as double)
         //     / 10^divisor
         RexNode division =
-            makeDivide(
-                makeMultiply(
-                    ensureType(real8, accessValue(operands.get(0))),
-                    ensureType(real8, accessValue(operands.get(1)))),
+            makeDivide(pos,
+                makeMultiply(pos,
+                    ensureType(pos, real8, accessValue(operands.get(0))),
+                    ensureType(pos, real8, accessValue(operands.get(1)))),
                 makeApproxLiteral(BigDecimal.TEN.pow(divisor)));
-        return encodeValue(division, call.getType(), true);
+        return encodeValue(pos, division, call.getType(), true);
       } else {
         // Exact implementation: scaleDown(a * b)
-        return encodeValue(
-            scaleDown(
-                builder.makeCall(
+        return encodeValue(pos,
+            scaleDown(pos,
+                builder.makeCall(pos,
                     call.getOperator(),
                     accessValue(operands.get(0)),
                     accessValue(operands.get(1))),
@@ -965,20 +985,22 @@ public class ReduceDecimalsRule
     }
 
     private RexNode expandComparison(RexCall call, List<RexNode> operands) {
+      final SqlParserPos pos = call.getParserPosition();
       int commonScale = Math.max(scaleA, scaleB);
-      return builder.makeCall(
+      return builder.makeCall(pos,
           call.getOperator(),
-          ensureScale(
+          ensureScale(pos,
               accessValue(operands.get(0)),
               scaleA,
               commonScale),
-          ensureScale(
+          ensureScale(pos,
               accessValue(operands.get(1)),
               scaleB,
               commonScale));
     }
 
     private RexNode expandMod(RexCall call, List<RexNode> operands) {
+      final SqlParserPos pos = call.getParserPosition();
       assert SqlTypeUtil.isExactNumeric(requireNonNull(typeA, "typeA"));
       assert SqlTypeUtil.isExactNumeric(requireNonNull(typeB, "typeB"));
       if (scaleA != 0 || scaleB != 0) {
@@ -986,15 +1008,15 @@ public class ReduceDecimalsRule
             .ex();
       }
       RexNode result =
-          builder.makeCall(
+          builder.makeCall(pos,
               call.getOperator(),
               accessValue(operands.get(0)),
               accessValue(operands.get(1)));
       RelDataType retType = call.getType();
       if (SqlTypeUtil.isDecimal(retType)) {
-        return encodeValue(result, retType);
+        return encodeValue(pos, result, retType);
       }
-      return ensureType(
+      return ensureType(pos,
           call.getType(),
           result);
     }
@@ -1018,9 +1040,10 @@ public class ReduceDecimalsRule
 
     @Override public RexNode expand(RexCall call) {
       assert call.getOperator() == SqlStdOperatorTable.FLOOR;
+      final SqlParserPos pos = call.getParserPosition();
       RexNode decValue = call.operands.get(0);
       int scale = decValue.getType().getScale();
-      RexNode value = decodeValue(decValue);
+      RexNode value = decodeValue(pos, decValue);
       final RelDataTypeSystem typeSystem =
           builder.getTypeFactory().getTypeSystem();
 
@@ -1039,12 +1062,12 @@ public class ReduceDecimalsRule
         rewrite =
             makeCase(
                 makeIsNegative(value),
-                makeDivide(
-                    makePlus(value, round),
+                makeDivide(pos,
+                    makePlus(pos, value, round),
                     scaleFactor),
-                makeDivide(value, scaleFactor));
+                makeDivide(pos, value, scaleFactor));
       }
-      return encodeValue(
+      return encodeValue(pos,
           rewrite,
           call.getType());
     }
@@ -1068,9 +1091,10 @@ public class ReduceDecimalsRule
 
     @Override public RexNode expand(RexCall call) {
       assert call.getOperator() == SqlStdOperatorTable.CEIL;
+      final SqlParserPos pos = call.getParserPosition();
       RexNode decValue = call.operands.get(0);
       int scale = decValue.getType().getScale();
-      RexNode value = decodeValue(decValue);
+      RexNode value = decodeValue(pos, decValue);
       final RelDataTypeSystem typeSystem =
           builder.getTypeFactory().getTypeSystem();
 
@@ -1089,12 +1113,12 @@ public class ReduceDecimalsRule
         rewrite =
             makeCase(
                 makeIsPositive(value),
-                makeDivide(
-                    makePlus(value, round),
+                makeDivide(pos,
+                    makePlus(pos, value, round),
                     scaleFactor),
-                makeDivide(value, scaleFactor));
+                makeDivide(pos, value, scaleFactor));
       }
-      return encodeValue(
+      return encodeValue(pos,
           rewrite,
           call.getType());
     }
@@ -1118,7 +1142,8 @@ public class ReduceDecimalsRule
     }
 
     @Override public RexNode expand(RexCall call) {
-      RelDataType retType = call.getType();
+      final SqlParserPos pos = call.getParserPosition();
+      final RelDataType retType = call.getType();
       int argCount = call.operands.size();
       ImmutableList.Builder<RexNode> opBuilder = ImmutableList.builder();
 
@@ -1128,17 +1153,17 @@ public class ReduceDecimalsRule
           opBuilder.add(call.operands.get(i));
           continue;
         }
-        RexNode expr = ensureType(retType, call.operands.get(i), false);
+        RexNode expr = ensureType(pos, retType, call.operands.get(i), false);
         if (SqlTypeUtil.isDecimal(retType)) {
-          expr = decodeValue(expr);
+          expr = decodeValue(pos, expr);
         }
         opBuilder.add(expr);
       }
 
       RexNode newCall =
-          builder.makeCall(retType, call.getOperator(), opBuilder.build());
+          builder.makeCall(pos, retType, call.getOperator(), opBuilder.build());
       if (SqlTypeUtil.isDecimal(retType)) {
-        newCall = encodeValue(newCall, retType);
+        newCall = encodeValue(pos, newCall, retType);
       }
       return newCall;
     }
@@ -1159,6 +1184,7 @@ public class ReduceDecimalsRule
     }
 
     @Override public RexNode expand(RexCall call) {
+      final SqlParserPos pos = call.getParserPosition();
       ImmutableList.Builder<RexNode> opBuilder = ImmutableList.builder();
       for (RexNode operand : call.operands) {
         if (SqlTypeUtil.isNumeric(operand.getType())) {
@@ -1169,10 +1195,10 @@ public class ReduceDecimalsRule
       }
 
       RexNode newCall =
-          builder.makeCall(call.getType(), call.getOperator(),
+          builder.makeCall(pos, call.getType(), call.getOperator(),
               opBuilder.build());
       if (SqlTypeUtil.isDecimal(call.getType())) {
-        return encodeValue(
+        return encodeValue(pos,
             newCall,
             call.getType());
       } else {
@@ -1212,24 +1238,25 @@ public class ReduceDecimalsRule
     public abstract RelDataType getArgType(RexCall call, int ordinal);
 
     @Override public RexNode expand(RexCall call) {
+      final SqlParserPos pos = call.getParserPosition();
       ImmutableList.Builder<RexNode> opBuilder = ImmutableList.builder();
 
       for (Ord<RexNode> operand : Ord.zip(call.operands)) {
         RelDataType targetType = getArgType(call, operand.i);
         if (SqlTypeUtil.isDecimal(operand.e.getType())) {
-          opBuilder.add(ensureType(targetType, operand.e, true));
+          opBuilder.add(ensureType(pos, targetType, operand.e, true));
         } else {
           opBuilder.add(operand.e);
         }
       }
 
       RexNode ret =
-          builder.makeCall(
+          builder.makeCall(pos,
               call.getType(),
               call.getOperator(),
               opBuilder.build());
       ret =
-          ensureType(
+          ensureType(pos,
               call.getType(),
               ret,
               true);
diff --git a/core/src/main/java/org/apache/calcite/rel/rules/materialize/MaterializedViewAggregateRule.java b/core/src/main/java/org/apache/calcite/rel/rules/materialize/MaterializedViewAggregateRule.java
index 579efc3336..e6cf9e1058 100644
--- a/core/src/main/java/org/apache/calcite/rel/rules/materialize/MaterializedViewAggregateRule.java
+++ b/core/src/main/java/org/apache/calcite/rel/rules/materialize/MaterializedViewAggregateRule.java
@@ -348,7 +348,7 @@ public abstract class MaterializedViewAggregateRule<C extends MaterializedViewAg
           rexBuilder.makeInputRef(relBuilder.peek(),
               aggregate.getGroupCount() + i);
       aggregateCalls.add(
-          relBuilder.aggregateCall(rollupAgg, operand)
+          relBuilder.aggregateCall(aggCall.getParserPosition(), rollupAgg, operand)
               .distinct(aggCall.isDistinct())
               .approximate(aggCall.isApproximate())
               .as(aggCall.name));
@@ -606,7 +606,7 @@ public abstract class MaterializedViewAggregateRule<C extends MaterializedViewAg
             queryAggregate.getGroupCount() + aggregateCalls.size());
         final RexInputRef operand = rexBuilder.makeInputRef(input, k);
         aggregateCalls.add(
-            relBuilder.aggregateCall(rollupAgg, operand)
+            relBuilder.aggregateCall(queryAggCall.getParserPosition(), rollupAgg, operand)
                 .approximate(queryAggCall.isApproximate())
                 .distinct(queryAggCall.isDistinct())
                 .as(queryAggCall.name));
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 18252de644..5cc4b21170 100644
--- a/core/src/main/java/org/apache/calcite/rex/RexBuilder.java
+++ b/core/src/main/java/org/apache/calcite/rex/RexBuilder.java
@@ -37,6 +37,7 @@ import org.apache.calcite.sql.SqlUtil;
 import org.apache.calcite.sql.fun.SqlCountAggFunction;
 import org.apache.calcite.sql.fun.SqlLibraryOperators;
 import org.apache.calcite.sql.fun.SqlStdOperatorTable;
+import org.apache.calcite.sql.parser.SqlParserPos;
 import org.apache.calcite.sql.type.ArraySqlType;
 import org.apache.calcite.sql.type.MapSqlType;
 import org.apache.calcite.sql.type.MultisetSqlType;
@@ -257,33 +258,79 @@ public class RexBuilder {
       RelDataType returnType,
       SqlOperator op,
       List<RexNode> exprs) {
-    return new RexCall(returnType, op, exprs);
+    return makeCall(SqlParserPos.ZERO, returnType, op, exprs);
+  }
+
+  /**
+   * Creates a call with a list of arguments and a predetermined type.
+   *
+   * @param pos should be different from ZERO if the call can
+   *            fail at runtime.
+   */
+  public RexNode makeCall(
+      SqlParserPos pos,
+      RelDataType returnType,
+      SqlOperator op,
+      List<RexNode> exprs) {
+    return new RexCall(pos, returnType, op, exprs);
   }
 
   /**
    * Creates a call with an array of arguments.
    *
    * <p>If you already know the return type of the call, then
-   * {@link #makeCall(org.apache.calcite.rel.type.RelDataType, org.apache.calcite.sql.SqlOperator, java.util.List)}
+   * {@link #makeCall(SqlParserPos, RelDataType, SqlOperator, List)}
    * is preferred.
    */
   public RexNode makeCall(
       SqlOperator op,
       List<? extends RexNode> exprs) {
+    return makeCall(SqlParserPos.ZERO, op, exprs);
+  }
+
+  /**
+   * Creates a call with an array of arguments.
+   *
+   * @param pos should be different from ZERO if the call can
+   *            fail at runtime.
+   *
+   * <p>If you already know the return type of the call, then
+   * {@link #makeCall(SqlParserPos, RelDataType, SqlOperator, List)}
+   * is preferred.
+   */
+  public RexNode makeCall(
+      SqlParserPos pos, SqlOperator op,
+      List<? extends RexNode> exprs) {
     final RelDataType type = deriveReturnType(op, exprs);
-    return new RexCall(type, op, exprs);
+    return new RexCall(pos, type, op, exprs);
+  }
+
+  /**
+   * Creates a call with a list of arguments.
+   *
+   * <p>Equivalent to
+   * <code>makeCall(ZERO, op, exprList.toArray(new RexNode[exprList.size()]))</code>.
+   */
+  public final RexNode makeCall(
+      SqlOperator op,
+      RexNode... exprs) {
+    return makeCall(SqlParserPos.ZERO, op, exprs);
   }
 
   /**
    * Creates a call with a list of arguments.
    *
+   * @param pos should be different from ZERO if the call can
+   *            fail at runtime.
+   *
    * <p>Equivalent to
-   * <code>makeCall(op, exprList.toArray(new RexNode[exprList.size()]))</code>.
+   * <code>makeCall(pos, op, exprList.toArray(new RexNode[exprList.size()]))</code>.
    */
   public final RexNode makeCall(
+      SqlParserPos pos,
       SqlOperator op,
       RexNode... exprs) {
-    return makeCall(op, ImmutableList.copyOf(exprs));
+    return makeCall(pos, op, ImmutableList.copyOf(exprs));
   }
 
   /**
@@ -585,6 +632,21 @@ public class RexBuilder {
     return makeCast(type, exp, false, false, constantNull);
   }
 
+  /**
+   * Creates a call to the CAST operator.
+   *
+   * @param pos  Parser position of the cast.
+   * @param type Type to cast to
+   * @param exp  Expression being cast
+   * @return Call to CAST operator
+   */
+  public RexNode makeCast(
+      SqlParserPos pos,
+      RelDataType type,
+      RexNode exp) {
+    return makeCast(pos, type, exp, false, false, constantNull);
+  }
+
   @Deprecated // to be removed before 2.0
   public RexNode makeCast(
       RelDataType type,
@@ -593,6 +655,31 @@ public class RexBuilder {
     return makeCast(type, exp, matchNullability, false, constantNull);
   }
 
+  /**
+   * Creates a call to the CAST operator, expanding if possible, and optionally
+   * also preserving nullability, and optionally in safe mode.
+   *
+   * <p>Tries to expand the cast, and therefore the result may be something
+   * other than a {@link RexCall} to the CAST operator, such as a
+   * {@link RexLiteral}.
+   *
+   * @param pos  Parser position
+   * @param type Type to cast to
+   * @param exp  Expression being cast
+   * @param matchNullability Whether to ensure the result has the same
+   * nullability as {@code type}
+   * @param safe Whether to return NULL if cast fails
+   * @return Call to CAST operator
+   */
+  public RexNode makeCast(
+      SqlParserPos pos,
+      RelDataType type,
+      RexNode exp,
+      boolean matchNullability,
+      boolean safe) {
+    return makeCast(pos, type, exp, matchNullability, safe, constantNull);
+  }
+
   /**
    * Creates a call to the CAST operator, expanding if possible, and optionally
    * also preserving nullability, and optionally in safe mode.
@@ -639,6 +726,33 @@ public class RexBuilder {
       boolean matchNullability,
       boolean safe,
       RexLiteral format) {
+    return makeCast(SqlParserPos.ZERO, type, exp, matchNullability, safe, format);
+  }
+
+  /**
+   * Creates a call to the CAST operator, expanding if possible, and optionally
+   * also preserving nullability, and optionally in safe mode.
+   *
+   * <p>Tries to expand the cast, and therefore the result may be something
+   * other than a {@link RexCall} to the CAST operator, such as a
+   * {@link RexLiteral}.
+   *
+   * @param pos  Parser position
+   * @param type Type to cast to
+   * @param exp  Expression being cast
+   * @param matchNullability Whether to ensure the result has the same
+   * nullability as {@code type}
+   * @param safe Whether to return NULL if cast fails
+   * @param format Type Format to cast into
+   * @return Call to CAST operator
+   */
+  public RexNode makeCast(
+      SqlParserPos pos,
+      RelDataType type,
+      RexNode exp,
+      boolean matchNullability,
+      boolean safe,
+      RexLiteral format) {
     final SqlTypeName sqlType = type.getSqlTypeName();
     if (exp instanceof RexLiteral) {
       RexLiteral literal = (RexLiteral) exp;
@@ -705,13 +819,13 @@ public class RexBuilder {
         if (type.isNullable()
             && !literal2.getType().isNullable()
             && matchNullability) {
-          return makeAbstractCast(type, literal2, safe, format);
+          return makeAbstractCast(pos, type, literal2, safe, format);
         }
         return literal2;
       }
     } else if (SqlTypeUtil.isExactNumeric(type)
         && SqlTypeUtil.isInterval(exp.getType())) {
-      return makeCastIntervalToExact(type, exp);
+      return makeCastIntervalToExact(pos, type, exp);
     } else if (sqlType == SqlTypeName.BOOLEAN
         && SqlTypeUtil.isExactNumeric(exp.getType())) {
       return makeCastExactToBoolean(type, exp);
@@ -719,7 +833,7 @@ public class RexBuilder {
         && SqlTypeUtil.isExactNumeric(type)) {
       return makeCastBooleanToExact(type, exp);
     }
-    return makeAbstractCast(type, exp, safe, format);
+    return makeAbstractCast(pos, type, exp, safe, format);
   }
 
   /** Returns the lowest granularity unit for the given unit.
@@ -824,18 +938,23 @@ public class RexBuilder {
             casted, makeNullLiteral(toType)));
   }
 
-  private RexNode makeCastIntervalToExact(RelDataType toType, RexNode exp) {
+  private RexNode makeCastIntervalToExact(SqlParserPos pos, RelDataType toType, RexNode exp) {
     final TimeUnit endUnit = exp.getType().getSqlTypeName().getEndUnit();
     final TimeUnit baseUnit = baseUnit(exp.getType().getSqlTypeName());
     final BigDecimal multiplier = baseUnit.multiplier;
     final BigDecimal divider = endUnit.multiplier;
     RexNode value =
-        multiplyDivide(decodeIntervalOrDecimal(exp), multiplier, divider);
-    return ensureType(toType, value, false);
+        multiplyDivide(pos, decodeIntervalOrDecimal(pos, exp), multiplier, divider);
+    return ensureType(pos, toType, value, false);
   }
 
   public RexNode multiplyDivide(RexNode e, BigDecimal multiplier,
       BigDecimal divider) {
+    return multiplyDivide(SqlParserPos.ZERO, e, multiplier, divider);
+  }
+
+  public RexNode multiplyDivide(SqlParserPos pos, RexNode e, BigDecimal multiplier,
+      BigDecimal divider) {
     assert multiplier.signum() > 0;
     assert divider.signum() > 0;
     switch (multiplier.compareTo(divider)) {
@@ -843,12 +962,12 @@ public class RexBuilder {
       return e;
     case 1:
       // E.g. multiplyDivide(e, 1000, 10) ==> e * 100
-      return makeCall(SqlStdOperatorTable.MULTIPLY, e,
+      return makeCall(pos, SqlStdOperatorTable.MULTIPLY, e,
           makeExactLiteral(
               multiplier.divide(divider, RoundingMode.UNNECESSARY)));
     case -1:
       // E.g. multiplyDivide(e, 10, 1000) ==> e / 100
-      return makeCall(SqlStdOperatorTable.DIVIDE_INTEGER, e,
+      return makeCall(pos, SqlStdOperatorTable.DIVIDE_INTEGER, e,
           makeExactLiteral(
               divider.divide(multiplier, RoundingMode.UNNECESSARY)));
     default:
@@ -876,10 +995,18 @@ public class RexBuilder {
       RexNode value,
       RelDataType type,
       boolean checkOverflow) {
+    return encodeIntervalOrDecimal(SqlParserPos.ZERO, value, type, checkOverflow);
+  }
+
+  public RexNode encodeIntervalOrDecimal(
+      SqlParserPos pos,
+      RexNode value,
+      RelDataType type,
+      boolean checkOverflow) {
     RelDataType bigintType =
         typeFactory.createSqlType(SqlTypeName.BIGINT);
-    RexNode cast = ensureType(bigintType, value, true);
-    return makeReinterpretCast(type, cast, makeLiteral(checkOverflow));
+    RexNode cast = ensureType(pos, bigintType, value, true);
+    return makeReinterpretCast(pos, type, cast, makeLiteral(checkOverflow));
   }
 
   /**
@@ -889,10 +1016,14 @@ public class RexBuilder {
    * @return an integer representation of the decimal value
    */
   public RexNode decodeIntervalOrDecimal(RexNode node) {
+    return decodeIntervalOrDecimal(SqlParserPos.ZERO, node);
+  }
+
+  public RexNode decodeIntervalOrDecimal(SqlParserPos pos, RexNode node) {
     assert SqlTypeUtil.isDecimal(node.getType())
         || SqlTypeUtil.isInterval(node.getType());
     RelDataType bigintType = typeFactory.createSqlType(SqlTypeName.BIGINT);
-    return makeReinterpretCast(
+    return makeReinterpretCast(pos,
         matchNullability(bigintType, node), node, makeLiteral(false));
   }
 
@@ -910,10 +1041,24 @@ public class RexBuilder {
    * @return Call to CAST operator
    */
   public RexNode makeAbstractCast(RelDataType type, RexNode exp, boolean safe) {
+    return makeAbstractCast(SqlParserPos.ZERO, type, exp, safe);
+  }
+
+  /**
+   * Creates a call to CAST or SAFE_CAST operator.
+   *
+   * @param pos  Parser position.
+   * @param type Type to cast to
+   * @param exp  Expression being cast
+   * @param safe Whether to return NULL if cast fails
+   * @return Call to CAST operator
+   * Casts can fail at runtime, so we expect position information.
+   */
+  public RexNode makeAbstractCast(SqlParserPos pos, RelDataType type, RexNode exp, boolean safe) {
     final SqlOperator operator =
         safe ? SqlLibraryOperators.SAFE_CAST
             : SqlStdOperatorTable.CAST;
-    return new RexCall(type, operator, ImmutableList.of(exp));
+    return new RexCall(pos, type, operator, ImmutableList.of(exp));
   }
 
   /**
@@ -926,24 +1071,56 @@ public class RexBuilder {
    * @return Call to CAST operator
    */
   public RexNode makeAbstractCast(RelDataType type, RexNode exp, boolean safe, RexLiteral format) {
+    return makeAbstractCast(SqlParserPos.ZERO, type, exp, safe, format);
+  }
+
+  /**
+     * Creates a call to CAST or SAFE_CAST operator with a FORMAT clause.
+     *
+     * @param pos  Parser position
+     * @param type Type to cast to
+     * @param exp  Expression being cast
+     * @param safe Whether to return NULL if cast fails
+     * @param format Conversion format for target type
+     * @return Call to CAST operator
+     */
+  public RexNode makeAbstractCast(
+      SqlParserPos pos, RelDataType type, RexNode exp, boolean safe, RexLiteral format) {
     final SqlOperator operator =
         safe ? SqlLibraryOperators.SAFE_CAST
             : SqlStdOperatorTable.CAST;
     if (format.isNull()) {
-      return new RexCall(type, operator, ImmutableList.of(exp));
+      return new RexCall(pos, type, operator, ImmutableList.of(exp));
     }
-    return new RexCall(type, operator, ImmutableList.of(exp, format));
+    return new RexCall(pos, type, operator, ImmutableList.of(exp, format));
+  }
+
+  /**
+   * Makes a reinterpret cast.
+   *
+   * @param type          type returned by the cast
+   * @param exp           expression to be casted
+   * @param checkOverflow whether an overflow check is required
+   * @return a RexCall with two operands and a special return type
+   */
+  public RexNode makeReinterpretCast(
+      RelDataType type,
+      RexNode exp,
+      RexNode checkOverflow) {
+    return makeReinterpretCast(SqlParserPos.ZERO, type, exp, checkOverflow);
   }
 
   /**
    * Makes a reinterpret cast.
    *
+   * @param pos           parser position
    * @param type          type returned by the cast
    * @param exp           expression to be cast
    * @param checkOverflow whether an overflow check is required
    * @return a RexCall with two operands and a special return type
    */
   public RexNode makeReinterpretCast(
+      SqlParserPos pos,
       RelDataType type,
       RexNode exp,
       RexNode checkOverflow) {
@@ -954,6 +1131,7 @@ public class RexBuilder {
       args = ImmutableList.of(exp);
     }
     return new RexCall(
+        pos,
         type,
         SqlStdOperatorTable.REINTERPRET,
         args);
@@ -986,7 +1164,7 @@ public class RexBuilder {
     }
     final RelDataType type2 =
         typeFactory.createTypeWithNullability(type, nullability);
-    return makeAbstractCast(type2, exp, false);
+    return makeAbstractCast(SqlParserPos.ZERO, type2, exp, false);
   }
 
   /**
@@ -1319,6 +1497,27 @@ public class RexBuilder {
       RelDataType type,
       RexNode node,
       boolean matchNullability) {
+    return ensureType(SqlParserPos.ZERO, type, node, matchNullability);
+  }
+
+    /**
+     * Ensures expression is interpreted as a specified type. The returned
+     * expression may be wrapped with a cast.
+     *
+     * @param pos              parser position
+     * @param type             desired type
+     * @param node             expression
+     * @param matchNullability whether to correct nullability of specified
+     *                         type to match the expression; this usually should
+     *                         be true, except for explicit casts which can
+     *                         override default nullability
+     * @return a casted expression or the original expression
+     */
+  public RexNode ensureType(
+      SqlParserPos pos,
+      RelDataType type,
+      RexNode node,
+      boolean matchNullability) {
     RelDataType targetType = type;
     if (matchNullability) {
       targetType = matchNullability(type, node);
@@ -1331,7 +1530,7 @@ public class RexBuilder {
     }
 
     if (!node.getType().equals(targetType)) {
-      return makeCast(targetType, node);
+      return makeCast(pos, targetType, node);
     }
     return node;
   }
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 b8089f775d..ecd3cccb2c 100644
--- a/core/src/main/java/org/apache/calcite/rex/RexCall.java
+++ b/core/src/main/java/org/apache/calcite/rex/RexCall.java
@@ -21,6 +21,7 @@ import org.apache.calcite.rel.type.RelDataType;
 import org.apache.calcite.sql.SqlKind;
 import org.apache.calcite.sql.SqlOperator;
 import org.apache.calcite.sql.SqlSyntax;
+import org.apache.calcite.sql.parser.SqlParserPos;
 import org.apache.calcite.sql.type.SqlTypeName;
 import org.apache.calcite.sql.type.SqlTypeUtil;
 import org.apache.calcite.util.Litmus;
@@ -56,6 +57,12 @@ public class RexCall extends RexNode {
 
   //~ Instance fields --------------------------------------------------------
 
+  /** In the calls which can produce runtime errors we carry
+   * 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. */
+  public final SqlParserPos pos;
   public final SqlOperator op;
   public final ImmutableList<RexNode> operands;
   public final RelDataType type;
@@ -74,9 +81,11 @@ public class RexCall extends RexNode {
   //~ Constructors -----------------------------------------------------------
 
   protected RexCall(
+      SqlParserPos pos,
       RelDataType type,
       SqlOperator operator,
       List<? extends RexNode> operands) {
+    this.pos = pos;
     this.type = requireNonNull(type, "type");
     this.op = requireNonNull(operator, "operator");
     this.operands = ImmutableList.copyOf(operands);
@@ -85,6 +94,13 @@ public class RexCall extends RexNode {
     assert operator.kind != SqlKind.IN || this instanceof RexSubQuery;
   }
 
+  protected RexCall(
+      RelDataType type,
+      SqlOperator operator,
+      List<? extends RexNode> operands) {
+    this(SqlParserPos.ZERO, type, operator, operands);
+  }
+
   //~ Methods ----------------------------------------------------------------
 
   /**
@@ -176,6 +192,10 @@ public class RexCall extends RexNode {
     return sb.toString();
   }
 
+  public SqlParserPos getParserPosition() {
+    return this.pos;
+  }
+
   @Override public final String toString() {
     return computeDigest(digestWithType());
   }
@@ -211,7 +231,7 @@ public class RexCall extends RexNode {
     case CAST:
       return operands.get(0).isAlwaysTrue();
     case SEARCH:
-      final Sarg sarg = ((RexLiteral) operands.get(1)).getValueAs(Sarg.class);
+      final Sarg<?> sarg = ((RexLiteral) operands.get(1)).getValueAs(Sarg.class);
       return requireNonNull(sarg, "sarg").isAll()
           && (sarg.nullAs == RexUnknownAs.TRUE
               || !operands.get(0).getType().isNullable());
@@ -233,7 +253,7 @@ public class RexCall extends RexNode {
     case CAST:
       return operands.get(0).isAlwaysFalse();
     case SEARCH:
-      final Sarg sarg = ((RexLiteral) operands.get(1)).getValueAs(Sarg.class);
+      final Sarg<?> sarg = ((RexLiteral) operands.get(1)).getValueAs(Sarg.class);
       return requireNonNull(sarg, "sarg").isNone()
           && (sarg.nullAs == RexUnknownAs.FALSE
               || !operands.get(0).getType().isNullable());
@@ -270,7 +290,7 @@ public class RexCall extends RexNode {
    * @return New call
    */
   public RexCall clone(RelDataType type, List<RexNode> operands) {
-    return new RexCall(type, op, operands);
+    return new RexCall(pos, type, op, operands);
   }
 
   private Pair<SqlOperator, List<RexNode>> getNormalized() {
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 e73a68d5f6..1f5700e482 100644
--- a/core/src/main/java/org/apache/calcite/rex/RexCopier.java
+++ b/core/src/main/java/org/apache/calcite/rex/RexCopier.java
@@ -57,7 +57,7 @@ class RexCopier extends RexShuttle {
 
   @Override public RexNode visitCall(final RexCall call) {
     final boolean[] update = null;
-    return builder.makeCall(copy(call.getType()),
+    return builder.makeCall(call.getParserPosition(), copy(call.getType()),
         call.getOperator(),
         visitList(call.getOperands(), update));
   }
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 6909d35c96..e05f9640f1 100644
--- a/core/src/main/java/org/apache/calcite/rex/RexSimplify.java
+++ b/core/src/main/java/org/apache/calcite/rex/RexSimplify.java
@@ -399,7 +399,7 @@ public class RexSimplify {
     if (e.operands.equals(operands)) {
       return e;
     }
-    return rexBuilder.makeCall(e.getType(), e.getOperator(), operands);
+    return rexBuilder.makeCall(e.getParserPosition(), e.getType(), e.getOperator(), operands);
   }
 
   /**
@@ -449,7 +449,7 @@ public class RexSimplify {
       // return the other operand.
       RexNode other = e.getOperands().get((zeroIndex + 1) % 2);
       return other.getType().equals(e.getType())
-          ? other : rexBuilder.makeCast(e.getType(), other);
+          ? other : rexBuilder.makeCast(e.getParserPosition(), e.getType(), other);
     }
     return simplifyGenericNode(e);
   }
@@ -459,7 +459,7 @@ public class RexSimplify {
     if (zeroIndex == 1) {
       RexNode leftOperand = e.getOperands().get(0);
       return leftOperand.getType().equals(e.getType())
-          ? leftOperand : rexBuilder.makeCast(e.getType(), leftOperand);
+          ? leftOperand : rexBuilder.makeCast(e.getParserPosition(), e.getType(), leftOperand);
     }
     return simplifyGenericNode(e);
   }
@@ -470,7 +470,7 @@ public class RexSimplify {
       // return the other operand.
       RexNode other = e.getOperands().get((oneIndex + 1) % 2);
       return other.getType().equals(e.getType())
-          ? other : rexBuilder.makeCast(e.getType(), other);
+          ? other : rexBuilder.makeCast(e.getParserPosition(), e.getType(), other);
     }
     return simplifyGenericNode(e);
   }
@@ -480,7 +480,7 @@ public class RexSimplify {
     if (oneIndex == 1) {
       RexNode leftOperand = e.getOperands().get(0);
       return leftOperand.getType().equals(e.getType())
-          ? leftOperand : rexBuilder.makeCast(e.getType(), leftOperand);
+          ? leftOperand : rexBuilder.makeCast(e.getParserPosition(), e.getType(), leftOperand);
     }
     return simplifyGenericNode(e);
   }
@@ -491,8 +491,9 @@ public class RexSimplify {
       if ("%".equals(literal.getValueAs(String.class))) {
         // "x LIKE '%'" simplifies to "x = x"
         final RexNode x = e.operands.get(0);
-        return simplify(rexBuilder.makeCall(SqlStdOperatorTable.EQUALS, x, x),
-            unknownAs);
+        return simplify(
+            rexBuilder.makeCall(
+                e.getParserPosition(), SqlStdOperatorTable.EQUALS, x, x), unknownAs);
       }
     }
     return simplifyGenericNode(e);
@@ -522,18 +523,18 @@ public class RexSimplify {
       case LESS_THAN_OR_EQUAL:
         // "x = x" simplifies to "null or x is not null" (similarly <= and >=)
         newExpr =
-            rexBuilder.makeCall(SqlStdOperatorTable.OR,
+            rexBuilder.makeCall(e.getParserPosition(), SqlStdOperatorTable.OR,
                 rexBuilder.makeNullLiteral(e.getType()),
-                rexBuilder.makeCall(SqlStdOperatorTable.IS_NOT_NULL, o0));
+                rexBuilder.makeCall(e.getParserPosition(), SqlStdOperatorTable.IS_NOT_NULL, o0));
         return simplify(newExpr, unknownAs);
       case NOT_EQUALS:
       case LESS_THAN:
       case GREATER_THAN:
         // "x != x" simplifies to "null and x is null" (similarly < and >)
         newExpr =
-            rexBuilder.makeCall(SqlStdOperatorTable.AND,
+            rexBuilder.makeCall(e.getParserPosition(), SqlStdOperatorTable.AND,
                 rexBuilder.makeNullLiteral(e.getType()),
-                rexBuilder.makeCall(SqlStdOperatorTable.IS_NULL, o0));
+                rexBuilder.makeCall(e.getParserPosition(), SqlStdOperatorTable.IS_NULL, o0));
         return simplify(newExpr, unknownAs);
       default:
         // unknown kind
@@ -541,7 +542,9 @@ public class RexSimplify {
     }
 
     if (o0.getType().getSqlTypeName() == SqlTypeName.BOOLEAN) {
-      Comparison cmp = Comparison.of(rexBuilder.makeCall(e.getOperator(), o0, o1), node -> true);
+      Comparison cmp =
+          Comparison.of(
+              rexBuilder.makeCall(e.getParserPosition(), e.getOperator(), o0, o1), node -> true);
       if (cmp != null) {
         if (cmp.literal.isAlwaysTrue()) {
           switch (cmp.kind) {
@@ -635,7 +638,7 @@ public class RexSimplify {
     if (operands.equals(e.operands)) {
       e2 = e;
     } else {
-      e2 = rexBuilder.makeCall(e.op, operands);
+      e2 = rexBuilder.makeCall(e.getParserPosition(), e.op, operands);
     }
     return simplifyUsingPredicates(e2, clazz);
   }
@@ -772,7 +775,8 @@ public class RexSimplify {
         newOperands.add(simplify(not(operand), unknownAs));
       }
       return simplify(
-          rexBuilder.makeCall(SqlStdOperatorTable.OR, newOperands), unknownAs);
+          rexBuilder.makeCall(
+              call.getParserPosition(), SqlStdOperatorTable.OR, newOperands), unknownAs);
 
     case OR:
       // NOT distributivity for OR
@@ -781,7 +785,8 @@ public class RexSimplify {
         newOperands.add(simplify(not(operand), unknownAs));
       }
       return simplify(
-          rexBuilder.makeCall(SqlStdOperatorTable.AND, newOperands), unknownAs);
+          rexBuilder.makeCall(
+              call.getParserPosition(), SqlStdOperatorTable.AND, newOperands), unknownAs);
 
     case CASE:
       newOperands = new ArrayList<>();
@@ -795,7 +800,8 @@ public class RexSimplify {
         }
       }
       return simplify(
-          rexBuilder.makeCall(SqlStdOperatorTable.CASE, newOperands), unknownAs);
+          rexBuilder.makeCall(
+              call.getParserPosition(), SqlStdOperatorTable.CASE, newOperands), unknownAs);
 
     case IN:
     case NOT_IN:
@@ -806,13 +812,14 @@ public class RexSimplify {
       final SqlKind negateKind = a.getKind().negate();
       if (a.getKind() != negateKind) {
         return simplify(
-            rexBuilder.makeCall(RexUtil.op(negateKind),
+            rexBuilder.makeCall(
+                call.getParserPosition(), RexUtil.op(negateKind),
                 ((RexCall) a).getOperands()), unknownAs);
       }
       final SqlKind negateKind2 = a.getKind().negateNullSafe();
       if (a.getKind() != negateKind2) {
         return simplify(
-            rexBuilder.makeCall(RexUtil.op(negateKind2),
+            rexBuilder.makeCall(call.getParserPosition(), RexUtil.op(negateKind2),
                 ((RexCall) a).getOperands()), unknownAs);
       }
     }
@@ -1213,7 +1220,7 @@ public class RexSimplify {
       if (sameTypeOrNarrowsNullability(caseType, value.getType())) {
         return value;
       } else {
-        return rexBuilder.makeAbstractCast(caseType, value, false);
+        return rexBuilder.makeAbstractCast(call.getParserPosition(), caseType, value, false);
       }
     }
 
@@ -1228,7 +1235,7 @@ public class RexSimplify {
           if (!simplified.getType().isNullable()) {
             return simplified;
           } else {
-            return rexBuilder.makeCast(call.getType(), simplified);
+            return rexBuilder.makeCast(call.getParserPosition(), call.getType(), simplified);
           }
         }
       }
@@ -1237,7 +1244,7 @@ public class RexSimplify {
     if (newOperands.equals(call.getOperands())) {
       return call;
     }
-    return rexBuilder.makeCall(SqlStdOperatorTable.CASE, newOperands);
+    return rexBuilder.makeCall(call.getParserPosition(), SqlStdOperatorTable.CASE, newOperands);
   }
 
   /**
@@ -2244,7 +2251,7 @@ public class RexSimplify {
           || operand.getType().getSqlTypeName() != SqlTypeName.CHAR)
           && SqlTypeCoercionRule.instance()
           .canApplyFrom(intExpr.getType().getSqlTypeName(), e.getType().getSqlTypeName())) {
-        return rexBuilder.makeCast(e.getType(), intExpr);
+        return rexBuilder.makeCast(e.getParserPosition(), e.getType(), intExpr);
       }
     }
     final boolean safe = e.getKind() == SqlKind.SAFE_CAST;
@@ -2258,7 +2265,7 @@ public class RexSimplify {
       // makeCast and canRemoveCastFromLiteral have the same logic, so we are
       // sure to be able to remove the cast.
       if (rexBuilder.canRemoveCastFromLiteral(e.getType(), value, typeName)) {
-        return rexBuilder.makeCast(e.getType(), operand);
+        return rexBuilder.makeCast(e.getParserPosition(), e.getType(), operand);
       }
 
       // Next, try to convert the value to a different type,
@@ -2277,7 +2284,7 @@ public class RexSimplify {
       }
       final List<RexNode> reducedValues = new ArrayList<>();
       final RexNode simplifiedExpr = e.operandCount() == 2
-          ? rexBuilder.makeCast(e.getType(), operand, safe, safe,
+          ? rexBuilder.makeCast(e.getParserPosition(), e.getType(), operand, safe, safe,
           (RexLiteral) e.getOperands().get(1))
           : rexBuilder.makeCast(e.getType(), operand, safe, safe);
       executor.reduce(rexBuilder, ImmutableList.of(simplifiedExpr), reducedValues);
@@ -2294,7 +2301,7 @@ public class RexSimplify {
         return e;
       } else {
         return e.operands.size() > 1
-            ? rexBuilder.makeCast(e.getType(), operand, safe, safe,
+            ? rexBuilder.makeCast(e.getParserPosition(), e.getType(), operand, safe, safe,
             (RexLiteral) e.getOperands().get(1))
             : rexBuilder.makeCast(e.getType(), operand, safe, safe);
       }
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 227e6e3701..dce7cc5390 100644
--- a/core/src/main/java/org/apache/calcite/rex/RexUtil.java
+++ b/core/src/main/java/org/apache/calcite/rex/RexUtil.java
@@ -1592,7 +1592,7 @@ public class RexUtil {
       final SqlOperator op = call.getOperator();
       final List<RexNode> flattenedOperands = flatten(call.getOperands(), op);
       if (!isFlat(call.getOperands(), op)) {
-        return rexBuilder.makeCall(call.getType(), op, flattenedOperands);
+        return rexBuilder.makeCall(call.getParserPosition(), call.getType(), op, flattenedOperands);
       }
     }
     return node;
@@ -2036,8 +2036,7 @@ public class RexUtil {
   }
 
   private static RexNode addNot(RexNode e) {
-    return new RexCall(e.getType(), SqlStdOperatorTable.NOT,
-        ImmutableList.of(e));
+    return new RexCall(e.getType(), SqlStdOperatorTable.NOT, ImmutableList.of(e));
   }
 
   @API(since = "1.27.0", status = API.Status.EXPERIMENTAL)
@@ -2114,7 +2113,7 @@ public class RexUtil {
     case LESS_THAN_OR_EQUAL:
     case GREATER_THAN_OR_EQUAL:
       final SqlOperator op = op(call.getKind().negateNullSafe());
-      return rexBuilder.makeCall(op, call.getOperands());
+      return rexBuilder.makeCall(call.getParserPosition(), op, call.getOperands());
     default:
       break;
     }
@@ -2130,7 +2129,7 @@ public class RexUtil {
     case LESS_THAN_OR_EQUAL:
     case GREATER_THAN_OR_EQUAL:
       final SqlOperator op = requireNonNull(call.getOperator().reverse());
-      return rexBuilder.makeCall(op, Lists.reverse(call.getOperands()));
+      return rexBuilder.makeCall(call.getParserPosition(), op, Lists.reverse(call.getOperands()));
     default:
       return null;
     }
diff --git a/core/src/main/java/org/apache/calcite/sql/SqlSplittableAggFunction.java b/core/src/main/java/org/apache/calcite/sql/SqlSplittableAggFunction.java
index d10c9f88d0..1918ef6701 100644
--- a/core/src/main/java/org/apache/calcite/sql/SqlSplittableAggFunction.java
+++ b/core/src/main/java/org/apache/calcite/sql/SqlSplittableAggFunction.java
@@ -26,6 +26,7 @@ import org.apache.calcite.rex.RexInputRef;
 import org.apache.calcite.rex.RexNode;
 import org.apache.calcite.rex.RexUtil;
 import org.apache.calcite.sql.fun.SqlStdOperatorTable;
+import org.apache.calcite.sql.parser.SqlParserPos;
 import org.apache.calcite.sql.type.SqlTypeName;
 import org.apache.calcite.util.ImmutableIntList;
 import org.apache.calcite.util.mapping.Mappings;
@@ -114,7 +115,7 @@ public interface SqlSplittableAggFunction extends SqlSingletonAggFunction {
     @Override public @Nullable AggregateCall other(RelDataTypeFactory typeFactory,
         AggregateCall e) {
       final RelDataType type = typeFactory.createSqlType(SqlTypeName.BIGINT);
-      return AggregateCall.create(SqlStdOperatorTable.COUNT, false, false,
+      return AggregateCall.create(e.getParserPosition(), SqlStdOperatorTable.COUNT, false, false,
           false, ImmutableList.of(), ImmutableIntList.of(), -1, null,
           RelCollations.EMPTY, type, null);
     }
@@ -123,6 +124,7 @@ public interface SqlSplittableAggFunction extends SqlSingletonAggFunction {
         Registry<RexNode> extra, int offset, RelDataType inputRowType,
         AggregateCall aggregateCall, int leftSubTotal, int rightSubTotal) {
       final List<RexNode> merges = new ArrayList<>();
+      final SqlParserPos pos = aggregateCall.getParserPosition();
       if (leftSubTotal >= 0) {
         merges.add(
             rexBuilder.makeInputRef(aggregateCall.type, leftSubTotal));
@@ -137,13 +139,13 @@ public interface SqlSplittableAggFunction extends SqlSingletonAggFunction {
         node = merges.get(0);
         break;
       case 2:
-        node = rexBuilder.makeCall(SqlStdOperatorTable.MULTIPLY, merges);
+        node = rexBuilder.makeCall(pos, SqlStdOperatorTable.MULTIPLY, merges);
         break;
       default:
         throw new AssertionError("unexpected count " + merges);
       }
       int ordinal = extra.register(node);
-      return AggregateCall.create(SqlStdOperatorTable.SUM0, false, false,
+      return AggregateCall.create(pos, SqlStdOperatorTable.SUM0, false, false,
           false, aggregateCall.rexList, ImmutableList.of(ordinal), -1,
           aggregateCall.distinctKeys, aggregateCall.collation,
           aggregateCall.type, aggregateCall.name);
@@ -183,7 +185,7 @@ public interface SqlSplittableAggFunction extends SqlSingletonAggFunction {
       if (bottom.getAggregation().getKind() == SqlKind.COUNT
           && (top.getAggregation().getKind() == SqlKind.SUM
               || top.getAggregation().getKind() == SqlKind.SUM0)) {
-        return AggregateCall.create(bottom.getAggregation(),
+        return AggregateCall.create(top.getParserPosition(), bottom.getAggregation(),
             bottom.isDistinct(), bottom.isApproximate(), false,
             bottom.rexList, bottom.getArgList(), bottom.filterArg,
             bottom.distinctKeys, bottom.getCollation(),
@@ -228,7 +230,7 @@ public interface SqlSplittableAggFunction extends SqlSingletonAggFunction {
 
     @Override public @Nullable AggregateCall merge(AggregateCall top, AggregateCall bottom) {
       if (top.getAggregation().getKind() == bottom.getAggregation().getKind()) {
-        return AggregateCall.create(bottom.getAggregation(),
+        return AggregateCall.create(top.getParserPosition(), bottom.getAggregation(),
             bottom.isDistinct(), bottom.isApproximate(), false,
             bottom.rexList, bottom.getArgList(), bottom.filterArg,
             bottom.distinctKeys, bottom.getCollation(),
@@ -258,7 +260,7 @@ public interface SqlSplittableAggFunction extends SqlSingletonAggFunction {
     @Override public @Nullable AggregateCall other(RelDataTypeFactory typeFactory,
         AggregateCall e) {
       final RelDataType type = typeFactory.createSqlType(SqlTypeName.BIGINT);
-      return AggregateCall.create(SqlStdOperatorTable.COUNT, false, false,
+      return AggregateCall.create(e.getParserPosition(), SqlStdOperatorTable.COUNT, false, false,
           false, ImmutableList.of(), ImmutableIntList.of(), -1, null,
           RelCollations.EMPTY, type, null);
     }
@@ -267,6 +269,7 @@ public interface SqlSplittableAggFunction extends SqlSingletonAggFunction {
         Registry<RexNode> extra, int offset, RelDataType inputRowType,
         AggregateCall aggregateCall, int leftSubTotal, int rightSubTotal) {
       final List<RexNode> merges = new ArrayList<>();
+      final SqlParserPos pos = aggregateCall.getParserPosition();
       final List<RelDataTypeField> fieldList = inputRowType.getFieldList();
       if (leftSubTotal >= 0) {
         final RelDataType type = fieldList.get(leftSubTotal).getType();
@@ -282,14 +285,14 @@ public interface SqlSplittableAggFunction extends SqlSingletonAggFunction {
         node = merges.get(0);
         break;
       case 2:
-        node = rexBuilder.makeCall(SqlStdOperatorTable.MULTIPLY, merges);
-        node = rexBuilder.makeAbstractCast(aggregateCall.type, node, false);
+        node = rexBuilder.makeCall(pos, SqlStdOperatorTable.MULTIPLY, merges);
+        node = rexBuilder.makeAbstractCast(pos, aggregateCall.type, node, false);
         break;
       default:
         throw new AssertionError("unexpected count " + merges);
       }
       int ordinal = extra.register(node);
-      return AggregateCall.create(getMergeAggFunctionOfTopSplit(), false, false,
+      return AggregateCall.create(pos, getMergeAggFunctionOfTopSplit(), false, false,
           false, aggregateCall.rexList, ImmutableList.of(ordinal), -1,
           aggregateCall.distinctKeys, aggregateCall.collation,
           aggregateCall.type, aggregateCall.name);
@@ -300,7 +303,7 @@ public interface SqlSplittableAggFunction extends SqlSingletonAggFunction {
       if (topKind == bottom.getAggregation().getKind()
           && (topKind == SqlKind.SUM
               || topKind == SqlKind.SUM0)) {
-        return AggregateCall.create(bottom.getAggregation(),
+        return AggregateCall.create(top.getParserPosition(), bottom.getAggregation(),
             bottom.isDistinct(), bottom.isApproximate(), false,
             bottom.rexList, bottom.getArgList(), bottom.filterArg,
             bottom.distinctKeys, bottom.getCollation(),
diff --git a/core/src/main/java/org/apache/calcite/sql2rel/AggConverter.java b/core/src/main/java/org/apache/calcite/sql2rel/AggConverter.java
index 56c3d027f0..6193c33df4 100644
--- a/core/src/main/java/org/apache/calcite/sql2rel/AggConverter.java
+++ b/core/src/main/java/org/apache/calcite/sql2rel/AggConverter.java
@@ -539,6 +539,7 @@ class AggConverter implements SqlVisitor<Void> {
     }
     final AggregateCall aggCall =
         AggregateCall.create(
+            call.getParserPosition(),
             aggFunction,
             distinct,
             approximate,
diff --git a/core/src/main/java/org/apache/calcite/sql2rel/RelDecorrelator.java b/core/src/main/java/org/apache/calcite/sql2rel/RelDecorrelator.java
index 1a63cf2d58..b78403a90b 100644
--- a/core/src/main/java/org/apache/calcite/sql2rel/RelDecorrelator.java
+++ b/core/src/main/java/org/apache/calcite/sql2rel/RelDecorrelator.java
@@ -1843,6 +1843,7 @@ public class RelDecorrelator implements ReflectiveVisitor {
         }
         newCall =
             rexBuilder.makeCall(
+                call.getParserPosition(),
                 newType,
                 operator,
                 clonedOperands);
diff --git a/core/src/main/java/org/apache/calcite/sql2rel/RelStructuredTypeFlattener.java b/core/src/main/java/org/apache/calcite/sql2rel/RelStructuredTypeFlattener.java
index 3f2a659a15..b1a7a230a7 100644
--- a/core/src/main/java/org/apache/calcite/sql2rel/RelStructuredTypeFlattener.java
+++ b/core/src/main/java/org/apache/calcite/sql2rel/RelStructuredTypeFlattener.java
@@ -700,8 +700,9 @@ public class RelStructuredTypeFlattener implements ReflectiveVisitor {
         // NOTE jvs 10-Feb-2005:  This is a lame hack to keep special
         // functions which return row types working.
         RexNode newExp = exp;
-        List<RexNode> operands = ((RexCall) exp).getOperands();
-        SqlOperator operator = ((RexCall) exp).getOperator();
+        RexCall expCall = (RexCall) exp;
+        List<RexNode> operands = expCall.getOperands();
+        SqlOperator operator = expCall.getOperator();
 
         if (operator == SqlStdOperatorTable.ITEM
             && operands.get(0).getType().isStruct()
@@ -747,7 +748,7 @@ public class RelStructuredTypeFlattener implements ReflectiveVisitor {
           }
         } else {
           newExp =
-              rexBuilder.makeCall(exp.getType(), operator,
+              rexBuilder.makeCall(expCall.getParserPosition(), exp.getType(), operator,
                   shuttle.visitList(operands));
           // flatten call result type
           flattenResultTypeOfRexCall(newExp, fieldName, flattenedExps);
@@ -983,6 +984,7 @@ public class RelStructuredTypeFlattener implements ReflectiveVisitor {
         RexNode input = rexCall.getOperands().get(0).accept(this);
         RelDataType targetType = removeDistinct(rexCall.getType());
         return rexBuilder.makeCast(
+            rexCall.getParserPosition(),
             targetType,
             input);
       }
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 b4f572d04c..65067c3b3c 100644
--- a/core/src/main/java/org/apache/calcite/sql2rel/SqlToRelConverter.java
+++ b/core/src/main/java/org/apache/calcite/sql2rel/SqlToRelConverter.java
@@ -1311,10 +1311,10 @@ public class SqlToRelConverter {
                 ImmutableBitSet.of(),
                 null,
                 ImmutableList.of(
-                    AggregateCall.create(SqlStdOperatorTable.COUNT, false,
+                    AggregateCall.create(call.getParserPosition(), SqlStdOperatorTable.COUNT, false,
                         false, false, ImmutableList.of(), ImmutableList.of(),
                         -1, null, RelCollations.EMPTY, longType, null),
-                    AggregateCall.create(SqlStdOperatorTable.COUNT, false,
+                    AggregateCall.create(call.getParserPosition(), SqlStdOperatorTable.COUNT, false,
                         false, false, ImmutableList.of(), args,
                         -1, null, RelCollations.EMPTY, longType, null)));
         LogicalJoin join =
@@ -2327,7 +2327,7 @@ public class SqlToRelConverter {
       bb.window = window;
       RexNode rexAgg = exprConverter.convertCall(bb, aggCall);
       rexAgg =
-          rexBuilder.ensureType(
+          rexBuilder.ensureType(call.getParserPosition(),
               validator().getValidatedNodeType(call), rexAgg, false);
 
       // Walk over the tree and apply 'over' to all agg functions. This is
@@ -2607,7 +2607,7 @@ public class SqlToRelConverter {
               RexNode arg = requireNonNull(node.accept(this), node::toString);
               newOperands.add(arg);
             }
-            return rexBuilder.makeCall(
+            return rexBuilder.makeCall(call.getParserPosition(),
               validator().getUnknownType(), call.getOperator(), newOperands);
           }
 
@@ -2657,15 +2657,16 @@ public class SqlToRelConverter {
 
     final RexNode after;
     if (afterMatch instanceof SqlCall) {
-      List<SqlNode> operands = ((SqlCall) afterMatch).getOperandList();
-      SqlOperator operator = ((SqlCall) afterMatch).getOperator();
+      final SqlCall afterCall = (SqlCall) afterMatch;
+      List<SqlNode> operands = afterCall.getOperandList();
+      SqlOperator operator = afterCall.getOperator();
       assert operands.size() == 1;
       SqlIdentifier id = (SqlIdentifier) operands.get(0);
       assert patternVarsSet.contains(id.getSimple())
           : id.getSimple() + " not defined in pattern";
       RexNode rex = rexBuilder.makeLiteral(id.getSimple());
       after =
-          rexBuilder.makeCall(validator().getUnknownType(), operator,
+          rexBuilder.makeCall(afterCall.getParserPosition(), validator().getUnknownType(), operator,
               ImmutableList.of(rex));
     } else {
       after = matchBb.convertExpression(afterMatch);
@@ -5733,7 +5734,8 @@ public class SqlToRelConverter {
         }
         requireNonNull(subQuery, "subQuery");
         rex = requireNonNull(subQuery.expr);
-        return StandardConvertletTable.castToValidatedType(expr, rex,
+        return StandardConvertletTable.castToValidatedType(
+            expr.getParserPosition(), expr, rex,
             validator(), rexBuilder, false);
 
       case SELECT:
@@ -6161,13 +6163,15 @@ public class SqlToRelConverter {
           exprs.set(
               0,
               reinterpretCast
-              ? rexBuilder.makeReinterpretCast(histogramType, exprs.get(0),
+              ? rexBuilder.makeReinterpretCast(
+                  call.getParserPosition(), histogramType, exprs.get(0),
                   rexBuilder.makeLiteral(false))
-              : rexBuilder.makeCast(histogramType, exprs.get(0)));
+              : rexBuilder.makeCast(call.getParserPosition(), histogramType, exprs.get(0)));
         }
 
         RexNode over =
-            relBuilder.aggregateCall(SqlStdOperatorTable.HISTOGRAM_AGG, exprs)
+            relBuilder.aggregateCall(
+                call.getParserPosition(), SqlStdOperatorTable.HISTOGRAM_AGG, exprs)
                 .distinct(distinct)
                 .ignoreNulls(ignoreNulls)
                 .over()
@@ -6181,7 +6185,7 @@ public class SqlToRelConverter {
                 .toRex();
 
         RexNode histogramCall =
-            rexBuilder.makeCall(
+            rexBuilder.makeCall(call.getParserPosition(),
                 histogramType,
                 histogramOp,
                 ImmutableList.of(over));
@@ -6191,13 +6195,13 @@ public class SqlToRelConverter {
         if (histogramType != type) {
           if (reinterpretCast) {
             histogramCall =
-                rexBuilder.makeReinterpretCast(
+                rexBuilder.makeReinterpretCast(call.getParserPosition(),
                     type,
                     histogramCall,
                     rexBuilder.makeLiteral(false));
           } else {
             histogramCall =
-                rexBuilder.makeCast(type, histogramCall);
+                rexBuilder.makeCast(call.getParserPosition(), type, histogramCall);
           }
         }
 
@@ -6205,7 +6209,7 @@ public class SqlToRelConverter {
       } else {
         boolean nullWhenCountZero = aggOp == SqlStdOperatorTable.SUM
             && type.isNullable();
-        return relBuilder.aggregateCall(aggOp, exprs)
+        return relBuilder.aggregateCall(call.getParserPosition(), aggOp, exprs)
             .distinct(distinct)
             .ignoreNulls(ignoreNulls)
             .over()
@@ -6614,7 +6618,8 @@ public class SqlToRelConverter {
             builder.add(call.operands.get(i));
           }
         }
-        return rexBuilder.makeCall(SqlStdOperatorTable.JSON_OBJECT, builder.build());
+        return rexBuilder.makeCall(
+            call.getParserPosition(), SqlStdOperatorTable.JSON_OBJECT, builder.build());
       }
       if (call.getOperator() == SqlStdOperatorTable.JSON_ARRAY) {
         final ImmutableList.Builder<RexNode> builder = ImmutableList.builder();
@@ -6622,7 +6627,8 @@ public class SqlToRelConverter {
         for (int i = 1; i < call.operands.size(); ++i) {
           builder.add(forceChildJsonType(call.operands.get(i)));
         }
-        return rexBuilder.makeCall(SqlStdOperatorTable.JSON_ARRAY, builder.build());
+        return rexBuilder.makeCall(
+            call.getParserPosition(), SqlStdOperatorTable.JSON_ARRAY, builder.build());
       }
       return super.visitCall(call);
     }
diff --git a/core/src/main/java/org/apache/calcite/sql2rel/StandardConvertletTable.java b/core/src/main/java/org/apache/calcite/sql2rel/StandardConvertletTable.java
index 8af3b87e40..216c34fd71 100644
--- a/core/src/main/java/org/apache/calcite/sql2rel/StandardConvertletTable.java
+++ b/core/src/main/java/org/apache/calcite/sql2rel/StandardConvertletTable.java
@@ -177,7 +177,8 @@ public class StandardConvertletTable extends ReflectiveConvertletTable {
               (RexCall) StandardConvertletTable.this.convertCall(cx, call);
           if (e.getOperands().size() == 1
               && SqlTypeUtil.isString(e.getOperands().get(0).getType())) {
-            return cx.getRexBuilder().makeCast(e.type, e.getOperands().get(0));
+            return cx.getRexBuilder().makeCast(
+                e.getParserPosition(), e.type, e.getOperands().get(0));
           }
           return e;
         });
@@ -403,7 +404,8 @@ public class StandardConvertletTable extends ReflectiveConvertletTable {
         cx.getTypeFactory().createTypeWithNullability(
             cx.getTypeFactory().createSqlType(SqlTypeName.BOOLEAN), right.getType().isNullable()
                 || left.getType().isNullable() || rightComponentType.isNullable());
-    return rexBuilder.makeCall(returnType, call.getOperator(), ImmutableList.of(left, right));
+    return rexBuilder.makeCall(call.getParserPosition(), returnType,
+        call.getOperator(), ImmutableList.of(left, right));
   }
 
   /** Converts a call to the {@code NVL} function (and also its synonym,
@@ -417,15 +419,15 @@ public class StandardConvertletTable extends ReflectiveConvertletTable {
     final RelDataType type =
         cx.getValidator().getValidatedNodeType(call);
     // Preserve Operand Nullability
-    return rexBuilder.makeCall(type, SqlStdOperatorTable.CASE,
+    return rexBuilder.makeCall(call.getParserPosition(), type, SqlStdOperatorTable.CASE,
         ImmutableList.of(
-            rexBuilder.makeCall(SqlStdOperatorTable.IS_NOT_NULL,
+            rexBuilder.makeCall(call.getParserPosition(), SqlStdOperatorTable.IS_NOT_NULL,
                 operand0),
-            rexBuilder.makeCast(
+            rexBuilder.makeCast(call.getParserPosition(),
                 cx.getTypeFactory()
                     .createTypeWithNullability(type, operand0.getType().isNullable()),
                 operand0),
-            rexBuilder.makeCast(
+            rexBuilder.makeCast(call.getParserPosition(),
                 cx.getTypeFactory()
                     .createTypeWithNullability(type, operand1.getType().isNullable()),
                 operand1)));
@@ -485,7 +487,7 @@ public class StandardConvertletTable extends ReflectiveConvertletTable {
       throw new UnsupportedOperationException("Position does not accept "
           + call.operandCount() + " operands");
     }
-    return rexBuilder.makeCall(type, SqlStdOperatorTable.POSITION, exprs);
+    return rexBuilder.makeCall(call.getParserPosition(), type, SqlStdOperatorTable.POSITION, exprs);
   }
 
   /** Converts a call to the DECODE function. */
@@ -507,7 +509,7 @@ public class StandardConvertletTable extends ReflectiveConvertletTable {
     } else {
       exprs.add(rexBuilder.makeNullLiteral(type));
     }
-    return rexBuilder.makeCall(type, SqlStdOperatorTable.CASE, exprs);
+    return rexBuilder.makeCall(call.getParserPosition(), type, SqlStdOperatorTable.CASE, exprs);
   }
 
   /** Converts a call to the IF function.
@@ -519,7 +521,7 @@ public class StandardConvertletTable extends ReflectiveConvertletTable {
         convertOperands(cx, call, SqlOperandTypeChecker.Consistency.NONE);
     final RelDataType type =
         cx.getValidator().getValidatedNodeType(call);
-    return rexBuilder.makeCall(type, SqlStdOperatorTable.CASE, operands);
+    return rexBuilder.makeCall(call.getParserPosition(), type, SqlStdOperatorTable.CASE, operands);
   }
 
   /** Converts an interval expression to a numeric multiplied by an interval
@@ -560,22 +562,22 @@ public class StandardConvertletTable extends ReflectiveConvertletTable {
     return rexBuilder.makeCall(SqlStdOperatorTable.AND, a0, a1);
   }
 
-  private static RexNode divideInt(RexBuilder rexBuilder, RexNode a0,
+  private static RexNode divideInt(SqlParserPos pos, RexBuilder rexBuilder, RexNode a0,
       RexNode a1) {
-    return rexBuilder.makeCall(SqlStdOperatorTable.DIVIDE_INTEGER, a0, a1);
+    return rexBuilder.makeCall(pos, SqlStdOperatorTable.DIVIDE_INTEGER, a0, a1);
   }
 
-  private static RexNode plus(RexBuilder rexBuilder, RexNode a0, RexNode a1) {
-    return rexBuilder.makeCall(SqlStdOperatorTable.PLUS, a0, a1);
+  private static RexNode plus(SqlParserPos pos, RexBuilder rexBuilder, RexNode a0, RexNode a1) {
+    return rexBuilder.makeCall(pos, SqlStdOperatorTable.PLUS, a0, a1);
   }
 
-  private static RexNode minus(RexBuilder rexBuilder, RexNode a0, RexNode a1) {
-    return rexBuilder.makeCall(SqlStdOperatorTable.MINUS, a0, a1);
+  private static RexNode minus(SqlParserPos pos, RexBuilder rexBuilder, RexNode a0, RexNode a1) {
+    return rexBuilder.makeCall(pos, SqlStdOperatorTable.MINUS, a0, a1);
   }
 
-  private static RexNode multiply(RexBuilder rexBuilder, RexNode a0,
+  private static RexNode multiply(SqlParserPos pos, RexBuilder rexBuilder, RexNode a0,
       RexNode a1) {
-    return rexBuilder.makeCall(SqlStdOperatorTable.MULTIPLY, a0, a1);
+    return rexBuilder.makeCall(pos, SqlStdOperatorTable.MULTIPLY, a0, a1);
   }
 
   private static RexNode case_(RexBuilder rexBuilder, RexNode... args) {
@@ -628,9 +630,10 @@ public class StandardConvertletTable extends ReflectiveConvertletTable {
         rexBuilder.deriveReturnType(call.getOperator(), exprList);
     for (int i : elseArgs(exprList.size())) {
       exprList.set(i,
-          rexBuilder.ensureType(type, exprList.get(i), false));
+          rexBuilder.ensureType(
+              call.getParserPosition(), type, exprList.get(i), false));
     }
-    return rexBuilder.makeCall(type, SqlStdOperatorTable.CASE, exprList);
+    return rexBuilder.makeCall(call.getParserPosition(), type, SqlStdOperatorTable.CASE, exprList);
   }
 
   public RexNode convertMultiset(
@@ -662,7 +665,8 @@ public class StandardConvertletTable extends ReflectiveConvertletTable {
       // then '$SLICE(<ms>) has type 'INTEGER MULTISET'.
       // This will be removed as the expression is translated.
       expr =
-          cx.getRexBuilder().makeCall(originalType, SqlStdOperatorTable.SLICE,
+          cx.getRexBuilder().makeCall(
+              call.getParserPosition(), originalType, SqlStdOperatorTable.SLICE,
               ImmutableList.of(expr));
     }
     return expr;
@@ -744,8 +748,8 @@ public class StandardConvertletTable extends ReflectiveConvertletTable {
         RexLiteral castedInterval =
             rexBuilder.makeIntervalLiteral(sourceValue,
                 intervalQualifier);
-        return castToValidatedType(call, castedInterval, validator, rexBuilder,
-            safe);
+        return castToValidatedType(
+            call.getParserPosition(), call, castedInterval, validator, rexBuilder, safe);
       } else if (left instanceof SqlNumericLiteral) {
         RexLiteral sourceInterval =
             (RexLiteral) cx.convertExpression(left);
@@ -757,11 +761,12 @@ public class StandardConvertletTable extends ReflectiveConvertletTable {
             rexBuilder.makeIntervalLiteral(
                 SqlFunctions.multiply(sourceValue, multiplier),
                 intervalQualifier);
-        return castToValidatedType(call, castedInterval, validator, rexBuilder,
-            safe);
+        return castToValidatedType(
+            call.getParserPosition(), call, castedInterval, validator, rexBuilder, safe);
       }
       RexNode value = cx.convertExpression(left);
-      return castToValidatedType(call, value, validator, rexBuilder, safe);
+      return castToValidatedType(
+          call.getParserPosition(), call, value, validator, rexBuilder, safe);
     }
 
     final SqlDataTypeSpec dataType = (SqlDataTypeSpec) right;
@@ -799,11 +804,12 @@ public class StandardConvertletTable extends ReflectiveConvertletTable {
         type = typeFactory.createTypeWithNullability(type, isn);
       }
     }
-    return rexBuilder.makeCast(type, arg, safe, safe, formatArg);
+    return rexBuilder.makeCast(call.getParserPosition(), type, arg, safe, safe, formatArg);
   }
 
   protected RexNode convertFloorCeil(SqlRexContext cx, SqlCall call) {
     final boolean floor = call.getKind() == SqlKind.FLOOR;
+    final SqlParserPos pos = call.getParserPosition();
     // Rewrite floor, ceil of interval
     if (call.operandCount() == 1
         && call.operand(0) instanceof SqlIntervalLiteral) {
@@ -821,19 +827,19 @@ public class StandardConvertletTable extends ReflectiveConvertletTable {
       RexNode pad =
           rexBuilder.makeExactLiteral(val.subtract(BigDecimal.ONE));
       RexNode cast =
-          rexBuilder.makeReinterpretCast(rexInterval.getType(), pad,
+          rexBuilder.makeReinterpretCast(pos, rexInterval.getType(), pad,
               rexBuilder.makeLiteral(false));
       RexNode sum =
-          floor ? minus(rexBuilder, rexInterval, cast)
-              : plus(rexBuilder, rexInterval, cast);
+          floor ? minus(pos, rexBuilder, rexInterval, cast)
+              : plus(pos, rexBuilder, rexInterval, cast);
 
       RexNode kase = floor
           ? case_(rexBuilder, rexInterval, cond, sum)
           : case_(rexBuilder, sum, cond, rexInterval);
 
       RexNode factor = rexBuilder.makeExactLiteral(val);
-      RexNode div = divideInt(rexBuilder, kase, factor);
-      return multiply(rexBuilder, div, factor);
+      RexNode div = divideInt(pos, rexBuilder, kase, factor);
+      return multiply(pos, rexBuilder, div, factor);
     }
 
     // normal floor, ceil function
@@ -843,11 +849,12 @@ public class StandardConvertletTable extends ReflectiveConvertletTable {
   protected RexNode convertCharset(
       @UnknownInitialization StandardConvertletTable this,
       SqlRexContext cx, SqlCall call) {
+    final SqlParserPos pos = call.getParserPosition();
     final SqlNode expr = call.operand(0);
     final String srcCharset = call.operand(1).toString();
     final String destCharset = call.operand(2).toString();
     final RexBuilder rexBuilder = cx.getRexBuilder();
-    return rexBuilder.makeCall(SqlStdOperatorTable.CONVERT,
+    return rexBuilder.makeCall(pos, SqlStdOperatorTable.CONVERT,
         cx.convertExpression(expr),
         rexBuilder.makeLiteral(srcCharset),
         rexBuilder.makeLiteral(destCharset));
@@ -856,10 +863,11 @@ public class StandardConvertletTable extends ReflectiveConvertletTable {
   protected RexNode translateCharset(
       @UnknownInitialization StandardConvertletTable this,
       SqlRexContext cx, SqlCall call) {
+    final SqlParserPos pos = call.getParserPosition();
     final SqlNode expr = call.operand(0);
     final String transcodingName = call.operand(1).toString();
     final RexBuilder rexBuilder = cx.getRexBuilder();
-    return rexBuilder.makeCall(SqlStdOperatorTable.TRANSLATE,
+    return rexBuilder.makeCall(pos, SqlStdOperatorTable.TRANSLATE,
         cx.convertExpression(expr),
         rexBuilder.makeLiteral(transcodingName));
   }
@@ -877,16 +885,17 @@ public class StandardConvertletTable extends ReflectiveConvertletTable {
   }
 
   @SuppressWarnings("unused")
-  private static RexNode mod(RexBuilder rexBuilder, RelDataType resType, RexNode res,
+  private static RexNode mod(SqlParserPos pos, RexBuilder rexBuilder,
+      RelDataType resType, RexNode res,
       BigDecimal val) {
     if (val.equals(BigDecimal.ONE)) {
       return res;
     }
-    return rexBuilder.makeCall(SqlStdOperatorTable.MOD, res,
+    return rexBuilder.makeCall(pos, SqlStdOperatorTable.MOD, res,
         rexBuilder.makeExactLiteral(val, resType));
   }
 
-  private static RexNode divide(RexBuilder rexBuilder, RexNode res,
+  private static RexNode divide(SqlParserPos pos, RexBuilder rexBuilder, RexNode res,
       BigDecimal val) {
     if (val.equals(BigDecimal.ONE)) {
       return res;
@@ -898,13 +907,13 @@ public class StandardConvertletTable extends ReflectiveConvertletTable {
       try {
         final BigDecimal reciprocal =
             BigDecimal.ONE.divide(val, RoundingMode.UNNECESSARY);
-        return multiply(rexBuilder, res,
+        return multiply(pos, rexBuilder, res,
             rexBuilder.makeExactLiteral(reciprocal));
       } catch (ArithmeticException e) {
         // ignore - reciprocal is not an integer
       }
     }
-    return divideInt(rexBuilder, res, rexBuilder.makeExactLiteral(val));
+    return divideInt(pos, rexBuilder, res, rexBuilder.makeExactLiteral(val));
   }
 
   public RexNode convertDatetimeMinus(
@@ -919,7 +928,7 @@ public class StandardConvertletTable extends ReflectiveConvertletTable {
 
     final RelDataType resType =
         cx.getValidator().getValidatedNodeType(call);
-    return rexBuilder.makeCall(resType, op, exprs.subList(0, 2));
+    return rexBuilder.makeCall(call.getParserPosition(), resType, op, exprs.subList(0, 2));
   }
 
   public RexNode convertFunction(
@@ -936,7 +945,7 @@ public class StandardConvertletTable extends ReflectiveConvertletTable {
     if (returnType == null) {
       returnType = cx.getRexBuilder().deriveReturnType(fun, exprs);
     }
-    return cx.getRexBuilder().makeCall(returnType, fun, exprs);
+    return cx.getRexBuilder().makeCall(call.getParserPosition(), returnType, fun, exprs);
   }
 
   public RexNode convertWindowFunction(
@@ -953,7 +962,7 @@ public class StandardConvertletTable extends ReflectiveConvertletTable {
     if (returnType == null) {
       returnType = cx.getRexBuilder().deriveReturnType(fun, exprs);
     }
-    return cx.getRexBuilder().makeCall(returnType, fun, exprs);
+    return cx.getRexBuilder().makeCall(call.getParserPosition(), returnType, fun, exprs);
   }
 
   public RexNode convertJsonValueFunction(
@@ -994,7 +1003,7 @@ public class StandardConvertletTable extends ReflectiveConvertletTable {
         convertOperands(cx, call, operands, SqlOperandTypeChecker.Consistency.NONE);
     RelDataType returnType = cx.getValidator().getValidatedNodeTypeIfKnown(call);
     requireNonNull(returnType, () -> "Unable to get type of " + call);
-    return cx.getRexBuilder().makeCall(returnType, fun, exprs);
+    return cx.getRexBuilder().makeCall(call.getParserPosition(), returnType, fun, exprs);
   }
 
   public RexNode convertSequenceValue(
@@ -1008,7 +1017,7 @@ public class StandardConvertletTable extends ReflectiveConvertletTable {
     final String key = Util.listToString(id.names);
     RelDataType returnType =
         cx.getValidator().getValidatedNodeType(call);
-    return cx.getRexBuilder().makeCall(returnType, fun,
+    return cx.getRexBuilder().makeCall(call.getParserPosition(), returnType, fun,
         ImmutableList.of(cx.getRexBuilder().makeLiteral(key)));
   }
 
@@ -1035,7 +1044,7 @@ public class StandardConvertletTable extends ReflectiveConvertletTable {
           };
       returnType = fun.inferReturnType(binding);
     }
-    return cx.getRexBuilder().makeCall(returnType, fun, exprs);
+    return cx.getRexBuilder().makeCall(call.getParserPosition(), returnType, fun, exprs);
   }
 
   private static RexNode makeConstructorCall(
@@ -1109,7 +1118,7 @@ public class StandardConvertletTable extends ReflectiveConvertletTable {
       }
     }
     RelDataType type = rexBuilder.deriveReturnType(op, exprs);
-    return rexBuilder.makeCall(type, op, RexUtil.flatten(exprs, op));
+    return rexBuilder.makeCall(call.getParserPosition(), type, op, RexUtil.flatten(exprs, op));
   }
 
   /**
@@ -1146,11 +1155,11 @@ public class StandardConvertletTable extends ReflectiveConvertletTable {
         final RexCall call1 = (RexCall) expr1;
         final List<RexNode> eqList = new ArrayList<>();
         Pair.forEach(call0.getOperands(), call1.getOperands(), (x, y) ->
-            eqList.add(rexBuilder.makeCall(op, x, y)));
+            eqList.add(rexBuilder.makeCall(call.getParserPosition(), op, x, y)));
         return RexUtil.composeConjunction(rexBuilder, eqList);
       }
     }
-    return rexBuilder.makeCall(type, op, RexUtil.flatten(exprs, op));
+    return rexBuilder.makeCall(call.getParserPosition(), type, op, RexUtil.flatten(exprs, op));
   }
 
   private static List<Integer> elseArgs(int count) {
@@ -1196,7 +1205,7 @@ public class StandardConvertletTable extends ReflectiveConvertletTable {
       final List<RexNode> oldExprs = new ArrayList<>(exprs);
       exprs.clear();
       Pair.forEach(oldExprs, operandTypes, (expr, type) ->
-          exprs.add(cx.getRexBuilder().ensureType(type, expr, true)));
+          exprs.add(cx.getRexBuilder().ensureType(call.getParserPosition(), type, expr, true)));
     }
     if (exprs.size() > 1) {
       final RelDataType type =
@@ -1205,7 +1214,7 @@ public class StandardConvertletTable extends ReflectiveConvertletTable {
         final List<RexNode> oldExprs = new ArrayList<>(exprs);
         exprs.clear();
         for (RexNode expr : oldExprs) {
-          exprs.add(cx.getRexBuilder().ensureType(type, expr, true));
+          exprs.add(cx.getRexBuilder().ensureType(call.getParserPosition(), type, expr, true));
         }
       }
     }
@@ -1289,7 +1298,7 @@ public class StandardConvertletTable extends ReflectiveConvertletTable {
           break;
         }
       }
-      return rexBuilder.makeCall(rex.getType(),
+      return rexBuilder.makeCall(call.getParserPosition(), rex.getType(),
           SqlStdOperatorTable.DATETIME_PLUS, operands);
     default:
       return rex;
@@ -1351,7 +1360,7 @@ public class StandardConvertletTable extends ReflectiveConvertletTable {
     final SqlBetweenOperator betweenOp =
         (SqlBetweenOperator) call.getOperator();
     if (betweenOp.isNegated()) {
-      res = rexBuilder.makeCall(SqlStdOperatorTable.NOT, res);
+      res = rexBuilder.makeCall(call.getParserPosition(), SqlStdOperatorTable.NOT, res);
     }
     return res;
   }
@@ -1424,7 +1433,8 @@ public class StandardConvertletTable extends ReflectiveConvertletTable {
     }
     final RelDataType type =
         rexBuilder.deriveReturnType(SqlStdOperatorTable.COLUMN_LIST, columns);
-    return rexBuilder.makeCall(type, SqlStdOperatorTable.COLUMN_LIST, columns);
+    return rexBuilder.makeCall(
+        call.getParserPosition(), type, SqlStdOperatorTable.COLUMN_LIST, columns);
   }
 
   /**
@@ -1516,14 +1526,14 @@ public class StandardConvertletTable extends ReflectiveConvertletTable {
       SqlRexContext cx,
       SqlCall call,
       RexNode value) {
-    return castToValidatedType(call, value, cx.getValidator(),
+    return castToValidatedType(call.getParserPosition(), call, value, cx.getValidator(),
         cx.getRexBuilder(), false);
   }
 
   @Deprecated // to be removed before 2.0
   public static RexNode castToValidatedType(SqlNode node, RexNode e,
       SqlValidator validator, RexBuilder rexBuilder) {
-    return castToValidatedType(node, e, validator, rexBuilder, false);
+    return castToValidatedType(node.getParserPosition(), node, e, validator, rexBuilder, false);
   }
 
   /**
@@ -1531,13 +1541,13 @@ public class StandardConvertletTable extends ReflectiveConvertletTable {
    * was already of the validated type, then the value is returned without an
    * additional cast.
    */
-  public static RexNode castToValidatedType(SqlNode node, RexNode e,
+  public static RexNode castToValidatedType(SqlParserPos pos, SqlNode node, RexNode e,
       SqlValidator validator, RexBuilder rexBuilder, boolean safe) {
     final RelDataType type = validator.getValidatedNodeType(node);
     if (e.getType() == type) {
       return e;
     }
-    return rexBuilder.makeCast(type, e, safe, safe);
+    return rexBuilder.makeCast(pos, type, e, safe, safe);
   }
 
   /** Convertlet that handles {@code COVAR_POP}, {@code COVAR_SAMP},
@@ -1574,7 +1584,7 @@ public class StandardConvertletTable extends ReflectiveConvertletTable {
         throw Util.unexpected(kind);
       }
       RexNode rex = cx.convertExpression(expr);
-      return cx.getRexBuilder().ensureType(type, rex, true);
+      return cx.getRexBuilder().ensureType(call.getParserPosition(), type, rex, true);
     }
 
     private static SqlNode expandRegrSzz(
@@ -1706,7 +1716,7 @@ public class StandardConvertletTable extends ReflectiveConvertletTable {
         throw Util.unexpected(kind);
       }
       RexNode rex = cx.convertExpression(expr);
-      return cx.getRexBuilder().ensureType(type, rex, true);
+      return cx.getRexBuilder().ensureType(call.getParserPosition(), type, rex, true);
     }
 
     private static SqlNode expandAvg(
@@ -1856,7 +1866,7 @@ public class StandardConvertletTable extends ReflectiveConvertletTable {
       final RexBuilder rexBuilder = cx.getRexBuilder();
       final RexNode operand =
           cx.convertExpression(call.getOperandList().get(0));
-      return rexBuilder.makeCall(SqlStdOperatorTable.TRIM,
+      return rexBuilder.makeCall(call.getParserPosition(), SqlStdOperatorTable.TRIM,
           rexBuilder.makeFlag(flag), rexBuilder.makeLiteral(" "), operand);
     }
   }
@@ -1897,7 +1907,8 @@ public class StandardConvertletTable extends ReflectiveConvertletTable {
       final List<RexNode> list = new ArrayList<>();
       final List<RexNode> orList = new ArrayList<>();
       for (RexNode expr : exprs) {
-        orList.add(rexBuilder.makeCall(SqlStdOperatorTable.IS_NULL, expr));
+        orList.add(
+            rexBuilder.makeCall(call.getParserPosition(), SqlStdOperatorTable.IS_NULL, expr));
       }
       list.add(RexUtil.composeDisjunction(rexBuilder, orList));
       list.add(rexBuilder.makeNullLiteral(type));
@@ -1906,13 +1917,13 @@ public class StandardConvertletTable extends ReflectiveConvertletTable {
         final List<RexNode> andList = new ArrayList<>();
         for (int j = i + 1; j < exprs.size(); j++) {
           final RexNode expr2 = exprs.get(j);
-          andList.add(rexBuilder.makeCall(op, expr, expr2));
+          andList.add(rexBuilder.makeCall(call.getParserPosition(), op, expr, expr2));
         }
         list.add(RexUtil.composeConjunction(rexBuilder, andList));
         list.add(expr);
       }
       list.add(exprs.get(exprs.size() - 1));
-      return rexBuilder.makeCall(type, SqlStdOperatorTable.CASE, list);
+      return rexBuilder.makeCall(call.getParserPosition(), type, SqlStdOperatorTable.CASE, list);
     }
   }
 
@@ -2040,6 +2051,7 @@ public class StandardConvertletTable extends ReflectiveConvertletTable {
       //       FOR CASE WHEN length < 0 THEN 0 ELSE length END)
 
       final RexBuilder rexBuilder = cx.getRexBuilder();
+      final SqlParserPos pos = call.getParserPosition();
       final List<RexNode> exprs =
           convertOperands(cx, call, SqlOperandTypeChecker.Consistency.NONE);
       final RexNode value = exprs.get(0);
@@ -2049,10 +2061,10 @@ public class StandardConvertletTable extends ReflectiveConvertletTable {
       final RexLiteral oneLiteral = rexBuilder.makeLiteral(1, startType);
       final RexNode valueLength =
           SqlTypeUtil.isBinary(value.getType())
-              ? rexBuilder.makeCall(SqlStdOperatorTable.OCTET_LENGTH, value)
-              : rexBuilder.makeCall(SqlStdOperatorTable.CHAR_LENGTH, value);
+              ? rexBuilder.makeCall(pos, SqlStdOperatorTable.OCTET_LENGTH, value)
+              : rexBuilder.makeCall(pos, SqlStdOperatorTable.CHAR_LENGTH, value);
       final RexNode valueLengthPlusOne =
-          rexBuilder.makeCall(SqlStdOperatorTable.PLUS, valueLength,
+          rexBuilder.makeCall(pos, SqlStdOperatorTable.PLUS, valueLength,
               oneLiteral);
 
       final RexNode newStart;
@@ -2060,8 +2072,8 @@ public class StandardConvertletTable extends ReflectiveConvertletTable {
       case POSTGRESQL:
         if (call.operandCount() == 2) {
           newStart =
-              rexBuilder.makeCall(SqlStdOperatorTable.CASE,
-                  rexBuilder.makeCall(SqlStdOperatorTable.LESS_THAN, start,
+              rexBuilder.makeCall(pos, SqlStdOperatorTable.CASE,
+                  rexBuilder.makeCall(pos, SqlStdOperatorTable.LESS_THAN, start,
                       oneLiteral),
                   oneLiteral, start);
         } else {
@@ -2070,37 +2082,37 @@ public class StandardConvertletTable extends ReflectiveConvertletTable {
         break;
       case BIG_QUERY:
         newStart =
-            rexBuilder.makeCall(SqlStdOperatorTable.CASE,
-                rexBuilder.makeCall(SqlStdOperatorTable.EQUALS, start,
+            rexBuilder.makeCall(pos, SqlStdOperatorTable.CASE,
+                rexBuilder.makeCall(pos, SqlStdOperatorTable.EQUALS, start,
                     zeroLiteral),
                 oneLiteral,
-                rexBuilder.makeCall(SqlStdOperatorTable.LESS_THAN, start,
+                rexBuilder.makeCall(pos, SqlStdOperatorTable.LESS_THAN, start,
                     zeroLiteral),
-                rexBuilder.makeCall(SqlStdOperatorTable.PLUS, start,
+                rexBuilder.makeCall(pos, SqlStdOperatorTable.PLUS, start,
                     valueLengthPlusOne),
                 start);
         break;
       default:
         newStart =
-            rexBuilder.makeCall(SqlStdOperatorTable.CASE,
-                rexBuilder.makeCall(SqlStdOperatorTable.EQUALS, start,
+            rexBuilder.makeCall(pos, SqlStdOperatorTable.CASE,
+                rexBuilder.makeCall(pos, SqlStdOperatorTable.EQUALS, start,
                     zeroLiteral),
                 library == SqlLibrary.MYSQL ? valueLengthPlusOne : oneLiteral,
-                rexBuilder.makeCall(SqlStdOperatorTable.LESS_THAN,
-                    rexBuilder.makeCall(SqlStdOperatorTable.PLUS, start,
+                rexBuilder.makeCall(pos, SqlStdOperatorTable.LESS_THAN,
+                    rexBuilder.makeCall(pos, SqlStdOperatorTable.PLUS, start,
                         valueLengthPlusOne),
                     oneLiteral),
                 valueLengthPlusOne,
-                rexBuilder.makeCall(SqlStdOperatorTable.LESS_THAN, start,
+                rexBuilder.makeCall(pos, SqlStdOperatorTable.LESS_THAN, start,
                     zeroLiteral),
-                rexBuilder.makeCall(SqlStdOperatorTable.PLUS, start,
+                rexBuilder.makeCall(pos, SqlStdOperatorTable.PLUS, start,
                     valueLengthPlusOne),
                 start);
         break;
       }
 
       if (call.operandCount() == 2) {
-        return rexBuilder.makeCall(SqlStdOperatorTable.SUBSTRING, value,
+        return rexBuilder.makeCall(pos, SqlStdOperatorTable.SUBSTRING, value,
             newStart);
       }
 
@@ -2113,19 +2125,19 @@ public class StandardConvertletTable extends ReflectiveConvertletTable {
         break;
       default:
         newLength =
-            rexBuilder.makeCall(SqlStdOperatorTable.CASE,
-                rexBuilder.makeCall(SqlStdOperatorTable.LESS_THAN, length,
+            rexBuilder.makeCall(pos, SqlStdOperatorTable.CASE,
+                rexBuilder.makeCall(pos, SqlStdOperatorTable.LESS_THAN, length,
                     zeroLiteral),
                 zeroLiteral, length);
       }
       final RexNode substringCall =
-          rexBuilder.makeCall(SqlStdOperatorTable.SUBSTRING, value, newStart,
+          rexBuilder.makeCall(pos, SqlStdOperatorTable.SUBSTRING, value, newStart,
               newLength);
       switch (library) {
       case BIG_QUERY:
-        return rexBuilder.makeCall(SqlStdOperatorTable.CASE,
-            rexBuilder.makeCall(SqlStdOperatorTable.LESS_THAN,
-                rexBuilder.makeCall(SqlStdOperatorTable.PLUS, start,
+        return rexBuilder.makeCall(pos, SqlStdOperatorTable.CASE,
+            rexBuilder.makeCall(pos, SqlStdOperatorTable.LESS_THAN,
+                rexBuilder.makeCall(pos, SqlStdOperatorTable.PLUS, start,
                     valueLengthPlusOne), oneLiteral),
             value, substringCall);
       default:
@@ -2144,6 +2156,7 @@ public class StandardConvertletTable extends ReflectiveConvertletTable {
       //  => timestamp + interval
       // "timestamp" may be of type TIMESTAMP or TIMESTAMP WITH LOCAL TIME ZONE.
       final RexBuilder rexBuilder = cx.getRexBuilder();
+      final SqlParserPos pos = call.getParserPosition();
       SqlIntervalQualifier qualifier;
       final RexNode op1;
       final RexNode op2;
@@ -2204,8 +2217,8 @@ public class StandardConvertletTable extends ReflectiveConvertletTable {
         // If the TIMESTAMPADD call has type TIMESTAMP and op2 has type DATE
         // (which can happen for sub-day time frames such as HOUR), cast op2 to
         // TIMESTAMP.
-        final RexNode op2b = rexBuilder.makeCast(type, op2);
-        return rexBuilder.makeCall(type, SqlStdOperatorTable.TIMESTAMP_ADD,
+        final RexNode op2b = rexBuilder.makeCast(pos, type, op2);
+        return rexBuilder.makeCall(pos, type, SqlStdOperatorTable.TIMESTAMP_ADD,
             ImmutableList.of(timeFrameName, op1, op2b));
       }
 
@@ -2220,19 +2233,19 @@ public class StandardConvertletTable extends ReflectiveConvertletTable {
       case MICROSECOND:
       case NANOSECOND:
         interval2Add =
-            divide(rexBuilder,
-                multiply(rexBuilder,
+            divide(pos, rexBuilder,
+                multiply(pos, rexBuilder,
                     rexBuilder.makeIntervalLiteral(BigDecimal.ONE, qualifier), op1),
                 BigDecimal.ONE.divide(unit.multiplier,
                     RoundingMode.UNNECESSARY));
         break;
       default:
         interval2Add =
-            multiply(rexBuilder,
+            multiply(pos, rexBuilder,
                 rexBuilder.makeIntervalLiteral(unit.multiplier, qualifier), op1);
       }
 
-      return rexBuilder.makeCall(SqlStdOperatorTable.DATETIME_PLUS,
+      return rexBuilder.makeCall(pos, SqlStdOperatorTable.DATETIME_PLUS,
           op2, interval2Add);
     }
   }
@@ -2243,13 +2256,14 @@ public class StandardConvertletTable extends ReflectiveConvertletTable {
   private static class TruncConvertlet implements SqlRexConvertlet {
     @Override public RexNode convertCall(SqlRexContext cx, SqlCall call) {
       final RexBuilder rexBuilder = cx.getRexBuilder();
+      final SqlParserPos pos = call.getParserPosition();
       RexNode op1 = cx.convertExpression(call.operand(0));
       RexNode op2 = cx.convertExpression(call.operand(1));
       if (op1.getType().getSqlTypeName() == SqlTypeName.DATE) {
         RelDataType type = cx.getValidator().getValidatedNodeType(call);
-        op1 = cx.getRexBuilder().makeCast(type, op1);
+        op1 = cx.getRexBuilder().makeCast(pos, type, op1);
       }
-      return rexBuilder.makeCall(call.getOperator(), op1, op2);
+      return rexBuilder.makeCall(pos, call.getOperator(), op1, op2);
     }
   }
 
@@ -2259,6 +2273,7 @@ public class StandardConvertletTable extends ReflectiveConvertletTable {
       // TIMESTAMP_SUB(timestamp, interval)
       //  => timestamp - count * INTERVAL '1' UNIT
       final RexBuilder rexBuilder = cx.getRexBuilder();
+      final SqlParserPos pos = call.getParserPosition();
       final SqlBasicCall operandCall = call.operand(1);
       SqlIntervalQualifier qualifier = operandCall.operand(1);
       final RexNode op1 = cx.convertExpression(operandCall.operand(0));
@@ -2272,19 +2287,19 @@ public class StandardConvertletTable extends ReflectiveConvertletTable {
       case MICROSECOND:
       case NANOSECOND:
         interval2Sub =
-            divide(rexBuilder,
-                multiply(rexBuilder,
+            divide(pos, rexBuilder,
+                multiply(pos, rexBuilder,
                     rexBuilder.makeIntervalLiteral(BigDecimal.ONE, qualifier), op1),
                 BigDecimal.ONE.divide(unit.multiplier,
                     RoundingMode.UNNECESSARY));
         break;
       default:
         interval2Sub =
-            multiply(rexBuilder,
+            multiply(pos, rexBuilder,
                 rexBuilder.makeIntervalLiteral(unit.multiplier, qualifier), op1);
       }
 
-      return rexBuilder.makeCall(SqlInternalOperators.MINUS_DATE2,
+      return rexBuilder.makeCall(pos, SqlInternalOperators.MINUS_DATE2,
           op2, interval2Sub);
     }
   }
@@ -2316,6 +2331,7 @@ public class StandardConvertletTable extends ReflectiveConvertletTable {
       final boolean preTruncate;
       final RexNode op1;
       final RexNode op2;
+      final SqlParserPos pos = call.getParserPosition();
       if (call.operand(0).getKind() == SqlKind.INTERVAL_QUALIFIER) {
         qualifier = call.operand(0);
         preTruncate = false;
@@ -2342,11 +2358,11 @@ public class StandardConvertletTable extends ReflectiveConvertletTable {
         // computing the difference.
         if (preTruncate) {
           truncateFn = e ->
-              rexBuilder.makeCall(e.getType(),
+              rexBuilder.makeCall(pos, e.getType(),
                   SqlLibraryOperators.TIMESTAMP_TRUNC,
                   ImmutableList.of(e, timeFrameName));
         }
-        return rexBuilder.makeCall(cx.getValidator().getValidatedNodeType(call),
+        return rexBuilder.makeCall(pos, cx.getValidator().getValidatedNodeType(call),
             SqlStdOperatorTable.TIMESTAMP_DIFF,
             ImmutableList.of(timeFrameName, truncateFn.apply(op1),
                 truncateFn.apply(op2)));
@@ -2358,7 +2374,7 @@ public class StandardConvertletTable extends ReflectiveConvertletTable {
         // should be returned.
         final RexNode timeUnit = cx.convertExpression(qualifier);
         truncateFn = e ->
-            rexBuilder.makeCall(e.getType(),
+            rexBuilder.makeCall(call.getParserPosition(), e.getType(),
                 SqlLibraryOperators.TIMESTAMP_TRUNC,
                 ImmutableList.of(e, timeUnit));
       }
@@ -2399,15 +2415,15 @@ public class StandardConvertletTable extends ReflectiveConvertletTable {
               typeFactory.createSqlIntervalType(qualifier),
               op1.getType().isNullable() || op2.getType().isNullable());
       final RexNode call2 =
-          rexBuilder.makeCall(intervalType,
+          rexBuilder.makeCall(pos, intervalType,
               SqlStdOperatorTable.MINUS_DATE,
               ImmutableList.of(truncateFn.apply(op2), truncateFn.apply(op1)));
       final RelDataType intType =
           typeFactory.createTypeWithNullability(
               typeFactory.createSqlType(sqlTypeName),
               SqlTypeUtil.containsNullable(call2.getType()));
-      RexNode e = rexBuilder.makeCast(intType, call2);
-      return rexBuilder.multiplyDivide(e, multiplier, divider);
+      RexNode e = rexBuilder.makeCast(pos, intType, call2);
+      return rexBuilder.multiplyDivide(pos, e, multiplier, divider);
     }
   }
 }
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 583052659b..86472f9d22 100644
--- a/core/src/main/java/org/apache/calcite/tools/RelBuilder.java
+++ b/core/src/main/java/org/apache/calcite/tools/RelBuilder.java
@@ -102,6 +102,7 @@ import org.apache.calcite.sql.fun.SqlInternalOperators;
 import org.apache.calcite.sql.fun.SqlLikeOperator;
 import org.apache.calcite.sql.fun.SqlQuantifyOperator;
 import org.apache.calcite.sql.fun.SqlStdOperatorTable;
+import org.apache.calcite.sql.parser.SqlParserPos;
 import org.apache.calcite.sql.type.SqlReturnTypeInference;
 import org.apache.calcite.sql.type.SqlTypeName;
 import org.apache.calcite.sql.type.TableFunctionReturnTypeInference;
@@ -736,7 +737,7 @@ public class RelBuilder {
   }
 
   /** Creates a call to a scalar operator. */
-  private RexCall call(SqlOperator operator, List<RexNode> operandList) {
+  private RexCall call(SqlParserPos pos, SqlOperator operator, List<RexNode> operandList) {
     switch (operator.getKind()) {
     case LIKE:
     case SIMILAR:
@@ -755,13 +756,13 @@ public class RelBuilder {
     }
     final RexBuilder builder = cluster.getRexBuilder();
     final RelDataType type = builder.deriveReturnType(operator, operandList);
-    return (RexCall) builder.makeCall(type, operator, operandList);
+    return (RexCall) builder.makeCall(pos, type, operator, operandList);
   }
 
   /** Creates a call to a scalar operator. */
   public RexNode call(SqlOperator operator,
       Iterable<? extends RexNode> operands) {
-    return call(operator, ImmutableList.copyOf(operands));
+    return call(SqlParserPos.ZERO, operator, ImmutableList.copyOf(operands));
   }
 
   /** Creates an IN predicate with a list of values.
@@ -1164,25 +1165,43 @@ public class RelBuilder {
 
   /** Creates an expression that casts an expression to a given type. */
   public RexNode cast(RexNode expr, SqlTypeName typeName) {
+    return cast(SqlParserPos.ZERO, expr, typeName);
+  }
+
+  /** Creates an expression that casts an expression to a given type. */
+  public RexNode cast(SqlParserPos pos, RexNode expr, SqlTypeName typeName) {
     final RelDataType type = cluster.getTypeFactory().createSqlType(typeName);
-    return cluster.getRexBuilder().makeCast(type, expr);
+    return cluster.getRexBuilder().makeCast(pos, type, expr);
   }
 
   /** Creates an expression that casts an expression to a type with a given name
    * and precision or length. */
   public RexNode cast(RexNode expr, SqlTypeName typeName, int precision) {
+    return cast(SqlParserPos.ZERO, expr, typeName, precision);
+  }
+
+    /** Creates an expression that casts an expression to a type with a given name
+     * and precision or length. */
+  public RexNode cast(SqlParserPos pos, RexNode expr, SqlTypeName typeName, int precision) {
     final RelDataType type =
         cluster.getTypeFactory().createSqlType(typeName, precision);
-    return cluster.getRexBuilder().makeCast(type, expr);
+    return cluster.getRexBuilder().makeCast(pos, type, expr);
   }
 
   /** Creates an expression that casts an expression to a type with a given
    * name, precision and scale. */
   public RexNode cast(RexNode expr, SqlTypeName typeName, int precision,
       int scale) {
+    return cast(SqlParserPos.ZERO, expr, typeName, precision, scale);
+  }
+
+  /** Creates an expression that casts an expression to a type with a given
+   * name, precision and scale. */
+  public RexNode cast(SqlParserPos pos, RexNode expr, SqlTypeName typeName, int precision,
+      int scale) {
     final RelDataType type =
         cluster.getTypeFactory().createSqlType(typeName, precision, scale);
-    return cluster.getRexBuilder().makeCast(type, expr);
+    return cluster.getRexBuilder().makeCast(pos, type, expr);
   }
 
   /**
@@ -1429,6 +1448,36 @@ public class RelBuilder {
         ImmutableList.copyOf(operands));
   }
 
+  /** Creates a call to an aggregate function.
+   *
+   * <p>To add other operands, apply
+   * {@link AggCall#distinct()},
+   * {@link AggCall#approximate(boolean)},
+   * {@link AggCall#filter(RexNode...)},
+   * {@link AggCall#sort},
+   * {@link AggCall#as} to the result. */
+  public AggCall aggregateCall(SqlParserPos pos, SqlAggFunction aggFunction,
+      Iterable<? extends RexNode> operands) {
+    return aggregateCall(pos, aggFunction, false, false, false, null, null,
+        ImmutableList.of(), null, ImmutableList.of(),
+        ImmutableList.copyOf(operands));
+  }
+
+  /** Creates a call to an aggregate function.
+   *
+   * <p>To add other operands, apply
+   * {@link AggCall#distinct()},
+   * {@link AggCall#approximate(boolean)},
+   * {@link AggCall#filter(RexNode...)},
+   * {@link AggCall#sort},
+   * {@link AggCall#as} to the result. */
+  public AggCall aggregateCall(SqlParserPos pos, SqlAggFunction aggFunction,
+      RexNode... operands) {
+    return aggregateCall(pos, aggFunction, false, false, false, null, null,
+        ImmutableList.of(), null, ImmutableList.of(),
+        ImmutableList.copyOf(operands));
+  }
+
   /** Creates a call to an aggregate function.
    *
    * <p>To add other operands, apply
@@ -1447,7 +1496,8 @@ public class RelBuilder {
   /** Creates a call to an aggregate function as a copy of an
    * {@link AggregateCall}. */
   public AggCall aggregateCall(AggregateCall a) {
-    return aggregateCall(a.getAggregation(), a.isDistinct(), a.isApproximate(),
+    return aggregateCall(a.getParserPosition(),
+        a.getAggregation(), a.isDistinct(), a.isApproximate(),
         a.ignoreNulls(), a.filterArg < 0 ? null : field(a.filterArg),
         a.distinctKeys == null ? null : fields(a.distinctKeys),
         fields(a.collation), a.name, ImmutableList.copyOf(a.rexList),
@@ -1457,7 +1507,8 @@ public class RelBuilder {
   /** Creates a call to an aggregate function as a copy of an
    * {@link AggregateCall}, applying a mapping. */
   public AggCall aggregateCall(AggregateCall a, Mapping mapping) {
-    return aggregateCall(a.getAggregation(), a.isDistinct(), a.isApproximate(),
+    return aggregateCall(a.getParserPosition(),
+        a.getAggregation(), a.isDistinct(), a.isApproximate(),
         a.ignoreNulls(),
         a.filterArg < 0 ? null : field(Mappings.apply(mapping, a.filterArg)),
         a.distinctKeys == null ? null
@@ -1473,7 +1524,17 @@ public class RelBuilder {
       @Nullable ImmutableList<RexNode> distinctKeys,
       ImmutableList<RexNode> orderKeys, @Nullable String alias,
       ImmutableList<RexNode> preOperands, ImmutableList<RexNode> operands) {
-    return new AggCallImpl(aggFunction, distinct, approximate, ignoreNulls,
+    return new AggCallImpl(SqlParserPos.ZERO, aggFunction, distinct, approximate, ignoreNulls,
+        filter, alias, preOperands, operands, distinctKeys, orderKeys);
+  }
+
+  /** Creates a call to an aggregate function with all applicable operands. */
+  protected AggCall aggregateCall(SqlParserPos pos, SqlAggFunction aggFunction, boolean distinct,
+      boolean approximate, boolean ignoreNulls, @Nullable RexNode filter,
+      @Nullable ImmutableList<RexNode> distinctKeys,
+      ImmutableList<RexNode> orderKeys, @Nullable String alias,
+      ImmutableList<RexNode> preOperands, ImmutableList<RexNode> operands) {
+    return new AggCallImpl(pos, aggFunction, distinct, approximate, ignoreNulls,
         filter, alias, preOperands, operands, distinctKeys, orderKeys);
   }
 
@@ -1515,6 +1576,11 @@ public class RelBuilder {
     return sum(false, null, operand);
   }
 
+  /** Creates a call to the {@code SUM} aggregate function. */
+  public AggCall sum(SqlParserPos pos, RexNode operand) {
+    return sum(pos, false, null, operand);
+  }
+
   /** Creates a call to the {@code SUM} aggregate function,
    * optionally distinct and with an alias. */
   public AggCall sum(boolean distinct, @Nullable String alias,
@@ -1524,16 +1590,39 @@ public class RelBuilder {
         ImmutableList.of(operand));
   }
 
+  /** Creates a call to the {@code SUM} aggregate function,
+   * optionally distinct and with an alias. */
+  public AggCall sum(SqlParserPos pos, boolean distinct, @Nullable String alias,
+      RexNode operand) {
+    return aggregateCall(pos, SqlStdOperatorTable.SUM, distinct, false, false, null,
+        null, ImmutableList.of(), alias, ImmutableList.of(),
+        ImmutableList.of(operand));
+  }
+
   /** Creates a call to the {@code AVG} aggregate function. */
   public AggCall avg(RexNode operand) {
     return avg(false, null, operand);
   }
 
+  /** Creates a call to the {@code AVG} aggregate function. */
+  public AggCall avg(SqlParserPos pos, RexNode operand) {
+    return avg(pos, false, null, operand);
+  }
+
   /** Creates a call to the {@code AVG} aggregate function,
    * optionally distinct and with an alias. */
   public AggCall avg(boolean distinct, @Nullable String alias,
       RexNode operand) {
-    return aggregateCall(SqlStdOperatorTable.AVG, distinct, false, false, null,
+    return aggregateCall(SqlParserPos.ZERO, SqlStdOperatorTable.AVG, distinct, false, false, null,
+        null, ImmutableList.of(), alias, ImmutableList.of(),
+        ImmutableList.of(operand));
+  }
+
+  /** Creates a call to the {@code AVG} aggregate function,
+   * optionally distinct and with an alias. */
+  public AggCall avg(SqlParserPos pos, boolean distinct, @Nullable String alias,
+      RexNode operand) {
+    return aggregateCall(pos, SqlStdOperatorTable.AVG, distinct, false, false, null,
         null, ImmutableList.of(), alias, ImmutableList.of(),
         ImmutableList.of(operand));
   }
@@ -1766,7 +1855,7 @@ public class RelBuilder {
       inputs.add(0, build());
     }
 
-    final RexCall call = call(operator, ImmutableList.copyOf(operands));
+    final RexCall call = call(SqlParserPos.ZERO, operator, ImmutableList.copyOf(operands));
     final RelNode functionScan =
         struct.tableFunctionScanFactory.createTableFunctionScan(cluster,
             inputs, call, null, getColumnMappings(operator));
@@ -4055,6 +4144,8 @@ public class RelBuilder {
    *
    * @see RelBuilder#aggregateCall */
   public interface AggCall {
+    SqlParserPos getPosition();
+
     /** Returns a copy of this AggCall that applies a filter before aggregating
      * values. */
     AggCall filter(@Nullable RexNode condition);
@@ -4223,6 +4314,7 @@ public class RelBuilder {
 
   /** Implementation of {@link AggCall}. */
   private class AggCallImpl implements AggCallPlus {
+    private final SqlParserPos pos;
     private final SqlAggFunction aggFunction;
     private final boolean distinct;
     private final boolean approximate;
@@ -4234,12 +4326,13 @@ public class RelBuilder {
     private final @Nullable ImmutableList<RexNode> distinctKeys; // may be empty or null
     private final ImmutableList<RexNode> orderKeys; // may be empty
 
-    AggCallImpl(SqlAggFunction aggFunction, boolean distinct,
+    AggCallImpl(SqlParserPos pos, SqlAggFunction aggFunction, boolean distinct,
         boolean approximate, boolean ignoreNulls, @Nullable RexNode filter,
         @Nullable String alias, ImmutableList<RexNode> preOperands,
         ImmutableList<RexNode> operands,
         @Nullable ImmutableList<RexNode> distinctKeys,
         ImmutableList<RexNode> orderKeys) {
+      this.pos = pos;
       this.aggFunction = requireNonNull(aggFunction, "aggFunction");
       // If the aggregate function ignores DISTINCT,
       // make the DISTINCT flag FALSE.
@@ -4263,6 +4356,10 @@ public class RelBuilder {
       this.filter = filter;
     }
 
+    @Override public SqlParserPos getPosition() {
+      return pos;
+    }
+
     @Override public String toString() {
       final StringBuilder b = new StringBuilder();
       b.append(aggFunction.getName())
@@ -4310,7 +4407,7 @@ public class RelBuilder {
       final RelCollation collation = RelCollations.EMPTY;
       final RelDataType type =
           getTypeFactory().createSqlType(SqlTypeName.BOOLEAN);
-      return AggregateCall.create(aggFunction, distinct, approximate,
+      return AggregateCall.create(pos, aggFunction, distinct, approximate,
           ignoreNulls, preOperands, ImmutableList.of(), -1,
           null, collation, type, alias);
     }
@@ -4346,7 +4443,7 @@ public class RelBuilder {
             .collect(toImmutableList());
       }
 
-      return AggregateCall.create(aggFunction, distinct, approximate,
+      return AggregateCall.create(pos, aggFunction, distinct, approximate,
           ignoreNulls, preOperands, args, filterArg, distinctKeys,
           collation, groupSet.cardinality(), r, null, alias);
     }
@@ -4368,7 +4465,7 @@ public class RelBuilder {
           ImmutableList.copyOf(preOperands);
       return preOperandList.equals(this.preOperands)
           ? this
-          : new AggCallImpl(aggFunction, distinct, approximate, ignoreNulls,
+          : new AggCallImpl(pos, aggFunction, distinct, approximate, ignoreNulls,
               filter, alias, preOperandList, operands, distinctKeys, orderKeys);
     }
 
@@ -4382,7 +4479,7 @@ public class RelBuilder {
           ImmutableList.copyOf(orderKeys);
       return orderKeyList.equals(this.orderKeys)
           ? this
-          : new AggCallImpl(aggFunction, distinct, approximate, ignoreNulls,
+          : new AggCallImpl(pos, aggFunction, distinct, approximate, ignoreNulls,
               filter, alias, preOperands, operands, distinctKeys, orderKeyList);
     }
 
@@ -4395,42 +4492,42 @@ public class RelBuilder {
           distinctKeys == null ? null : ImmutableList.copyOf(distinctKeys);
       return Objects.equals(distinctKeyList, this.distinctKeys)
           ? this
-          : new AggCallImpl(aggFunction, distinct, approximate, ignoreNulls,
+          : new AggCallImpl(pos, aggFunction, distinct, approximate, ignoreNulls,
               filter, alias, preOperands, operands, distinctKeyList, orderKeys);
     }
 
     @Override public AggCall approximate(boolean approximate) {
       return approximate == this.approximate
           ? this
-          : new AggCallImpl(aggFunction, distinct, approximate, ignoreNulls,
+          : new AggCallImpl(pos, aggFunction, distinct, approximate, ignoreNulls,
               filter, alias, preOperands, operands, distinctKeys, orderKeys);
     }
 
     @Override public AggCall filter(@Nullable RexNode condition) {
       return Objects.equals(condition, this.filter)
           ? this
-          : new AggCallImpl(aggFunction, distinct, approximate, ignoreNulls,
+          : new AggCallImpl(pos, aggFunction, distinct, approximate, ignoreNulls,
               condition, alias, preOperands, operands, distinctKeys, orderKeys);
     }
 
     @Override public AggCall as(@Nullable String alias) {
       return Objects.equals(alias, this.alias)
           ? this
-          : new AggCallImpl(aggFunction, distinct, approximate, ignoreNulls,
+          : new AggCallImpl(pos, aggFunction, distinct, approximate, ignoreNulls,
               filter, alias, preOperands, operands, distinctKeys, orderKeys);
     }
 
     @Override public AggCall distinct(boolean distinct) {
       return distinct == this.distinct
           ? this
-          : new AggCallImpl(aggFunction, distinct, approximate, ignoreNulls,
+          : new AggCallImpl(pos, aggFunction, distinct, approximate, ignoreNulls,
               filter, alias, preOperands, operands, distinctKeys, orderKeys);
     }
 
     @Override public AggCall ignoreNulls(boolean ignoreNulls) {
       return ignoreNulls == this.ignoreNulls
           ? this
-          : new AggCallImpl(aggFunction, distinct, approximate, ignoreNulls,
+          : new AggCallImpl(pos, aggFunction, distinct, approximate, ignoreNulls,
               filter, alias, preOperands, operands, distinctKeys, orderKeys);
     }
   }
@@ -4452,6 +4549,10 @@ public class RelBuilder {
           aggregateCall.name);
     }
 
+    @Override public SqlParserPos getPosition() {
+      return this.aggregateCall.getParserPosition();
+    }
+
     @Override public String toString() {
       return aggregateCall.toString();
     }
diff --git a/core/src/test/java/org/apache/calcite/materialize/NormalizationTrimFieldTest.java b/core/src/test/java/org/apache/calcite/materialize/NormalizationTrimFieldTest.java
index c53b822b21..06cb1c5de9 100644
--- a/core/src/test/java/org/apache/calcite/materialize/NormalizationTrimFieldTest.java
+++ b/core/src/test/java/org/apache/calcite/materialize/NormalizationTrimFieldTest.java
@@ -84,7 +84,7 @@ public class NormalizationTrimFieldTest extends SqlToRelTestBase {
     final ImmutableBitSet groupSet = ImmutableBitSet.of(4);
     final AggregateCall count = aggregate.getAggCallList().get(0);
     final AggregateCall call =
-        AggregateCall.create(count.getAggregation(),
+        AggregateCall.create(count.getParserPosition(), count.getAggregation(),
             count.isDistinct(), count.isApproximate(),
             count.ignoreNulls(), count.rexList, ImmutableList.of(3),
             count.filterArg, null, count.collation,
diff --git a/core/src/test/java/org/apache/calcite/test/fuzzer/RexShrinker.java b/core/src/test/java/org/apache/calcite/test/fuzzer/RexShrinker.java
index 9cf470fdd1..84272d0de6 100644
--- a/core/src/test/java/org/apache/calcite/test/fuzzer/RexShrinker.java
+++ b/core/src/test/java/org/apache/calcite/test/fuzzer/RexShrinker.java
@@ -76,7 +76,7 @@ public class RexShrinker extends RexShuttle {
       if (res != null) {
         didWork = true;
         if (!res.getType().equals(type)) {
-          return rexBuilder.makeCast(type, res);
+          return rexBuilder.makeCast(call.getParserPosition(), type, res);
         }
         return res;
       }