Repository: calcite Updated Branches: refs/heads/master a5f09f5a5 -> 8f1494546
[CALCITE-2571] TRIM function now trims more than one character (Andrew Pilloud) If strict SQL conformance, still just trims one character, per SQL standard. Make SQL conformance available to RexToLixTranslator (Julian Hyde). Close apache/calcite#840 A previous PR: Close apache/calcite#820 Project: http://git-wip-us.apache.org/repos/asf/calcite/repo Commit: http://git-wip-us.apache.org/repos/asf/calcite/commit/8f149454 Tree: http://git-wip-us.apache.org/repos/asf/calcite/tree/8f149454 Diff: http://git-wip-us.apache.org/repos/asf/calcite/diff/8f149454 Branch: refs/heads/master Commit: 8f14945461b3c20cba3f2d794a6ac950b7687da9 Parents: a5f09f5 Author: Andrew Pilloud <[email protected]> Authored: Wed Sep 19 09:21:04 2018 -0700 Committer: Julian Hyde <[email protected]> Committed: Mon Sep 24 00:29:00 2018 -0400 ---------------------------------------------------------------------- .../adapter/enumerable/EnumerableAggregate.java | 3 +- .../adapter/enumerable/EnumerableCalc.java | 8 +- .../enumerable/EnumerableRelImplementor.java | 7 ++ .../enumerable/EnumerableTableFunctionScan.java | 3 +- .../adapter/enumerable/EnumerableThetaJoin.java | 3 +- .../adapter/enumerable/EnumerableWindow.java | 15 +-- .../calcite/adapter/enumerable/RexImpTable.java | 4 +- .../adapter/enumerable/RexToLixTranslator.java | 104 ++++++++----------- .../calcite/interpreter/AggregateNode.java | 7 +- .../calcite/interpreter/JaninoRexCompiler.java | 8 +- .../calcite/jdbc/CalciteConnectionImpl.java | 1 - .../org/apache/calcite/plan/RelImplementor.java | 3 + .../calcite/prepare/CalcitePrepareImpl.java | 2 + .../calcite/rel/type/RelDataTypeSystemImpl.java | 4 + .../org/apache/calcite/rex/RexExecutorImpl.java | 8 +- .../apache/calcite/runtime/SqlFunctions.java | 21 ++-- .../sql/validate/SqlAbstractConformance.java | 4 + .../calcite/sql/validate/SqlConformance.java | 21 ++++ .../sql/validate/SqlConformanceEnum.java | 13 +++ .../org/apache/calcite/util/BuiltInMethod.java | 2 +- .../calcite/sql/test/SqlOperatorBaseTest.java | 46 ++++---- .../apache/calcite/sql/test/SqlTestFactory.java | 7 ++ .../java/org/apache/calcite/test/JdbcTest.java | 4 +- .../calcite/adapter/spark/SparkRules.java | 6 +- .../spark/SparkToEnumerableConverter.java | 5 + 25 files changed, 194 insertions(+), 115 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/calcite/blob/8f149454/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableAggregate.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableAggregate.java b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableAggregate.java index 56f3d95..49af4ba 100644 --- a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableAggregate.java +++ b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableAggregate.java @@ -315,7 +315,8 @@ public class EnumerableAggregate extends Aggregate implements EnumerableRel { currentBlock(), new RexToLixTranslator.InputGetterImpl( Collections.singletonList( - Pair.of((Expression) inParameter, inputPhysType)))) + Pair.of((Expression) inParameter, inputPhysType))), + implementor.getConformance()) .setNullable(currentNullables()); } }; http://git-wip-us.apache.org/repos/asf/calcite/blob/8f149454/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableCalc.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableCalc.java b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableCalc.java index 4b043c5..5e38a8b 100644 --- a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableCalc.java +++ b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableCalc.java @@ -42,6 +42,8 @@ import org.apache.calcite.rex.RexBuilder; import org.apache.calcite.rex.RexProgram; import org.apache.calcite.rex.RexSimplify; import org.apache.calcite.rex.RexUtil; +import org.apache.calcite.sql.validate.SqlConformance; +import org.apache.calcite.sql.validate.SqlConformanceEnum; import org.apache.calcite.util.BuiltInMethod; import org.apache.calcite.util.Pair; import org.apache.calcite.util.Util; @@ -164,7 +166,7 @@ public class EnumerableCalc extends Calc implements EnumerableRel { new RexToLixTranslator.InputGetterImpl( Collections.singletonList( Pair.of(input, result.physType))), - implementor.allCorrelateVariables); + implementor.allCorrelateVariables, implementor.getConformance()); builder2.add( Expressions.ifThen( condition, @@ -183,10 +185,14 @@ public class EnumerableCalc extends Calc implements EnumerableRel { } final BlockBuilder builder3 = new BlockBuilder(); + final SqlConformance conformance = + (SqlConformance) implementor.map.getOrDefault("_conformance", + SqlConformanceEnum.DEFAULT); List<Expression> expressions = RexToLixTranslator.translateProjects( program, typeFactory, + conformance, builder3, physType, DataContext.ROOT, http://git-wip-us.apache.org/repos/asf/calcite/blob/8f149454/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableRelImplementor.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableRelImplementor.java b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableRelImplementor.java index 548a891..81ddc32 100644 --- a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableRelImplementor.java +++ b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableRelImplementor.java @@ -40,6 +40,8 @@ import org.apache.calcite.linq4j.tree.Types; import org.apache.calcite.linq4j.tree.VisitorImpl; import org.apache.calcite.rex.RexBuilder; import org.apache.calcite.runtime.Bindable; +import org.apache.calcite.sql.validate.SqlConformance; +import org.apache.calcite.sql.validate.SqlConformanceEnum; import org.apache.calcite.util.BuiltInMethod; import com.google.common.collect.Collections2; @@ -470,6 +472,11 @@ public class EnumerableRelImplementor extends JavaRelImplementor { block, physType, ((PhysTypeImpl) physType).format); } + public SqlConformance getConformance() { + return (SqlConformance) map.getOrDefault("_conformance", + SqlConformanceEnum.DEFAULT); + } + /** Visitor that finds types in an {@link Expression} tree. */ private static class TypeFinder extends VisitorImpl<Void> { private final Collection<Type> types; http://git-wip-us.apache.org/repos/asf/calcite/blob/8f149454/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableTableFunctionScan.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableTableFunctionScan.java b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableTableFunctionScan.java index 0a0b810..cc5b49a 100644 --- a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableTableFunctionScan.java +++ b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableTableFunctionScan.java @@ -78,7 +78,8 @@ public class EnumerableTableFunctionScan extends TableFunctionScan PhysTypeImpl.of(implementor.getTypeFactory(), getRowType(), format, false); RexToLixTranslator t = RexToLixTranslator.forAggregation( - (JavaTypeFactory) getCluster().getTypeFactory(), bb, null); + (JavaTypeFactory) getCluster().getTypeFactory(), bb, null, + implementor.getConformance()); t = t.setCorrelates(implementor.allCorrelateVariables); bb.add(Expressions.return_(null, t.translate(getCall()))); return implementor.result(physType, bb.toBlock()); http://git-wip-us.apache.org/repos/asf/calcite/blob/8f149454/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableThetaJoin.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableThetaJoin.java b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableThetaJoin.java index dc400ad..b052247 100644 --- a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableThetaJoin.java +++ b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableThetaJoin.java @@ -180,7 +180,8 @@ public class EnumerableThetaJoin extends Join implements EnumerableRel { new RexToLixTranslator.InputGetterImpl( ImmutableList.of(Pair.of((Expression) left_, leftPhysType), Pair.of((Expression) right_, rightPhysType))), - implementor.allCorrelateVariables))); + implementor.allCorrelateVariables, + implementor.getConformance()))); return Expressions.lambda(Predicate2.class, builder.toBlock(), left_, right_); } http://git-wip-us.apache.org/repos/asf/calcite/blob/8f149454/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableWindow.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableWindow.java b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableWindow.java index bcce732..d013c20 100644 --- a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableWindow.java +++ b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableWindow.java @@ -48,6 +48,7 @@ import org.apache.calcite.rex.RexNode; import org.apache.calcite.rex.RexWindowBound; import org.apache.calcite.runtime.SortedMultiMap; import org.apache.calcite.sql.SqlAggFunction; +import org.apache.calcite.sql.validate.SqlConformance; import org.apache.calcite.util.BuiltInMethod; import org.apache.calcite.util.ImmutableBitSet; import org.apache.calcite.util.Pair; @@ -273,7 +274,7 @@ public class EnumerableWindow extends Window implements EnumerableRel { final RexToLixTranslator translator = RexToLixTranslator.forAggregation(typeFactory, builder4, - inputGetter); + inputGetter, implementor.getConformance()); final List<Expression> outputRow = new ArrayList<>(); int fieldCountWithAggResults = @@ -422,9 +423,9 @@ public class EnumerableWindow extends Window implements EnumerableRel { final PhysType inputPhysTypeFinal = inputPhysType; final Function<BlockBuilder, WinAggFrameResultContext> resultContextBuilder = - getBlockBuilderWinAggFrameResultContextFunction(typeFactory, result, - translatedConstants, comparator_, rows_, i_, startX, endX, - minX, maxX, + getBlockBuilderWinAggFrameResultContextFunction(typeFactory, + implementor.getConformance(), result, translatedConstants, + comparator_, rows_, i_, startX, endX, minX, maxX, hasRows, frameRowCount, partitionRowCount, jDecl, inputPhysTypeFinal); @@ -518,8 +519,8 @@ public class EnumerableWindow extends Window implements EnumerableRel { private Function<BlockBuilder, WinAggFrameResultContext> getBlockBuilderWinAggFrameResultContextFunction( - final JavaTypeFactory typeFactory, final Result result, - final List<Expression> translatedConstants, + final JavaTypeFactory typeFactory, final SqlConformance conformance, + final Result result, final List<Expression> translatedConstants, final Expression comparator_, final Expression rows_, final ParameterExpression i_, final Expression startX, final Expression endX, @@ -538,7 +539,7 @@ public class EnumerableWindow extends Window implements EnumerableRel { translatedConstants); return RexToLixTranslator.forAggregation(typeFactory, - block, inputGetter); + block, inputGetter, conformance); } public Expression computeIndex(Expression offset, http://git-wip-us.apache.org/repos/asf/calcite/blob/8f149454/core/src/main/java/org/apache/calcite/adapter/enumerable/RexImpTable.java ---------------------------------------------------------------------- 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 d9eb63b..ac03f7d 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 @@ -1724,6 +1724,7 @@ public class RexImpTable { private static class TrimImplementor implements NotNullImplementor { public Expression implement(RexToLixTranslator translator, RexCall call, List<Expression> translatedOperands) { + final boolean strict = !translator.conformance.allowExtendedTrim(); final Object value = ((ConstantExpression) translatedOperands.get(0)).value; SqlTrimFunction.Flag flag = (SqlTrimFunction.Flag) value; @@ -1736,7 +1737,8 @@ public class RexImpTable { flag == SqlTrimFunction.Flag.BOTH || flag == SqlTrimFunction.Flag.TRAILING), translatedOperands.get(1), - translatedOperands.get(2)); + translatedOperands.get(2), + Expressions.constant(strict)); } } http://git-wip-us.apache.org/repos/asf/calcite/blob/8f149454/core/src/main/java/org/apache/calcite/adapter/enumerable/RexToLixTranslator.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/adapter/enumerable/RexToLixTranslator.java b/core/src/main/java/org/apache/calcite/adapter/enumerable/RexToLixTranslator.java index 0c460d3..a798242 100644 --- a/core/src/main/java/org/apache/calcite/adapter/enumerable/RexToLixTranslator.java +++ b/core/src/main/java/org/apache/calcite/adapter/enumerable/RexToLixTranslator.java @@ -46,6 +46,7 @@ import org.apache.calcite.sql.SqlIntervalQualifier; import org.apache.calcite.sql.SqlKind; import org.apache.calcite.sql.SqlOperator; import org.apache.calcite.sql.type.SqlTypeUtil; +import org.apache.calcite.sql.validate.SqlConformance; import org.apache.calcite.util.BuiltInMethod; import org.apache.calcite.util.ControlFlowException; import org.apache.calcite.util.Pair; @@ -62,6 +63,7 @@ import java.util.Collections; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.Objects; import static org.apache.calcite.sql.fun.OracleSqlOperatorTable.TRANSLATE3; import static org.apache.calcite.sql.fun.SqlStdOperatorTable.CHARACTER_LENGTH; @@ -90,6 +92,7 @@ public class RexToLixTranslator { final JavaTypeFactory typeFactory; final RexBuilder builder; private final RexProgram program; + final SqlConformance conformance; private final Expression root; private final RexToLixTranslator.InputGetter inputGetter; private final BlockBuilder list; @@ -106,57 +109,26 @@ public class RexToLixTranslator { } } - private RexToLixTranslator(RexProgram program, JavaTypeFactory typeFactory, - Expression root, InputGetter inputGetter, BlockBuilder list) { - this(program, typeFactory, root, inputGetter, list, - Collections.emptyMap(), - new RexBuilder(typeFactory)); - } - - private RexToLixTranslator( - RexProgram program, - JavaTypeFactory typeFactory, - Expression root, - InputGetter inputGetter, - BlockBuilder list, - Map<RexNode, Boolean> exprNullableMap, - RexBuilder builder) { - this(program, typeFactory, root, inputGetter, list, exprNullableMap, - builder, null); - } - - private RexToLixTranslator( - RexProgram program, - JavaTypeFactory typeFactory, - Expression root, - InputGetter inputGetter, - BlockBuilder list, - Map<? extends RexNode, Boolean> exprNullableMap, - RexBuilder builder, - RexToLixTranslator parent) { - this(program, typeFactory, root, inputGetter, list, exprNullableMap, - builder, parent, null); - } - - private RexToLixTranslator( - RexProgram program, + private RexToLixTranslator(RexProgram program, JavaTypeFactory typeFactory, Expression root, InputGetter inputGetter, BlockBuilder list, Map<? extends RexNode, Boolean> exprNullableMap, RexBuilder builder, + SqlConformance conformance, RexToLixTranslator parent, Function1<String, InputGetter> correlates) { - this.program = program; - this.typeFactory = typeFactory; - this.root = root; + this.program = program; // may be null + this.typeFactory = Objects.requireNonNull(typeFactory); + this.conformance = Objects.requireNonNull(conformance); + this.root = Objects.requireNonNull(root); this.inputGetter = inputGetter; - this.list = list; - this.exprNullableMap = exprNullableMap; - this.builder = builder; - this.parent = parent; - this.correlates = correlates; + this.list = Objects.requireNonNull(list); + this.exprNullableMap = Objects.requireNonNull(exprNullableMap); + this.builder = Objects.requireNonNull(builder); + this.parent = parent; // may be null + this.correlates = correlates; // may be null } /** @@ -165,6 +137,7 @@ public class RexToLixTranslator { * * @param program Program to be translated * @param typeFactory Type factory + * @param conformance SQL conformance * @param list List of statements, populated with declarations * @param outputPhysType Output type, or null * @param root Root expression @@ -174,12 +147,9 @@ public class RexToLixTranslator { * @return Sequence of expressions, optional condition */ public static List<Expression> translateProjects(RexProgram program, - JavaTypeFactory typeFactory, - BlockBuilder list, - PhysType outputPhysType, - Expression root, - InputGetter inputGetter, - Function1<String, InputGetter> correlates) { + JavaTypeFactory typeFactory, SqlConformance conformance, + BlockBuilder list, PhysType outputPhysType, Expression root, + InputGetter inputGetter, Function1<String, InputGetter> correlates) { List<Type> storageTypes = null; if (outputPhysType != null) { final RelDataType rowType = outputPhysType.getRowType(); @@ -188,16 +158,20 @@ public class RexToLixTranslator { storageTypes.add(outputPhysType.getJavaFieldType(i)); } } - return new RexToLixTranslator(program, typeFactory, root, inputGetter, list) + return new RexToLixTranslator(program, typeFactory, root, inputGetter, + list, Collections.emptyMap(), new RexBuilder(typeFactory), conformance, + null, null) .setCorrelates(correlates) .translateList(program.getProjectList(), storageTypes); } /** Creates a translator for translating aggregate functions. */ public static RexToLixTranslator forAggregation(JavaTypeFactory typeFactory, - BlockBuilder list, InputGetter inputGetter) { + BlockBuilder list, InputGetter inputGetter, SqlConformance conformance) { final ParameterExpression root = DataContext.ROOT; - return new RexToLixTranslator(null, typeFactory, root, inputGetter, list); + return new RexToLixTranslator(null, typeFactory, root, inputGetter, list, + Collections.emptyMap(), new RexBuilder(typeFactory), conformance, null, + null); } Expression translate(RexNode expr) { @@ -908,23 +882,23 @@ public class RexToLixTranslator { return list; } - public static Expression translateCondition( - RexProgram program, - JavaTypeFactory typeFactory, - BlockBuilder list, - InputGetter inputGetter, - Function1<String, InputGetter> correlates) { + public static Expression translateCondition(RexProgram program, + JavaTypeFactory typeFactory, BlockBuilder list, InputGetter inputGetter, + Function1<String, InputGetter> correlates, SqlConformance conformance) { if (program.getCondition() == null) { return RexImpTable.TRUE_EXPR; } final ParameterExpression root = DataContext.ROOT; RexToLixTranslator translator = - new RexToLixTranslator(program, typeFactory, root, inputGetter, list); + new RexToLixTranslator(program, typeFactory, root, inputGetter, list, + Collections.emptyMap(), new RexBuilder(typeFactory), conformance, + null, null); translator = translator.setCorrelates(correlates); return translator.translate( program.getCondition(), RexImpTable.NullAs.FALSE); } + public static Expression convert(Expression operand, Type toType) { final Type fromType = operand.getType(); return convert(operand, fromType, toType); @@ -1215,7 +1189,7 @@ public class RexToLixTranslator { return this; } return new RexToLixTranslator(program, typeFactory, root, inputGetter, list, - nullable, builder, this, correlates); + nullable, builder, conformance, this, correlates); } public RexToLixTranslator setBlock(BlockBuilder block) { @@ -1223,7 +1197,7 @@ public class RexToLixTranslator { return this; } return new RexToLixTranslator(program, typeFactory, root, inputGetter, - block, ImmutableMap.of(), builder, this, correlates); + block, ImmutableMap.of(), builder, conformance, this, correlates); } public RexToLixTranslator setCorrelates( @@ -1232,7 +1206,15 @@ public class RexToLixTranslator { return this; } return new RexToLixTranslator(program, typeFactory, root, inputGetter, list, - Collections.emptyMap(), builder, this, correlates); + Collections.emptyMap(), builder, conformance, this, correlates); + } + + private RexToLixTranslator withConformance(SqlConformance conformance) { + if (conformance == this.conformance) { + return this; + } + return new RexToLixTranslator(program, typeFactory, root, inputGetter, list, + Collections.emptyMap(), builder, conformance, this, correlates); } public RelDataType nullifyType(RelDataType type, boolean nullable) { http://git-wip-us.apache.org/repos/asf/calcite/blob/8f149454/core/src/main/java/org/apache/calcite/interpreter/AggregateNode.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/interpreter/AggregateNode.java b/core/src/main/java/org/apache/calcite/interpreter/AggregateNode.java index c547dfc..80d56f8 100644 --- a/core/src/main/java/org/apache/calcite/interpreter/AggregateNode.java +++ b/core/src/main/java/org/apache/calcite/interpreter/AggregateNode.java @@ -37,6 +37,8 @@ import org.apache.calcite.rex.RexInputRef; import org.apache.calcite.rex.RexNode; import org.apache.calcite.schema.impl.AggregateFunctionImpl; import org.apache.calcite.sql.fun.SqlStdOperatorTable; +import org.apache.calcite.sql.validate.SqlConformance; +import org.apache.calcite.sql.validate.SqlConformanceEnum; import org.apache.calcite.util.ImmutableBitSet; import org.apache.calcite.util.Pair; @@ -221,11 +223,14 @@ public class AggregateNode extends AbstractSingleNode<Aggregate> { } public RexToLixTranslator rowTranslator() { + final SqlConformance conformance = + SqlConformanceEnum.DEFAULT; // TODO: get this from implementor return RexToLixTranslator.forAggregation(typeFactory, currentBlock(), new RexToLixTranslator.InputGetterImpl( Collections.singletonList( - Pair.of((Expression) inParameter, inputPhysType)))) + Pair.of((Expression) inParameter, inputPhysType))), + conformance) .setNullable(currentNullables()); } }; http://git-wip-us.apache.org/repos/asf/calcite/blob/8f149454/core/src/main/java/org/apache/calcite/interpreter/JaninoRexCompiler.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/interpreter/JaninoRexCompiler.java b/core/src/main/java/org/apache/calcite/interpreter/JaninoRexCompiler.java index 2020636..5702667 100644 --- a/core/src/main/java/org/apache/calcite/interpreter/JaninoRexCompiler.java +++ b/core/src/main/java/org/apache/calcite/interpreter/JaninoRexCompiler.java @@ -34,6 +34,8 @@ import org.apache.calcite.rex.RexBuilder; import org.apache.calcite.rex.RexNode; import org.apache.calcite.rex.RexProgram; import org.apache.calcite.rex.RexProgramBuilder; +import org.apache.calcite.sql.validate.SqlConformance; +import org.apache.calcite.sql.validate.SqlConformanceEnum; import org.apache.calcite.util.BuiltInMethod; import org.apache.calcite.util.Pair; import org.apache.calcite.util.Util; @@ -93,9 +95,11 @@ public class JaninoRexCompiler implements Interpreter.ScalarCompiler { }; final Expression root = Expressions.field(context_, BuiltInMethod.CONTEXT_ROOT.field); + final SqlConformance conformance = + SqlConformanceEnum.DEFAULT; // TODO: get this from implementor final List<Expression> list = - RexToLixTranslator.translateProjects(program, javaTypeFactory, builder, - null, root, inputGetter, correlates); + RexToLixTranslator.translateProjects(program, javaTypeFactory, + conformance, builder, null, root, inputGetter, correlates); for (int i = 0; i < list.size(); i++) { builder.add( Expressions.statement( http://git-wip-us.apache.org/repos/asf/calcite/blob/8f149454/core/src/main/java/org/apache/calcite/jdbc/CalciteConnectionImpl.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/jdbc/CalciteConnectionImpl.java b/core/src/main/java/org/apache/calcite/jdbc/CalciteConnectionImpl.java index b7df1c6..7df8c3e 100644 --- a/core/src/main/java/org/apache/calcite/jdbc/CalciteConnectionImpl.java +++ b/core/src/main/java/org/apache/calcite/jdbc/CalciteConnectionImpl.java @@ -131,7 +131,6 @@ abstract class CalciteConnectionImpl return true; } }; - cfg.typeSystem(RelDataTypeSystem.class, RelDataTypeSystem.DEFAULT); } this.typeFactory = new JavaTypeFactoryImpl(typeSystem); } http://git-wip-us.apache.org/repos/asf/calcite/blob/8f149454/core/src/main/java/org/apache/calcite/plan/RelImplementor.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/plan/RelImplementor.java b/core/src/main/java/org/apache/calcite/plan/RelImplementor.java index 50b1cef..56157c3 100644 --- a/core/src/main/java/org/apache/calcite/plan/RelImplementor.java +++ b/core/src/main/java/org/apache/calcite/plan/RelImplementor.java @@ -17,6 +17,7 @@ package org.apache.calcite.plan; import org.apache.calcite.rel.RelNode; +import org.apache.calcite.sql.validate.SqlConformance; /** * This is a marker interface for a callback used to convert a tree of @@ -25,6 +26,8 @@ import org.apache.calcite.rel.RelNode; * tree, and correspondingly have their own implementors */ public interface RelImplementor { + /** Returns the desired SQL conformance. */ + SqlConformance getConformance(); } // End RelImplementor.java http://git-wip-us.apache.org/repos/asf/calcite/blob/8f149454/core/src/main/java/org/apache/calcite/prepare/CalcitePrepareImpl.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/prepare/CalcitePrepareImpl.java b/core/src/main/java/org/apache/calcite/prepare/CalcitePrepareImpl.java index b6023d0..55db902 100644 --- a/core/src/main/java/org/apache/calcite/prepare/CalcitePrepareImpl.java +++ b/core/src/main/java/org/apache/calcite/prepare/CalcitePrepareImpl.java @@ -1233,6 +1233,8 @@ public class CalcitePrepareImpl implements CalcitePrepare { try { CatalogReader.THREAD_LOCAL.set(catalogReader); + final SqlConformance conformance = context.config().conformance(); + internalParameters.put("_conformance", conformance); bindable = EnumerableInterpretable.toBindable(internalParameters, context.spark(), enumerable, prefer); } finally { http://git-wip-us.apache.org/repos/asf/calcite/blob/8f149454/core/src/main/java/org/apache/calcite/rel/type/RelDataTypeSystemImpl.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/rel/type/RelDataTypeSystemImpl.java b/core/src/main/java/org/apache/calcite/rel/type/RelDataTypeSystemImpl.java index 2e9399d..6131fb3 100644 --- a/core/src/main/java/org/apache/calcite/rel/type/RelDataTypeSystemImpl.java +++ b/core/src/main/java/org/apache/calcite/rel/type/RelDataTypeSystemImpl.java @@ -248,6 +248,10 @@ public abstract class RelDataTypeSystemImpl implements RelDataTypeSystem { return false; } + public boolean allowExtendedTrim() { + return false; + } + } // End RelDataTypeSystemImpl.java http://git-wip-us.apache.org/repos/asf/calcite/blob/8f149454/core/src/main/java/org/apache/calcite/rex/RexExecutorImpl.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/rex/RexExecutorImpl.java b/core/src/main/java/org/apache/calcite/rex/RexExecutorImpl.java index a5e0aab..407a2f8 100644 --- a/core/src/main/java/org/apache/calcite/rex/RexExecutorImpl.java +++ b/core/src/main/java/org/apache/calcite/rex/RexExecutorImpl.java @@ -31,6 +31,8 @@ import org.apache.calcite.linq4j.tree.ParameterExpression; import org.apache.calcite.prepare.CalcitePrepareImpl; import org.apache.calcite.rel.type.RelDataType; import org.apache.calcite.rel.type.RelDataTypeFactory; +import org.apache.calcite.sql.validate.SqlConformance; +import org.apache.calcite.sql.validate.SqlConformanceEnum; import org.apache.calcite.util.BuiltInMethod; import org.apache.calcite.util.Util; @@ -76,9 +78,11 @@ public class RexExecutorImpl implements RexExecutor { Expressions.declare( Modifier.FINAL, root_, Expressions.convert_(root0_, DataContext.class))); + final SqlConformance conformance = SqlConformanceEnum.DEFAULT; + final RexProgram program = programBuilder.getProgram(); final List<Expression> expressions = - RexToLixTranslator.translateProjects(programBuilder.getProgram(), - javaTypeFactory, blockBuilder, null, root_, getter, null); + RexToLixTranslator.translateProjects(program, javaTypeFactory, + conformance, blockBuilder, null, root_, getter, null); blockBuilder.add( Expressions.return_(null, Expressions.newArrayInit(Object[].class, expressions))); http://git-wip-us.apache.org/repos/asf/calcite/blob/8f149454/core/src/main/java/org/apache/calcite/runtime/SqlFunctions.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/runtime/SqlFunctions.java b/core/src/main/java/org/apache/calcite/runtime/SqlFunctions.java index 8062afe..6754eab 100644 --- a/core/src/main/java/org/apache/calcite/runtime/SqlFunctions.java +++ b/core/src/main/java/org/apache/calcite/runtime/SqlFunctions.java @@ -217,29 +217,32 @@ public class SqlFunctions { /** SQL {@code RTRIM} function applied to string. */ public static String rtrim(String s) { - return trim_(s, false, true, ' '); + return trim(false, true, " ", s); } /** SQL {@code LTRIM} function. */ public static String ltrim(String s) { - return trim_(s, true, false, ' '); + return trim(true, false, " ", s); } /** SQL {@code TRIM(... seek FROM s)} function. */ - public static String trim(boolean leading, boolean trailing, String seek, + public static String trim(boolean left, boolean right, String seek, String s) { - return trim_(s, leading, trailing, seek.charAt(0)); + return trim(left, right, seek, s, true); } - /** SQL {@code TRIM} function. */ - private static String trim_(String s, boolean left, boolean right, char c) { + public static String trim(boolean left, boolean right, String seek, + String s, boolean strict) { + if (strict && seek.length() != 1) { + throw new IllegalArgumentException("trim error: trim character must be exactly 1 character"); + } int j = s.length(); if (right) { for (;;) { if (j == 0) { return ""; } - if (s.charAt(j - 1) != c) { + if (seek.indexOf(s.charAt(j - 1)) < 0) { break; } --j; @@ -251,7 +254,7 @@ public class SqlFunctions { if (i == j) { return ""; } - if (s.charAt(i) != c) { + if (seek.indexOf(s.charAt(i)) < 0) { break; } ++i; @@ -1497,7 +1500,7 @@ public class SqlFunctions { /** CAST(VARCHAR AS BOOLEAN). */ public static boolean toBoolean(String s) { - s = trim_(s, true, true, ' '); + s = trim(true, true, " ", s); if (s.equalsIgnoreCase("TRUE")) { return true; } else if (s.equalsIgnoreCase("FALSE")) { http://git-wip-us.apache.org/repos/asf/calcite/blob/8f149454/core/src/main/java/org/apache/calcite/sql/validate/SqlAbstractConformance.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/sql/validate/SqlAbstractConformance.java b/core/src/main/java/org/apache/calcite/sql/validate/SqlAbstractConformance.java index 059127b..f676419 100644 --- a/core/src/main/java/org/apache/calcite/sql/validate/SqlAbstractConformance.java +++ b/core/src/main/java/org/apache/calcite/sql/validate/SqlAbstractConformance.java @@ -98,6 +98,10 @@ public abstract class SqlAbstractConformance implements SqlConformance { public boolean shouldConvertRaggedUnionTypesToVarying() { return SqlConformanceEnum.DEFAULT.shouldConvertRaggedUnionTypesToVarying(); } + + public boolean allowExtendedTrim() { + return SqlConformanceEnum.DEFAULT.allowExtendedTrim(); + } } // End SqlAbstractConformance.java http://git-wip-us.apache.org/repos/asf/calcite/blob/8f149454/core/src/main/java/org/apache/calcite/sql/validate/SqlConformance.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/sql/validate/SqlConformance.java b/core/src/main/java/org/apache/calcite/sql/validate/SqlConformance.java index 9fa0f5e..644886f 100644 --- a/core/src/main/java/org/apache/calcite/sql/validate/SqlConformance.java +++ b/core/src/main/java/org/apache/calcite/sql/validate/SqlConformance.java @@ -372,6 +372,27 @@ public interface SqlConformance { * false otherwise. */ boolean shouldConvertRaggedUnionTypesToVarying(); + + /** + * Whether TRIM should support more than one trim character. + * + * <p>For example, consider the query + * + * <blockquote><pre>SELECT TRIM('eh' FROM 'hehe__hehe')</pre></blockquote> + * + * <p>Under strict behavior, if the length of trim character is not 1, + * TRIM throws an exception, and the query fails. + * However many implementations (in databases such as MySQL and SQL Server) + * trim all the characters, resulting in a return value of '__'. + * + * <p>Among the built-in conformance levels, true in + * {@link SqlConformanceEnum#BABEL}, + * {@link SqlConformanceEnum#LENIENT}, + * {@link SqlConformanceEnum#MYSQL_5}, + * {@link SqlConformanceEnum#SQL_SERVER_2008}; + * false otherwise. + */ + boolean allowExtendedTrim(); } // End SqlConformance.java http://git-wip-us.apache.org/repos/asf/calcite/blob/8f149454/core/src/main/java/org/apache/calcite/sql/validate/SqlConformanceEnum.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/sql/validate/SqlConformanceEnum.java b/core/src/main/java/org/apache/calcite/sql/validate/SqlConformanceEnum.java index 192e58b..5d9c44e 100644 --- a/core/src/main/java/org/apache/calcite/sql/validate/SqlConformanceEnum.java +++ b/core/src/main/java/org/apache/calcite/sql/validate/SqlConformanceEnum.java @@ -291,6 +291,19 @@ public enum SqlConformanceEnum implements SqlConformance { return false; } } + + public boolean allowExtendedTrim() { + switch (this) { + case BABEL: + case LENIENT: + case MYSQL_5: + case SQL_SERVER_2008: + return true; + default: + return false; + } + } + } // End SqlConformanceEnum.java http://git-wip-us.apache.org/repos/asf/calcite/blob/8f149454/core/src/main/java/org/apache/calcite/util/BuiltInMethod.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/util/BuiltInMethod.java b/core/src/main/java/org/apache/calcite/util/BuiltInMethod.java index 97359ea..45db5e7 100644 --- a/core/src/main/java/org/apache/calcite/util/BuiltInMethod.java +++ b/core/src/main/java/org/apache/calcite/util/BuiltInMethod.java @@ -275,7 +275,7 @@ public enum BuiltInMethod { TRUNCATE(SqlFunctions.class, "truncate", String.class, int.class), TRUNCATE_OR_PAD(SqlFunctions.class, "truncateOrPad", String.class, int.class), TRIM(SqlFunctions.class, "trim", boolean.class, boolean.class, String.class, - String.class), + String.class, boolean.class), REPLACE(SqlFunctions.class, "replace", String.class, String.class, String.class), TRANSLATE3(SqlFunctions.class, "translate3", String.class, String.class, String.class), http://git-wip-us.apache.org/repos/asf/calcite/blob/8f149454/core/src/test/java/org/apache/calcite/sql/test/SqlOperatorBaseTest.java ---------------------------------------------------------------------- diff --git a/core/src/test/java/org/apache/calcite/sql/test/SqlOperatorBaseTest.java b/core/src/test/java/org/apache/calcite/sql/test/SqlOperatorBaseTest.java index c892f94..4484762 100644 --- a/core/src/test/java/org/apache/calcite/sql/test/SqlOperatorBaseTest.java +++ b/core/src/test/java/org/apache/calcite/sql/test/SqlOperatorBaseTest.java @@ -5301,29 +5301,29 @@ public abstract class SqlOperatorBaseTest { tester.checkNull("trim(cast(null as varchar(1)) from 'a')"); tester.checkNull("trim('a' from cast(null as varchar(1)))"); - if (Bug.FNL3_FIXED) { - // SQL:2003 6.29.9: trim string must have length=1. Failure occurs - // at runtime. - // - // TODO: Change message to "Invalid argument\(s\) for - // 'TRIM' function". - // The message should come from a resource file, and should still - // have the SQL error code 22027. - tester.checkFails( - "trim('xy' from 'abcde')", - "could not calculate results for the following row:\n" - + "\\[ 0 \\]\n" - + "Messages:\n" - + "\\[0\\]:PC=0 Code=22027 ", - true); - tester.checkFails( - "trim('' from 'abcde')", - "could not calculate results for the following row:\n" - + "\\[ 0 \\]\n" - + "Messages:\n" - + "\\[0\\]:PC=0 Code=22027 ", - true); - } + // SQL:2003 6.29.9: trim string must have length=1. Failure occurs + // at runtime. + // + // TODO: Change message to "Invalid argument\(s\) for + // 'TRIM' function". + // The message should come from a resource file, and should still + // have the SQL error code 22027. + tester.checkFails( + "trim('xy' from 'abcde')", + "trim error: trim character must be exactly 1 character", + true); + tester.checkFails( + "trim('' from 'abcde')", + "trim error: trim character must be exactly 1 character", + true); + + final SqlTester tester1 = tester.withConformance(SqlConformanceEnum.MYSQL_5); + tester1.checkString( + "trim(leading 'eh' from 'hehe__hehe')", "__hehe", "VARCHAR(10) NOT NULL"); + tester1.checkString( + "trim(trailing 'eh' from 'hehe__hehe')", "hehe__", "VARCHAR(10) NOT NULL"); + tester1.checkString( + "trim('eh' from 'hehe__hehe')", "__", "VARCHAR(10) NOT NULL"); } @Test public void testRtrimFunc() { http://git-wip-us.apache.org/repos/asf/calcite/blob/8f149454/core/src/test/java/org/apache/calcite/sql/test/SqlTestFactory.java ---------------------------------------------------------------------- diff --git a/core/src/test/java/org/apache/calcite/sql/test/SqlTestFactory.java b/core/src/test/java/org/apache/calcite/sql/test/SqlTestFactory.java index c3e94cf..a1f1ad1 100644 --- a/core/src/test/java/org/apache/calcite/sql/test/SqlTestFactory.java +++ b/core/src/test/java/org/apache/calcite/sql/test/SqlTestFactory.java @@ -178,6 +178,13 @@ public class SqlTestFactory { } }; } + if (conformance.allowExtendedTrim()) { + typeSystem = new DelegatingTypeSystem(typeSystem) { + public boolean allowExtendedTrim() { + return true; + } + }; + } return new JavaTypeFactoryImpl(typeSystem); } http://git-wip-us.apache.org/repos/asf/calcite/blob/8f149454/core/src/test/java/org/apache/calcite/test/JdbcTest.java ---------------------------------------------------------------------- diff --git a/core/src/test/java/org/apache/calcite/test/JdbcTest.java b/core/src/test/java/org/apache/calcite/test/JdbcTest.java index d3e3a66..08414be 100644 --- a/core/src/test/java/org/apache/calcite/test/JdbcTest.java +++ b/core/src/test/java/org/apache/calcite/test/JdbcTest.java @@ -2428,7 +2428,7 @@ public class JdbcTest { + ": org.apache.calcite.runtime.SqlFunctions.substring(" + "org.apache.calcite.runtime.SqlFunctions.trim(true, true, \" \", " + "org.apache.calcite.runtime.SqlFunctions.substring(inp2_, " - + "inp1_ * 0 + 1)), (v5 ? 4 : 5) - 2);") + + "inp1_ * 0 + 1), true), (v5 ? 4 : 5) - 2);") .returns("T=ill\n" + "T=ric\n" + "T=ebastian\n" @@ -2465,7 +2465,7 @@ public class JdbcTest { + ": org.apache.calcite.runtime.SqlFunctions.substring(" + "org.apache.calcite.runtime.SqlFunctions.trim(true, true, \" \", " + "org.apache.calcite.runtime.SqlFunctions.substring(inp2_, " - + "inp1_ * 0 + 1)), $L4J$C$5_2);") + + "inp1_ * 0 + 1), true), $L4J$C$5_2);") .returns("T=ll\n" + "T=ic\n" + "T=bastian\n" http://git-wip-us.apache.org/repos/asf/calcite/blob/8f149454/spark/src/main/java/org/apache/calcite/adapter/spark/SparkRules.java ---------------------------------------------------------------------- diff --git a/spark/src/main/java/org/apache/calcite/adapter/spark/SparkRules.java b/spark/src/main/java/org/apache/calcite/adapter/spark/SparkRules.java index 53cb7b2..3ae350a 100644 --- a/spark/src/main/java/org/apache/calcite/adapter/spark/SparkRules.java +++ b/spark/src/main/java/org/apache/calcite/adapter/spark/SparkRules.java @@ -53,6 +53,8 @@ import org.apache.calcite.rel.type.RelDataTypeField; import org.apache.calcite.rex.RexLiteral; import org.apache.calcite.rex.RexMultisetUtil; import org.apache.calcite.rex.RexProgram; +import org.apache.calcite.sql.validate.SqlConformance; +import org.apache.calcite.sql.validate.SqlConformanceEnum; import org.apache.calcite.util.BuiltInMethod; import org.apache.calcite.util.Pair; import org.apache.calcite.util.Util; @@ -346,7 +348,7 @@ public abstract class SparkRules { new RexToLixTranslator.InputGetterImpl( Collections.singletonList( Pair.of((Expression) e_, result.physType))), - null); + null, implementor.getConformance()); builder2.add( Expressions.ifThen( Expressions.not(condition), @@ -355,10 +357,12 @@ public abstract class SparkRules { BuiltInMethod.COLLECTIONS_EMPTY_LIST.method)))); } + final SqlConformance conformance = SqlConformanceEnum.DEFAULT; List<Expression> expressions = RexToLixTranslator.translateProjects( program, typeFactory, + conformance, builder2, null, DataContext.ROOT, http://git-wip-us.apache.org/repos/asf/calcite/blob/8f149454/spark/src/main/java/org/apache/calcite/adapter/spark/SparkToEnumerableConverter.java ---------------------------------------------------------------------- diff --git a/spark/src/main/java/org/apache/calcite/adapter/spark/SparkToEnumerableConverter.java b/spark/src/main/java/org/apache/calcite/adapter/spark/SparkToEnumerableConverter.java index 8d74df5..620b67c 100644 --- a/spark/src/main/java/org/apache/calcite/adapter/spark/SparkToEnumerableConverter.java +++ b/spark/src/main/java/org/apache/calcite/adapter/spark/SparkToEnumerableConverter.java @@ -34,6 +34,7 @@ import org.apache.calcite.plan.RelTraitSet; import org.apache.calcite.rel.RelNode; import org.apache.calcite.rel.convert.ConverterImpl; import org.apache.calcite.rel.metadata.RelMetadataQuery; +import org.apache.calcite.sql.validate.SqlConformance; import java.util.List; @@ -115,6 +116,10 @@ public class SparkToEnumerableConverter public JavaTypeFactory getTypeFactory() { return implementor.getTypeFactory(); } + + public SqlConformance getConformance() { + return implementor.getConformance(); + } } }
