Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

IGNITE-19130 Sql. Align RexImpTable implementation #2032

Closed
wants to merge 9 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@

package org.apache.ignite.internal.sql.engine.exec;

import static org.apache.ignite.internal.sql.engine.exec.exp.ExpressionFactoryImpl.UNSPECIFIED_VALUE_PLACEHOLDER;

import java.nio.ByteBuffer;
import java.util.List;
import org.apache.ignite.internal.binarytuple.BinaryTupleBuilder;
Expand All @@ -27,7 +29,6 @@
import org.apache.ignite.internal.schema.BinaryTupleSchema;
import org.apache.ignite.internal.schema.BinaryTupleSchema.Element;
import org.apache.ignite.internal.schema.NativeTypeSpec;
import org.apache.ignite.internal.sql.engine.exec.exp.RexImpTable;
import org.apache.ignite.internal.sql.engine.schema.TableDescriptor;
import org.apache.ignite.internal.sql.engine.util.TypeUtils;
import org.apache.ignite.internal.util.IgniteUtils;
Expand Down Expand Up @@ -73,7 +74,7 @@ public static <RowT> BinaryTuplePrefix toBinaryTuplePrefix(

int specifiedCols = 0;
for (int i = 0; i < prefixColumnsCount; i++) {
if (handler.get(i, searchRow) == RexImpTable.UNSPECIFIED_VALUE_PLACEHOLDER) {
if (handler.get(i, searchRow) == UNSPECIFIED_VALUE_PLACEHOLDER) {
break;
}

Expand Down Expand Up @@ -109,7 +110,7 @@ public static <RowT> BinaryTuple toBinaryTuple(

if (IgniteUtils.assertionsEnabled()) {
for (int i = 0; i < rowColumnsCount; i++) {
if (handler.get(i, searchRow) == RexImpTable.UNSPECIFIED_VALUE_PLACEHOLDER) {
if (handler.get(i, searchRow) == UNSPECIFIED_VALUE_PLACEHOLDER) {
throw new AssertionError("Invalid lookup key.");
}
}
Expand All @@ -132,7 +133,7 @@ private static <RowT> ByteBuffer toByteBuffer(
for (int i = 0; i < columnsCount; i++) {
Object val = handler.get(i, searchRow);

if (val == RexImpTable.UNSPECIFIED_VALUE_PLACEHOLDER) {
if (val == UNSPECIFIED_VALUE_PLACEHOLDER) {
break; // No more columns in prefix.
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,18 +21,15 @@
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import org.apache.calcite.adapter.enumerable.EnumUtils;
import org.apache.calcite.adapter.enumerable.RexImpTable;
import org.apache.calcite.linq4j.tree.ConstantExpression;
import org.apache.calcite.linq4j.tree.ConstantUntypedNull;
import org.apache.calcite.linq4j.tree.Expression;
import org.apache.calcite.linq4j.tree.ExpressionType;
import org.apache.calcite.linq4j.tree.Expressions;
import org.apache.calcite.linq4j.tree.Primitive;
import org.apache.calcite.linq4j.tree.Types;
import org.apache.calcite.linq4j.tree.UnaryExpression;
import org.apache.calcite.rel.type.RelDataType;
import org.apache.calcite.rex.RexNode;
import org.apache.calcite.runtime.SqlFunctions;
import org.apache.calcite.sql.type.SqlTypeName;
import org.apache.calcite.util.BuiltInMethod;
import org.apache.calcite.util.Util;
Expand Down Expand Up @@ -221,144 +218,10 @@ public static Expression convert(Expression operand, Type fromType, Type toType)
if (toType == BigDecimal.class) {
throw new AssertionError("For conversion to decimal, ConverterUtils#convertToDecimal method should be used instead.");
}
// E.g. from "Short" to "int".
// Generate "x.intValue()".
final Primitive toPrimitive = Primitive.of(toType);
final Primitive toBox = Primitive.ofBox(toType);
final Primitive fromBox = Primitive.ofBox(fromType);
final Primitive fromPrimitive = Primitive.of(fromType);
final boolean fromNumber = fromType instanceof Class
&& Number.class.isAssignableFrom((Class) fromType);
if (fromType == String.class) {
if (toPrimitive != null) {
switch (toPrimitive) {
case CHAR:
case SHORT:
case INT:
case LONG:
case FLOAT:
case DOUBLE:
// Generate "SqlFunctions.toShort(x)".
return Expressions.call(
SqlFunctions.class,
"to" + SqlFunctions.initcap(toPrimitive.primitiveName),
operand);
default:
// Generate "Short.parseShort(x)".
return Expressions.call(
toPrimitive.boxClass,
"parse" + SqlFunctions.initcap(toPrimitive.primitiveName),
operand);
}
}
if (toBox != null) {
switch (toBox) {
case CHAR:
// Generate "SqlFunctions.toCharBoxed(x)".
return Expressions.call(
SqlFunctions.class,
"to" + SqlFunctions.initcap(toBox.primitiveName) + "Boxed",
operand);
default:
// Generate "Short.valueOf(x)".
return Expressions.call(
toBox.boxClass,
"valueOf",
operand);
}
}
}
if (toPrimitive != null) {
if (fromPrimitive != null) {
// E.g. from "float" to "double"
return Expressions.convert_(
operand, toPrimitive.primitiveClass);
}
if (fromNumber || fromBox == Primitive.CHAR) {
// Generate "x.shortValue()".
return Expressions.unbox(operand, toPrimitive);
} else {
// E.g. from "Object" to "short".
// Generate "SqlFunctions.toShort(x)"
return Expressions.call(
SqlFunctions.class,
"to" + SqlFunctions.initcap(toPrimitive.primitiveName),
operand);
}
} else if (fromNumber && toBox != null) {
// E.g. from "Short" to "Integer"
// Generate "x == null ? null : Integer.valueOf(x.intValue())"
return Expressions.condition(
Expressions.equal(operand, RexImpTable.NULL_EXPR),
RexImpTable.NULL_EXPR,
Expressions.box(
Expressions.unbox(operand, toBox),
toBox));
} else if (fromPrimitive != null && toBox != null) {
// E.g. from "int" to "Long".
// Generate Long.valueOf(x)
// Eliminate primitive casts like Long.valueOf((long) x)
if (operand instanceof UnaryExpression) {
UnaryExpression una = (UnaryExpression) operand;
if (una.nodeType == ExpressionType.Convert
&& Primitive.of(una.getType()) == toBox) {
Primitive origin = Primitive.of(una.expression.type);
if (origin != null && toBox.assignableFrom(origin)) {
return Expressions.box(una.expression, toBox);
}
}
}
if (fromType == toBox.primitiveClass) {
return Expressions.box(operand, toBox);
}
// E.g., from "int" to "Byte".
// Convert it first and generate "Byte.valueOf((byte)x)"
// Because there is no method "Byte.valueOf(int)" in Byte
return Expressions.box(
Expressions.convert_(operand, toBox.primitiveClass),
toBox);
}
// Convert datetime types to internal storage type:
// 1. java.sql.Date -> int or Integer
// 2. java.sql.Time -> int or Integer
// 3. java.sql.Timestamp -> long or Long
if (representAsInternalType(fromType)) {
final Expression internalTypedOperand =
toInternal(operand, fromType, toType);
if (operand != internalTypedOperand) {
return internalTypedOperand;
}
}
// Convert internal storage type to datetime types:
// 1. int or Integer -> java.sql.Date
// 2. int or Integer -> java.sql.Time
// 3. long or Long -> java.sql.Timestamp
if (representAsInternalType(toType)) {
final Expression originTypedOperand =
fromInternal(operand, fromType, toType);
if (operand != originTypedOperand) {
return originTypedOperand;
}
} else if (toType == String.class) {
if (fromPrimitive != null) {
switch (fromPrimitive) {
case DOUBLE:
case FLOAT:
// E.g. from "double" to "String"
// Generate "SqlFunctions.toString(x)"
return Expressions.call(
SqlFunctions.class,
"toString",
operand);
default:
// E.g. from "int" to "String"
// Generate "Integer.toString(x)"
return Expressions.call(
fromPrimitive.boxClass,
"toString",
operand);
}
} else if (fromType == BigDecimal.class) {

// SELECT '0.1'::DECIMAL::VARCHAR case, looks like a stub
if (toType == String.class) {
if (fromType == BigDecimal.class) {
// E.g. from "BigDecimal" to "String"
// Generate "SqlFunctions.toString(x)"
return Expressions.condition(
Expand All @@ -368,41 +231,11 @@ public static Expression convert(Expression operand, Type fromType, Type toType)
IgniteSqlFunctions.class,
"toString",
operand));
} else {
Expression result;
try {
// Avoid to generate code like:
// "null.toString()" or "(xxx) null.toString()"
if (operand instanceof ConstantExpression) {
ConstantExpression ce = (ConstantExpression) operand;
if (ce.value == null) {
return Expressions.convert_(operand, toType);
}
}
// Try to call "toString()" method
// E.g. from "Integer" to "String"
// Generate "x == null ? null : x.toString()"
result = Expressions.condition(
Expressions.equal(operand, RexImpTable.NULL_EXPR),
RexImpTable.NULL_EXPR,
Expressions.call(operand, "toString"));
} catch (RuntimeException e) {
// For some special cases, e.g., "BuiltInMethod.LESSER",
// its return type is generic ("Comparable"), which contains
// no "toString()" method. We fall through to "(String)x".
return Expressions.convert_(operand, toType);
}
return result;
}
}

// IgniteCustomType: call runtime conversion routines.
var toCustomType = CustomTypesConversion.INSTANCE.tryConvert(operand, toType);
if (toCustomType != null) {
return toCustomType;
} else {
return Expressions.convert_(operand, toType);
}
return toCustomType != null ? toCustomType : EnumUtils.convert(operand, toType);
}

private static boolean isA(Type fromType, Primitive primitive) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,20 @@
public class ExpressionFactoryImpl<RowT> implements ExpressionFactory<RowT> {
private static final int CACHE_SIZE = 1024;

/** Placeholder for DEFAULT operator value. */
// TODO Remove this constant when https://issues.apache.org/jira/browse/IGNITE-19096 is complete
public static final Object DEFAULT_VALUE_PLACEHOLDER = Placeholder.DEFAULT_VALUE;

/** Placeholder for values, which expressions are not specified. */
public static final Object UNSPECIFIED_VALUE_PLACEHOLDER = Placeholder.UNSPECIFIED_VALUE;

// We use enums for placeholders because enum serialization/deserialization guarantees to preserve object's identity.
private enum Placeholder {
// TODO Remove this enum element when https://issues.apache.org/jira/browse/IGNITE-19096 is complete
DEFAULT_VALUE,
UNSPECIFIED_VALUE
}

private static final ConcurrentMap<String, Scalar> SCALAR_CACHE = Caffeine.newBuilder()
.maximumSize(CACHE_SIZE)
.<String, Scalar>build()
Expand Down Expand Up @@ -143,7 +157,7 @@ public Comparator<RowT> comparator(RelCollation collation) {

return (o1, o2) -> {
RowHandler<RowT> hnd = ctx.rowHandler();
Object unspecifiedVal = RexImpTable.UNSPECIFIED_VALUE_PLACEHOLDER;
Object unspecifiedVal = UNSPECIFIED_VALUE_PLACEHOLDER;

for (RelFieldCollation field : collation.getFieldCollations()) {
int fieldIdx = field.getFieldIndex();
Expand Down Expand Up @@ -425,7 +439,7 @@ private SingleScalar scalar(RexNode node, RelDataType type) {
/**
* Creates {@link SingleScalar}, a code-generated expressions evaluator.
*
* @param nodes Expressions. {@code Null} expressions will be evaluated to {@link RexImpTable#UNSPECIFIED_VALUE_PLACEHOLDER}.
* @param nodes Expressions. {@code Null} expressions will be evaluated to {@link ExpressionFactoryImpl#UNSPECIFIED_VALUE_PLACEHOLDER}.
* @param type Row type.
* @return SingleScalar.
*/
Expand Down Expand Up @@ -498,14 +512,14 @@ private Scalar compile(List<RexNode> nodes, RelDataType type, boolean biInParams

Function1<String, InputGetter> correlates = new CorrelatesBuilder(builder, ctx, hnd).build(nodes);

List<Expression> projects = RexToLixTranslator.translateProjects(program, typeFactory, rexBuilder, conformance,
builder, null, ctx, inputGetter, correlates);
List<Expression> projects = RexToLixTranslator.translateProjects(program, typeFactory, conformance,
builder, null, null, ctx, inputGetter, correlates);

assert nodes.size() == projects.size();

for (int i = 0; i < projects.size(); i++) {
Expression val = unspecifiedValues.get(i)
? Expressions.field(null, RexImpTable.class, "UNSPECIFIED_VALUE_PLACEHOLDER")
? Expressions.field(null, ExpressionFactoryImpl.class, "UNSPECIFIED_VALUE_PLACEHOLDER")
: projects.get(i);

builder.add(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.apache.ignite.internal.sql.engine.exec.exp;

import org.apache.calcite.linq4j.tree.Expression;
import org.apache.calcite.linq4j.tree.ParameterExpression;
import org.apache.calcite.rex.RexCall;

/** Implementor of Functions used in MATCH_RECOGNIZE Context. */
public interface MatchImplementor {

/**
* Implements a call.
*
* @param translator Translator for the call
* @param call Call that should be implemented
* @param row Current Row
* @param rows All Rows that are traversed so far
* @param symbols All Symbols of the rows that were traversed so far
* @return Translated call
*/
Expression implement(
RexToLixTranslator translator,
RexCall call,
ParameterExpression row,
ParameterExpression rows,
ParameterExpression symbols,
ParameterExpression currentIndex);

}
Original file line number Diff line number Diff line change
Expand Up @@ -100,8 +100,8 @@ private static String compile(
final RexProgram program = programBuilder.getProgram();

final List<Expression> expressions =
RexToLixTranslator.translateProjects(program, javaTypeFactory, rexBuilder,
conformance, blockBuilder, null, root_, getter, null);
RexToLixTranslator.translateProjects(program, javaTypeFactory,
conformance, blockBuilder, null, null, root_, getter, null);

blockBuilder.add(
Expressions.return_(
Expand Down
Loading