All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.hazelcast.org.apache.calcite.adapter.enumerable.RexImpTable Maven / Gradle / Ivy

There is a newer version: 5.4.0
Show newest version
/*
 * 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 com.hazelcast.com.liance with
 * the License.  You may obtain a copy of the License at
 *
 * http://www.apache.com.hazelcast.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 com.hazelcast.org.apache.calcite.adapter.enumerable;

import com.hazelcast.org.apache.calcite.avatica.util.DateTimeUtils;
import com.hazelcast.org.apache.calcite.avatica.util.TimeUnit;
import com.hazelcast.org.apache.calcite.avatica.util.TimeUnitRange;
import com.hazelcast.org.apache.calcite.linq4j.Ord;
import com.hazelcast.org.apache.calcite.linq4j.tree.BinaryExpression;
import com.hazelcast.org.apache.calcite.linq4j.tree.BlockBuilder;
import com.hazelcast.org.apache.calcite.linq4j.tree.BlockStatement;
import com.hazelcast.org.apache.calcite.linq4j.tree.ConstantExpression;
import com.hazelcast.org.apache.calcite.linq4j.tree.Expression;
import com.hazelcast.org.apache.calcite.linq4j.tree.ExpressionType;
import com.hazelcast.org.apache.calcite.linq4j.tree.Expressions;
import com.hazelcast.org.apache.calcite.linq4j.tree.MemberExpression;
import com.hazelcast.org.apache.calcite.linq4j.tree.MethodCallExpression;
import com.hazelcast.org.apache.calcite.linq4j.tree.OptimizeShuttle;
import com.hazelcast.org.apache.calcite.linq4j.tree.ParameterExpression;
import com.hazelcast.org.apache.calcite.linq4j.tree.Primitive;
import com.hazelcast.org.apache.calcite.linq4j.tree.UnaryExpression;
import com.hazelcast.org.apache.calcite.rel.type.RelDataType;
import com.hazelcast.org.apache.calcite.rel.type.RelDataTypeFactory;
import com.hazelcast.org.apache.calcite.rel.type.RelDataTypeFactoryImpl;
import com.hazelcast.org.apache.calcite.rex.RexCall;
import com.hazelcast.org.apache.calcite.rex.RexInputRef;
import com.hazelcast.org.apache.calcite.rex.RexLiteral;
import com.hazelcast.org.apache.calcite.rex.RexNode;
import com.hazelcast.org.apache.calcite.rex.RexPatternFieldRef;
import com.hazelcast.org.apache.calcite.runtime.SqlFunctions;
import com.hazelcast.org.apache.calcite.schema.ImplementableAggFunction;
import com.hazelcast.org.apache.calcite.schema.ImplementableFunction;
import com.hazelcast.org.apache.calcite.schema.impl.AggregateFunctionImpl;
import com.hazelcast.org.apache.calcite.sql.SqlAggFunction;
import com.hazelcast.org.apache.calcite.sql.SqlBinaryOperator;
import com.hazelcast.org.apache.calcite.sql.SqlJsonConstructorNullClause;
import com.hazelcast.org.apache.calcite.sql.SqlJsonEmptyOrError;
import com.hazelcast.org.apache.calcite.sql.SqlJsonValueEmptyOrErrorBehavior;
import com.hazelcast.org.apache.calcite.sql.SqlMatchFunction;
import com.hazelcast.org.apache.calcite.sql.SqlOperator;
import com.hazelcast.org.apache.calcite.sql.SqlTypeConstructorFunction;
import com.hazelcast.org.apache.calcite.sql.SqlWindowTableFunction;
import com.hazelcast.org.apache.calcite.sql.fun.SqlJsonArrayAggAggFunction;
import com.hazelcast.org.apache.calcite.sql.fun.SqlJsonObjectAggAggFunction;
import com.hazelcast.org.apache.calcite.sql.fun.SqlStdOperatorTable;
import com.hazelcast.org.apache.calcite.sql.fun.SqlTrimFunction;
import com.hazelcast.org.apache.calcite.sql.type.SqlTypeName;
import com.hazelcast.org.apache.calcite.sql.type.SqlTypeUtil;
import com.hazelcast.org.apache.calcite.sql.validate.SqlUserDefinedAggFunction;
import com.hazelcast.org.apache.calcite.sql.validate.SqlUserDefinedFunction;
import com.hazelcast.org.apache.calcite.util.BuiltInMethod;
import com.hazelcast.org.apache.calcite.util.Util;

import com.hazelcast.com.google.com.hazelcast.com.on.collect.ImmutableList;
import com.hazelcast.com.google.com.hazelcast.com.on.collect.Iterables;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Type;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Supplier;

import static com.hazelcast.org.apache.calcite.linq4j.tree.ExpressionType.Add;
import static com.hazelcast.org.apache.calcite.linq4j.tree.ExpressionType.AndAlso;
import static com.hazelcast.org.apache.calcite.linq4j.tree.ExpressionType.Divide;
import static com.hazelcast.org.apache.calcite.linq4j.tree.ExpressionType.Equal;
import static com.hazelcast.org.apache.calcite.linq4j.tree.ExpressionType.GreaterThan;
import static com.hazelcast.org.apache.calcite.linq4j.tree.ExpressionType.GreaterThanOrEqual;
import static com.hazelcast.org.apache.calcite.linq4j.tree.ExpressionType.LessThan;
import static com.hazelcast.org.apache.calcite.linq4j.tree.ExpressionType.LessThanOrEqual;
import static com.hazelcast.org.apache.calcite.linq4j.tree.ExpressionType.Multiply;
import static com.hazelcast.org.apache.calcite.linq4j.tree.ExpressionType.Negate;
import static com.hazelcast.org.apache.calcite.linq4j.tree.ExpressionType.Not;
import static com.hazelcast.org.apache.calcite.linq4j.tree.ExpressionType.NotEqual;
import static com.hazelcast.org.apache.calcite.linq4j.tree.ExpressionType.OrElse;
import static com.hazelcast.org.apache.calcite.linq4j.tree.ExpressionType.Subtract;
import static com.hazelcast.org.apache.calcite.linq4j.tree.ExpressionType.UnaryPlus;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlLibraryOperators.CHR;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlLibraryOperators.COMPRESS;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlLibraryOperators.CONCAT2;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlLibraryOperators.CONCAT_FUNCTION;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlLibraryOperators.COSH;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlLibraryOperators.DAYNAME;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlLibraryOperators.DIFFERENCE;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlLibraryOperators.EXISTS_NODE;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlLibraryOperators.EXTRACT_VALUE;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlLibraryOperators.EXTRACT_XML;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlLibraryOperators.FROM_BASE64;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlLibraryOperators.JSON_DEPTH;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlLibraryOperators.JSON_KEYS;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlLibraryOperators.JSON_LENGTH;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlLibraryOperators.JSON_PRETTY;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlLibraryOperators.JSON_REMOVE;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlLibraryOperators.JSON_STORAGE_SIZE;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlLibraryOperators.JSON_TYPE;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlLibraryOperators.LEFT;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlLibraryOperators.MD5;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlLibraryOperators.MONTHNAME;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlLibraryOperators.REGEXP_REPLACE;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlLibraryOperators.REPEAT;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlLibraryOperators.REVERSE;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlLibraryOperators.RIGHT;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlLibraryOperators.SHA1;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlLibraryOperators.SINH;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlLibraryOperators.SOUNDEX;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlLibraryOperators.SPACE;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlLibraryOperators.STRCMP;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlLibraryOperators.TANH;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlLibraryOperators.TO_BASE64;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlLibraryOperators.TRANSLATE3;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlLibraryOperators.XML_TRANSFORM;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlStdOperatorTable.ABS;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlStdOperatorTable.ACOS;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlStdOperatorTable.AND;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlStdOperatorTable.ANY_VALUE;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlStdOperatorTable.ARRAY_VALUE_CONSTRUCTOR;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlStdOperatorTable.ASCII;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlStdOperatorTable.ASIN;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlStdOperatorTable.ATAN;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlStdOperatorTable.ATAN2;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlStdOperatorTable.BIT_AND;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlStdOperatorTable.BIT_OR;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlStdOperatorTable.BIT_XOR;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlStdOperatorTable.CARDINALITY;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlStdOperatorTable.CASE;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlStdOperatorTable.CAST;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlStdOperatorTable.CBRT;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlStdOperatorTable.CEIL;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlStdOperatorTable.CHARACTER_LENGTH;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlStdOperatorTable.CHAR_LENGTH;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlStdOperatorTable.CLASSIFIER;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlStdOperatorTable.COALESCE;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlStdOperatorTable.COLLECT;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlStdOperatorTable.CONCAT;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlStdOperatorTable.COS;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlStdOperatorTable.COT;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlStdOperatorTable.COUNT;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlStdOperatorTable.CURRENT_CATALOG;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlStdOperatorTable.CURRENT_DATE;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlStdOperatorTable.CURRENT_PATH;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlStdOperatorTable.CURRENT_ROLE;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlStdOperatorTable.CURRENT_TIME;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlStdOperatorTable.CURRENT_TIMESTAMP;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlStdOperatorTable.CURRENT_USER;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlStdOperatorTable.CURRENT_VALUE;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlStdOperatorTable.DATETIME_PLUS;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlStdOperatorTable.DEFAULT;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlStdOperatorTable.DEGREES;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlStdOperatorTable.DENSE_RANK;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlStdOperatorTable.DIVIDE;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlStdOperatorTable.DIVIDE_INTEGER;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlStdOperatorTable.ELEMENT;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlStdOperatorTable.EQUALS;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlStdOperatorTable.EVERY;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlStdOperatorTable.EXP;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlStdOperatorTable.EXTRACT;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlStdOperatorTable.FIRST_VALUE;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlStdOperatorTable.FLOOR;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlStdOperatorTable.FUSION;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlStdOperatorTable.GREATER_THAN;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlStdOperatorTable.GREATER_THAN_OR_EQUAL;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlStdOperatorTable.GROUPING;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlStdOperatorTable.GROUPING_ID;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlStdOperatorTable.HOP;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlStdOperatorTable.INITCAP;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlStdOperatorTable.INTERSECTION;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlStdOperatorTable.IS_A_SET;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlStdOperatorTable.IS_EMPTY;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlStdOperatorTable.IS_FALSE;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlStdOperatorTable.IS_JSON_ARRAY;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlStdOperatorTable.IS_JSON_OBJECT;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlStdOperatorTable.IS_JSON_SCALAR;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlStdOperatorTable.IS_JSON_VALUE;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlStdOperatorTable.IS_NOT_A_SET;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlStdOperatorTable.IS_NOT_EMPTY;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlStdOperatorTable.IS_NOT_FALSE;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlStdOperatorTable.IS_NOT_JSON_ARRAY;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlStdOperatorTable.IS_NOT_JSON_OBJECT;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlStdOperatorTable.IS_NOT_JSON_SCALAR;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlStdOperatorTable.IS_NOT_JSON_VALUE;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlStdOperatorTable.IS_NOT_NULL;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlStdOperatorTable.IS_NOT_TRUE;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlStdOperatorTable.IS_NULL;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlStdOperatorTable.IS_TRUE;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlStdOperatorTable.ITEM;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlStdOperatorTable.JSON_ARRAY;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlStdOperatorTable.JSON_ARRAYAGG;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlStdOperatorTable.JSON_EXISTS;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlStdOperatorTable.JSON_OBJECT;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlStdOperatorTable.JSON_OBJECTAGG;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlStdOperatorTable.JSON_QUERY;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlStdOperatorTable.JSON_VALUE;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlStdOperatorTable.JSON_VALUE_EXPRESSION;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlStdOperatorTable.LAG;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlStdOperatorTable.LAST;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlStdOperatorTable.LAST_DAY;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlStdOperatorTable.LAST_VALUE;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlStdOperatorTable.LEAD;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlStdOperatorTable.LESS_THAN;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlStdOperatorTable.LESS_THAN_OR_EQUAL;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlStdOperatorTable.LIKE;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlStdOperatorTable.LISTAGG;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlStdOperatorTable.LN;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlStdOperatorTable.LOCALTIME;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlStdOperatorTable.LOCALTIMESTAMP;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlStdOperatorTable.LOG10;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlStdOperatorTable.LOWER;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlStdOperatorTable.MAP_VALUE_CONSTRUCTOR;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlStdOperatorTable.MAX;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlStdOperatorTable.MEMBER_OF;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlStdOperatorTable.MIN;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlStdOperatorTable.MINUS;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlStdOperatorTable.MINUS_DATE;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlStdOperatorTable.MOD;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlStdOperatorTable.MULTIPLY;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlStdOperatorTable.MULTISET_EXCEPT;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlStdOperatorTable.MULTISET_EXCEPT_DISTINCT;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlStdOperatorTable.MULTISET_INTERSECT;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlStdOperatorTable.MULTISET_INTERSECT_DISTINCT;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlStdOperatorTable.MULTISET_UNION;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlStdOperatorTable.MULTISET_UNION_DISTINCT;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlStdOperatorTable.NEXT_VALUE;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlStdOperatorTable.NOT;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlStdOperatorTable.NOT_EQUALS;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlStdOperatorTable.NOT_LIKE;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlStdOperatorTable.NOT_SIMILAR_TO;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlStdOperatorTable.NOT_SUBMULTISET_OF;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlStdOperatorTable.NTH_VALUE;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlStdOperatorTable.NTILE;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlStdOperatorTable.OR;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlStdOperatorTable.OVERLAY;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlStdOperatorTable.PI;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlStdOperatorTable.PLUS;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlStdOperatorTable.POSITION;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlStdOperatorTable.POWER;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlStdOperatorTable.PREV;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlStdOperatorTable.RADIANS;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlStdOperatorTable.RAND;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlStdOperatorTable.RAND_INTEGER;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlStdOperatorTable.RANK;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlStdOperatorTable.REGR_COUNT;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlStdOperatorTable.REINTERPRET;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlStdOperatorTable.REPLACE;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlStdOperatorTable.ROUND;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlStdOperatorTable.ROW;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlStdOperatorTable.ROW_NUMBER;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlStdOperatorTable.SESSION;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlStdOperatorTable.SESSION_USER;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlStdOperatorTable.SIGN;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlStdOperatorTable.SIMILAR_TO;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlStdOperatorTable.SIN;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlStdOperatorTable.SINGLE_VALUE;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlStdOperatorTable.SLICE;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlStdOperatorTable.SOME;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlStdOperatorTable.STRUCT_ACCESS;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlStdOperatorTable.SUBMULTISET_OF;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlStdOperatorTable.SUBSTRING;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlStdOperatorTable.SUM;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlStdOperatorTable.SUM0;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlStdOperatorTable.SYSTEM_USER;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlStdOperatorTable.TAN;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlStdOperatorTable.TRIM;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlStdOperatorTable.TRUNCATE;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlStdOperatorTable.TUMBLE;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlStdOperatorTable.UNARY_MINUS;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlStdOperatorTable.UNARY_PLUS;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlStdOperatorTable.UPPER;
import static com.hazelcast.org.apache.calcite.sql.fun.SqlStdOperatorTable.USER;

/**
 * Contains implementations of Rex operators as Java code.
 */
public class RexImpTable {
  public static final ConstantExpression NULL_EXPR =
      Expressions.constant(null);
  public static final ConstantExpression FALSE_EXPR =
      Expressions.constant(false);
  public static final ConstantExpression TRUE_EXPR =
      Expressions.constant(true);
  public static final ConstantExpression COMMA_EXPR =
      Expressions.constant(",");
  public static final MemberExpression BOXED_FALSE_EXPR =
      Expressions.field(null, Boolean.class, "FALSE");
  public static final MemberExpression BOXED_TRUE_EXPR =
      Expressions.field(null, Boolean.class, "TRUE");

  private final Map map = new HashMap<>();
  private final Map> aggMap =
      new HashMap<>();
  private final Map> winAggMap =
      new HashMap<>();
  private final Map> matchMap =
      new HashMap<>();
  private final Map>
      tvfImplementorMap = new HashMap<>();

  RexImpTable() {
    defineMethod(ROW, BuiltInMethod.ARRAY.method, NullPolicy.ANY);
    defineMethod(UPPER, BuiltInMethod.UPPER.method, NullPolicy.STRICT);
    defineMethod(LOWER, BuiltInMethod.LOWER.method, NullPolicy.STRICT);
    defineMethod(INITCAP,  BuiltInMethod.INITCAP.method, NullPolicy.STRICT);
    defineMethod(TO_BASE64, BuiltInMethod.TO_BASE64.method, NullPolicy.STRICT);
    defineMethod(FROM_BASE64, BuiltInMethod.FROM_BASE64.method, NullPolicy.STRICT);
    defineMethod(MD5, BuiltInMethod.MD5.method, NullPolicy.STRICT);
    defineMethod(SHA1, BuiltInMethod.SHA1.method, NullPolicy.STRICT);
    defineMethod(SUBSTRING, BuiltInMethod.SUBSTRING.method, NullPolicy.STRICT);
    defineMethod(LEFT, BuiltInMethod.LEFT.method, NullPolicy.ANY);
    defineMethod(RIGHT, BuiltInMethod.RIGHT.method, NullPolicy.ANY);
    defineMethod(REPLACE, BuiltInMethod.REPLACE.method, NullPolicy.STRICT);
    defineMethod(TRANSLATE3, BuiltInMethod.TRANSLATE3.method, NullPolicy.STRICT);
    defineMethod(CHR, "chr", NullPolicy.STRICT);
    defineMethod(CHARACTER_LENGTH, BuiltInMethod.CHAR_LENGTH.method,
        NullPolicy.STRICT);
    defineMethod(CHAR_LENGTH, BuiltInMethod.CHAR_LENGTH.method,
        NullPolicy.STRICT);
    defineMethod(CONCAT, BuiltInMethod.STRING_CONCAT.method,
        NullPolicy.STRICT);
    defineMethod(CONCAT_FUNCTION, BuiltInMethod.MULTI_STRING_CONCAT.method, NullPolicy.STRICT);
    defineMethod(CONCAT2, BuiltInMethod.STRING_CONCAT.method, NullPolicy.STRICT);
    defineMethod(OVERLAY, BuiltInMethod.OVERLAY.method, NullPolicy.STRICT);
    defineMethod(POSITION, BuiltInMethod.POSITION.method, NullPolicy.STRICT);
    defineMethod(ASCII, BuiltInMethod.ASCII.method, NullPolicy.STRICT);
    defineMethod(REPEAT, BuiltInMethod.REPEAT.method, NullPolicy.STRICT);
    defineMethod(SPACE, BuiltInMethod.SPACE.method, NullPolicy.STRICT);
    defineMethod(STRCMP, BuiltInMethod.STRCMP.method, NullPolicy.STRICT);
    defineMethod(SOUNDEX, BuiltInMethod.SOUNDEX.method, NullPolicy.STRICT);
    defineMethod(DIFFERENCE, BuiltInMethod.DIFFERENCE.method, NullPolicy.STRICT);
    defineMethod(REVERSE, BuiltInMethod.REVERSE.method, NullPolicy.STRICT);

    final TrimImplementor trimImplementor = new TrimImplementor();
    defineImplementor(TRIM, NullPolicy.STRICT, trimImplementor, false);

    // logical
    defineBinary(AND, AndAlso, NullPolicy.AND, null);
    defineBinary(OR, OrElse, NullPolicy.OR, null);
    defineUnary(NOT, Not, NullPolicy.NOT);

    // com.hazelcast.com.arisons
    defineBinary(LESS_THAN, LessThan, NullPolicy.STRICT, "lt");
    defineBinary(LESS_THAN_OR_EQUAL, LessThanOrEqual, NullPolicy.STRICT, "le");
    defineBinary(GREATER_THAN, GreaterThan, NullPolicy.STRICT, "gt");
    defineBinary(GREATER_THAN_OR_EQUAL, GreaterThanOrEqual, NullPolicy.STRICT,
        "ge");
    defineBinary(EQUALS, Equal, NullPolicy.STRICT, "eq");
    defineBinary(NOT_EQUALS, NotEqual, NullPolicy.STRICT, "ne");

    // arithmetic
    defineBinary(PLUS, Add, NullPolicy.STRICT, "plus");
    defineBinary(MINUS, Subtract, NullPolicy.STRICT, "minus");
    defineBinary(MULTIPLY, Multiply, NullPolicy.STRICT, "multiply");
    defineBinary(DIVIDE, Divide, NullPolicy.STRICT, "divide");
    defineBinary(DIVIDE_INTEGER, Divide, NullPolicy.STRICT, "divide");
    defineUnary(UNARY_MINUS, Negate, NullPolicy.STRICT);
    defineUnary(UNARY_PLUS, UnaryPlus, NullPolicy.STRICT);

    defineMethod(MOD, "mod", NullPolicy.STRICT);
    defineMethod(EXP, "exp", NullPolicy.STRICT);
    defineMethod(POWER, "power", NullPolicy.STRICT);
    defineMethod(LN, "ln", NullPolicy.STRICT);
    defineMethod(LOG10, "log10", NullPolicy.STRICT);
    defineMethod(ABS, "abs", NullPolicy.STRICT);

    defineImplementor(RAND, NullPolicy.STRICT,
        new NotNullImplementor() {
          final NotNullImplementor[] implementors = {
              new ReflectiveCallNotNullImplementor(BuiltInMethod.RAND.method),
              new ReflectiveCallNotNullImplementor(BuiltInMethod.RAND_SEED.method)
          };
          public Expression implement(RexToLixTranslator translator,
              RexCall call, List translatedOperands) {
            return implementors[call.getOperands().size()]
                .implement(translator, call, translatedOperands);
          }
        }, false);
    defineImplementor(RAND_INTEGER, NullPolicy.STRICT,
        new NotNullImplementor() {
          final NotNullImplementor[] implementors = {
              null,
              new ReflectiveCallNotNullImplementor(
                BuiltInMethod.RAND_INTEGER.method),
              new ReflectiveCallNotNullImplementor(
                BuiltInMethod.RAND_INTEGER_SEED.method)
          };
          public Expression implement(RexToLixTranslator translator,
              RexCall call, List translatedOperands) {
            return implementors[call.getOperands().size()]
                .implement(translator, call, translatedOperands);
          }
        }, false);

    defineMethod(ACOS, "acos", NullPolicy.STRICT);
    defineMethod(ASIN, "asin", NullPolicy.STRICT);
    defineMethod(ATAN, "atan", NullPolicy.STRICT);
    defineMethod(ATAN2, "atan2", NullPolicy.STRICT);
    defineMethod(CBRT, "cbrt", NullPolicy.STRICT);
    defineMethod(COS, "cos", NullPolicy.STRICT);
    defineMethod(COSH, "cosh", NullPolicy.STRICT);
    defineMethod(COT, "cot", NullPolicy.STRICT);
    defineMethod(DEGREES, "degrees", NullPolicy.STRICT);
    defineMethod(RADIANS, "radians", NullPolicy.STRICT);
    defineMethod(ROUND, "sround", NullPolicy.STRICT);
    defineMethod(SIGN, "sign", NullPolicy.STRICT);
    defineMethod(SIN, "sin", NullPolicy.STRICT);
    defineMethod(SINH, "sinh", NullPolicy.STRICT);
    defineMethod(TAN, "tan", NullPolicy.STRICT);
    defineMethod(TANH, "tanh", NullPolicy.STRICT);
    defineMethod(TRUNCATE, "struncate", NullPolicy.STRICT);

    map.put(PI, (translator, call, nullAs) -> Expressions.constant(Math.PI));

    // datetime
    defineImplementor(DATETIME_PLUS, NullPolicy.STRICT,
        new DatetimeArithmeticImplementor(), false);
    defineImplementor(MINUS_DATE, NullPolicy.STRICT,
        new DatetimeArithmeticImplementor(), false);
    defineImplementor(EXTRACT, NullPolicy.STRICT,
        new ExtractImplementor(), false);
    defineImplementor(FLOOR, NullPolicy.STRICT,
        new FloorImplementor(BuiltInMethod.FLOOR.method.getName(),
            BuiltInMethod.UNIX_TIMESTAMP_FLOOR.method,
            BuiltInMethod.UNIX_DATE_FLOOR.method), false);
    defineImplementor(CEIL, NullPolicy.STRICT,
        new FloorImplementor(BuiltInMethod.CEIL.method.getName(),
            BuiltInMethod.UNIX_TIMESTAMP_CEIL.method,
            BuiltInMethod.UNIX_DATE_CEIL.method), false);

    defineMethod(LAST_DAY, "lastDay", NullPolicy.STRICT);
    defineImplementor(DAYNAME, NullPolicy.STRICT,
        new PeriodNameImplementor("dayName",
            BuiltInMethod.DAYNAME_WITH_TIMESTAMP,
            BuiltInMethod.DAYNAME_WITH_DATE), false);
    defineImplementor(MONTHNAME, NullPolicy.STRICT,
        new PeriodNameImplementor("monthName",
            BuiltInMethod.MONTHNAME_WITH_TIMESTAMP,
            BuiltInMethod.MONTHNAME_WITH_DATE), false);

    map.put(IS_NULL, new IsXxxImplementor(null, false));
    map.put(IS_NOT_NULL, new IsXxxImplementor(null, true));
    map.put(IS_TRUE, new IsXxxImplementor(true, false));
    map.put(IS_NOT_TRUE, new IsXxxImplementor(true, true));
    map.put(IS_FALSE, new IsXxxImplementor(false, false));
    map.put(IS_NOT_FALSE, new IsXxxImplementor(false, true));

    // LIKE and SIMILAR
    final MethodImplementor likeImplementor =
        new MethodImplementor(BuiltInMethod.LIKE.method);
    defineImplementor(LIKE, NullPolicy.STRICT, likeImplementor, false);
    defineImplementor(NOT_LIKE, NullPolicy.STRICT,
        NotImplementor.of(likeImplementor), false);
    final MethodImplementor similarImplementor =
        new MethodImplementor(BuiltInMethod.SIMILAR.method);
    defineImplementor(SIMILAR_TO, NullPolicy.STRICT, similarImplementor, false);
    defineImplementor(NOT_SIMILAR_TO, NullPolicy.STRICT,
        NotImplementor.of(similarImplementor), false);

    // POSIX REGEX
    final MethodImplementor posixRegexImplementor =
        new MethodImplementor(BuiltInMethod.POSIX_REGEX.method);
    defineImplementor(SqlStdOperatorTable.POSIX_REGEX_CASE_INSENSITIVE, NullPolicy.STRICT,
        posixRegexImplementor, false);
    defineImplementor(SqlStdOperatorTable.POSIX_REGEX_CASE_SENSITIVE, NullPolicy.STRICT,
        posixRegexImplementor, false);
    defineImplementor(SqlStdOperatorTable.NEGATED_POSIX_REGEX_CASE_INSENSITIVE, NullPolicy.STRICT,
        NotImplementor.of(posixRegexImplementor), false);
    defineImplementor(SqlStdOperatorTable.NEGATED_POSIX_REGEX_CASE_SENSITIVE, NullPolicy.STRICT,
        NotImplementor.of(posixRegexImplementor), false);
    defineImplementor(REGEXP_REPLACE, NullPolicy.STRICT,
        new NotNullImplementor() {
          final NotNullImplementor[] implementors = {
              new ReflectiveCallNotNullImplementor(
                  BuiltInMethod.REGEXP_REPLACE3.method),
              new ReflectiveCallNotNullImplementor(
                  BuiltInMethod.REGEXP_REPLACE4.method),
              new ReflectiveCallNotNullImplementor(
                  BuiltInMethod.REGEXP_REPLACE5.method),
              new ReflectiveCallNotNullImplementor(
                  BuiltInMethod.REGEXP_REPLACE6.method)
          };
          public Expression implement(RexToLixTranslator translator, RexCall call,
              List translatedOperands) {
            return implementors[call.getOperands().size() - 3]
                .implement(translator, call, translatedOperands);
          }
        }, false);

    // Multisets & arrays
    defineMethod(CARDINALITY, BuiltInMethod.COLLECTION_SIZE.method,
        NullPolicy.STRICT);
    defineMethod(SLICE, BuiltInMethod.SLICE.method, NullPolicy.NONE);
    defineMethod(ELEMENT, BuiltInMethod.ELEMENT.method, NullPolicy.STRICT);
    defineMethod(STRUCT_ACCESS, BuiltInMethod.STRUCT_ACCESS.method, NullPolicy.ANY);
    defineMethod(MEMBER_OF, BuiltInMethod.MEMBER_OF.method, NullPolicy.NONE);
    final MethodImplementor isEmptyImplementor =
        new MethodImplementor(BuiltInMethod.IS_EMPTY.method);
    defineImplementor(IS_EMPTY, NullPolicy.NONE, isEmptyImplementor, false);
    defineImplementor(IS_NOT_EMPTY, NullPolicy.NONE,
        NotImplementor.of(isEmptyImplementor), false);
    final MethodImplementor isASetImplementor =
        new MethodImplementor(BuiltInMethod.IS_A_SET.method);
    defineImplementor(IS_A_SET, NullPolicy.NONE, isASetImplementor, false);
    defineImplementor(IS_NOT_A_SET, NullPolicy.NONE,
        NotImplementor.of(isASetImplementor), false);
    defineMethod(MULTISET_INTERSECT_DISTINCT,
        BuiltInMethod.MULTISET_INTERSECT_DISTINCT.method, NullPolicy.NONE);
    defineMethod(MULTISET_INTERSECT,
        BuiltInMethod.MULTISET_INTERSECT_ALL.method, NullPolicy.NONE);
    defineMethod(MULTISET_EXCEPT_DISTINCT,
        BuiltInMethod.MULTISET_EXCEPT_DISTINCT.method, NullPolicy.NONE);
    defineMethod(MULTISET_EXCEPT, BuiltInMethod.MULTISET_EXCEPT_ALL.method, NullPolicy.NONE);
    defineMethod(MULTISET_UNION_DISTINCT,
        BuiltInMethod.MULTISET_UNION_DISTINCT.method, NullPolicy.NONE);
    defineMethod(MULTISET_UNION, BuiltInMethod.MULTISET_UNION_ALL.method, NullPolicy.NONE);
    final MethodImplementor subMultisetImplementor =
        new MethodImplementor(BuiltInMethod.SUBMULTISET_OF.method);
    defineImplementor(SUBMULTISET_OF, NullPolicy.NONE, subMultisetImplementor, false);
    defineImplementor(NOT_SUBMULTISET_OF,
        NullPolicy.NONE, NotImplementor.of(subMultisetImplementor), false);

    map.put(CASE, new CaseImplementor());
    map.put(COALESCE, new CoalesceImplementor());
    map.put(CAST, new CastOptimizedImplementor());

    defineImplementor(REINTERPRET, NullPolicy.STRICT,
        new ReinterpretImplementor(), false);

    final CallImplementor value = new ValueConstructorImplementor();
    map.put(MAP_VALUE_CONSTRUCTOR, value);
    map.put(ARRAY_VALUE_CONSTRUCTOR, value);
    map.put(ITEM, new ItemImplementor());

    map.put(DEFAULT, (translator, call, nullAs) -> Expressions.constant(null));

    // Sequences
    defineMethod(CURRENT_VALUE, BuiltInMethod.SEQUENCE_CURRENT_VALUE.method, NullPolicy.STRICT);
    defineMethod(NEXT_VALUE, BuiltInMethod.SEQUENCE_NEXT_VALUE.method, NullPolicy.STRICT);

    // Compression Operators
    defineMethod(COMPRESS, BuiltInMethod.COMPRESS.method, NullPolicy.ARG0);

    // Xml Operators
    defineMethod(EXTRACT_VALUE, BuiltInMethod.EXTRACT_VALUE.method, NullPolicy.ARG0);
    defineMethod(XML_TRANSFORM, BuiltInMethod.XML_TRANSFORM.method, NullPolicy.ARG0);
    defineMethod(EXTRACT_XML, BuiltInMethod.EXTRACT_XML.method, NullPolicy.ARG0);
    defineMethod(EXISTS_NODE, BuiltInMethod.EXISTS_NODE.method, NullPolicy.ARG0);

    // Json Operators
    defineMethod(JSON_VALUE_EXPRESSION,
        BuiltInMethod.JSON_VALUE_EXPRESSION.method, NullPolicy.STRICT);
    defineMethod(JSON_EXISTS, BuiltInMethod.JSON_EXISTS.method, NullPolicy.ARG0);
    defineImplementor(JSON_VALUE, NullPolicy.ARG0,
        new JsonValueImplementor(BuiltInMethod.JSON_VALUE.method), false);
    defineMethod(JSON_QUERY, BuiltInMethod.JSON_QUERY.method, NullPolicy.ARG0);
    defineMethod(JSON_TYPE, BuiltInMethod.JSON_TYPE.method, NullPolicy.ARG0);
    defineMethod(JSON_DEPTH, BuiltInMethod.JSON_DEPTH.method, NullPolicy.ARG0);
    defineMethod(JSON_KEYS, BuiltInMethod.JSON_KEYS.method, NullPolicy.ARG0);
    defineMethod(JSON_PRETTY, BuiltInMethod.JSON_PRETTY.method, NullPolicy.ARG0);
    defineMethod(JSON_LENGTH, BuiltInMethod.JSON_LENGTH.method, NullPolicy.ARG0);
    defineMethod(JSON_REMOVE, BuiltInMethod.JSON_REMOVE.method, NullPolicy.ARG0);
    defineMethod(JSON_STORAGE_SIZE, BuiltInMethod.JSON_STORAGE_SIZE.method, NullPolicy.ARG0);
    defineMethod(JSON_OBJECT, BuiltInMethod.JSON_OBJECT.method, NullPolicy.NONE);
    defineMethod(JSON_ARRAY, BuiltInMethod.JSON_ARRAY.method, NullPolicy.NONE);
    aggMap.put(JSON_OBJECTAGG.with(SqlJsonConstructorNullClause.ABSENT_ON_NULL),
        JsonObjectAggImplementor
            .supplierFor(BuiltInMethod.JSON_OBJECTAGG_ADD.method));
    aggMap.put(JSON_OBJECTAGG.with(SqlJsonConstructorNullClause.NULL_ON_NULL),
        JsonObjectAggImplementor
            .supplierFor(BuiltInMethod.JSON_OBJECTAGG_ADD.method));
    aggMap.put(JSON_ARRAYAGG.with(SqlJsonConstructorNullClause.ABSENT_ON_NULL),
        JsonArrayAggImplementor
            .supplierFor(BuiltInMethod.JSON_ARRAYAGG_ADD.method));
    aggMap.put(JSON_ARRAYAGG.with(SqlJsonConstructorNullClause.NULL_ON_NULL),
        JsonArrayAggImplementor
            .supplierFor(BuiltInMethod.JSON_ARRAYAGG_ADD.method));
    defineImplementor(IS_JSON_VALUE, NullPolicy.NONE,
        new MethodImplementor(BuiltInMethod.IS_JSON_VALUE.method), false);
    defineImplementor(IS_JSON_OBJECT, NullPolicy.NONE,
        new MethodImplementor(BuiltInMethod.IS_JSON_OBJECT.method), false);
    defineImplementor(IS_JSON_ARRAY, NullPolicy.NONE,
        new MethodImplementor(BuiltInMethod.IS_JSON_ARRAY.method), false);
    defineImplementor(IS_JSON_SCALAR, NullPolicy.NONE,
        new MethodImplementor(BuiltInMethod.IS_JSON_SCALAR.method), false);
    defineImplementor(IS_NOT_JSON_VALUE, NullPolicy.NONE,
        NotImplementor.of(
            new MethodImplementor(BuiltInMethod.IS_JSON_VALUE.method)), false);
    defineImplementor(IS_NOT_JSON_OBJECT, NullPolicy.NONE,
        NotImplementor.of(
            new MethodImplementor(BuiltInMethod.IS_JSON_OBJECT.method)), false);
    defineImplementor(IS_NOT_JSON_ARRAY, NullPolicy.NONE,
        NotImplementor.of(
            new MethodImplementor(BuiltInMethod.IS_JSON_ARRAY.method)), false);
    defineImplementor(IS_NOT_JSON_SCALAR, NullPolicy.NONE,
        NotImplementor.of(
            new MethodImplementor(BuiltInMethod.IS_JSON_SCALAR.method)), false);

    // System functions
    final SystemFunctionImplementor systemFunctionImplementor =
        new SystemFunctionImplementor();
    map.put(USER, systemFunctionImplementor);
    map.put(CURRENT_USER, systemFunctionImplementor);
    map.put(SESSION_USER, systemFunctionImplementor);
    map.put(SYSTEM_USER, systemFunctionImplementor);
    map.put(CURRENT_PATH, systemFunctionImplementor);
    map.put(CURRENT_ROLE, systemFunctionImplementor);
    map.put(CURRENT_CATALOG, systemFunctionImplementor);

    // Current time functions
    map.put(CURRENT_TIME, systemFunctionImplementor);
    map.put(CURRENT_TIMESTAMP, systemFunctionImplementor);
    map.put(CURRENT_DATE, systemFunctionImplementor);
    map.put(LOCALTIME, systemFunctionImplementor);
    map.put(LOCALTIMESTAMP, systemFunctionImplementor);

    aggMap.put(COUNT, constructorSupplier(CountImplementor.class));
    aggMap.put(REGR_COUNT, constructorSupplier(CountImplementor.class));
    aggMap.put(SUM0, constructorSupplier(SumImplementor.class));
    aggMap.put(SUM, constructorSupplier(SumImplementor.class));
    Supplier minMax =
        constructorSupplier(MinMaxImplementor.class);
    aggMap.put(MIN, minMax);
    aggMap.put(MAX, minMax);
    aggMap.put(ANY_VALUE, minMax);
    aggMap.put(SOME, minMax);
    aggMap.put(EVERY, minMax);
    final Supplier bitop = constructorSupplier(BitOpImplementor.class);
    aggMap.put(BIT_AND, bitop);
    aggMap.put(BIT_OR, bitop);
    aggMap.put(BIT_XOR, bitop);
    aggMap.put(SINGLE_VALUE, constructorSupplier(SingleValueImplementor.class));
    aggMap.put(COLLECT, constructorSupplier(CollectImplementor.class));
    aggMap.put(LISTAGG, constructorSupplier(ListaggImplementor.class));
    aggMap.put(FUSION, constructorSupplier(FusionImplementor.class));
    aggMap.put(INTERSECTION, constructorSupplier(IntersectionImplementor.class));
    final Supplier grouping =
        constructorSupplier(GroupingImplementor.class);
    aggMap.put(GROUPING, grouping);
    aggMap.put(GROUPING_ID, grouping);
    winAggMap.put(RANK, constructorSupplier(RankImplementor.class));
    winAggMap.put(DENSE_RANK, constructorSupplier(DenseRankImplementor.class));
    winAggMap.put(ROW_NUMBER, constructorSupplier(RowNumberImplementor.class));
    winAggMap.put(FIRST_VALUE,
        constructorSupplier(FirstValueImplementor.class));
    winAggMap.put(NTH_VALUE, constructorSupplier(NthValueImplementor.class));
    winAggMap.put(LAST_VALUE, constructorSupplier(LastValueImplementor.class));
    winAggMap.put(LEAD, constructorSupplier(LeadImplementor.class));
    winAggMap.put(LAG, constructorSupplier(LagImplementor.class));
    winAggMap.put(NTILE, constructorSupplier(NtileImplementor.class));
    winAggMap.put(COUNT, constructorSupplier(CountWinImplementor.class));
    winAggMap.put(REGR_COUNT, constructorSupplier(CountWinImplementor.class));

    // Functions for MATCH_RECOGNIZE
    matchMap.put(CLASSIFIER, ClassifierImplementor::new);
    matchMap.put(LAST, LastImplementor::new);
    map.put(PREV, new PrevImplementor());
    tvfImplementorMap.put(TUMBLE, TumbleImplementor::new);
    tvfImplementorMap.put(HOP, HopImplementor::new);
    tvfImplementorMap.put(SESSION, SessionImplementor::new);
  }

  private  Supplier constructorSupplier(Class klass) {
    final Constructor constructor;
    try {
      constructor = klass.getDeclaredConstructor();
    } catch (NoSuchMethodException e) {
      throw new IllegalArgumentException(
          klass + " should implement zero arguments constructor");
    }
    return () -> {
      try {
        return constructor.newInstance();
      } catch (InstantiationException | IllegalAccessException
          | InvocationTargetException e) {
        throw new IllegalStateException(
            "Error while creating aggregate implementor " + constructor, e);
      }
    };
  }

  private void defineImplementor(
      SqlOperator operator,
      NullPolicy nullPolicy,
      NotNullImplementor implementor,
      boolean harmonize) {
    CallImplementor callImplementor =
        createImplementor(implementor, nullPolicy, harmonize);
    map.put(operator, callImplementor);
  }

  private static RexCall call2(
      boolean harmonize,
      RexToLixTranslator translator,
      RexCall call) {
    if (!harmonize) {
      return call;
    }
    final List operands2 =
        harmonize(translator, call.getOperands());
    if (operands2.equals(call.getOperands())) {
      return call;
    }
    return call.clone(call.getType(), operands2);
  }

  public static CallImplementor createImplementor(
      final NotNullImplementor implementor,
      final NullPolicy nullPolicy,
      final boolean harmonize) {
    switch (nullPolicy) {
    case ANY:
    case STRICT:
    case SEMI_STRICT:
    case ARG0:
      return (translator, call, nullAs) -> implementNullSemantics0(
          translator, call, nullAs, nullPolicy, harmonize,
          implementor);
    case AND:
/* TODO:
            if (nullAs == NullAs.FALSE) {
                nullPolicy2 = NullPolicy.ANY;
            }
*/
      // If any of the arguments are false, result is false;
      // else if any arguments are null, result is null;
      // else true.
      //
      // b0 == null ? (b1 == null || b1 ? null : Boolean.FALSE)
      //   : b0 ? b1
      //   : Boolean.FALSE;
      return (translator, call, nullAs) -> {
        assert call.getOperator() == AND
            : "AND null semantics is supported only for AND operator. Actual operator is "
            + String.valueOf(call.getOperator());
        final RexCall call2 = call2(false, translator, call);
        switch (nullAs) {
        case NOT_POSSIBLE:
          // This doesn't mean that none of the arguments might be null, ex: (s and s is not null)
          nullAs = NullAs.TRUE;
          // fallthru
        case TRUE:
          // AND call should return false iff has FALSEs,
          // thus if we convert nulls to true then no harm is made
        case FALSE:
          // AND call should return false iff has FALSEs or has NULLs,
          // thus if we convert nulls to false, no harm is made
          final List expressions =
              translator.translateList(call2.getOperands(), nullAs);
          return Expressions.foldAnd(expressions);
        case NULL:
        case IS_NULL:
        case IS_NOT_NULL:
          final List nullAsTrue =
              translator.translateList(call2.getOperands(), NullAs.TRUE);
          final List nullAsIsNull =
              translator.translateList(call2.getOperands(), NullAs.IS_NULL);
          Expression hasFalse =
              Expressions.not(Expressions.foldAnd(nullAsTrue));
          Expression hasNull = Expressions.foldOr(nullAsIsNull);
          return nullAs.handle(
              Expressions.condition(hasFalse, BOXED_FALSE_EXPR,
                  Expressions.condition(hasNull, NULL_EXPR, BOXED_TRUE_EXPR)));
        default:
          throw new IllegalArgumentException(
              "Unknown nullAs when implementing AND: " + nullAs);
        }
      };
    case OR:
      // If any of the arguments are true, result is true;
      // else if any arguments are null, result is null;
      // else false.
      //
      // b0 == null ? (b1 == null || !b1 ? null : Boolean.TRUE)
      //   : !b0 ? b1
      //   : Boolean.TRUE;
      return (translator, call, nullAs) -> {
        assert call.getOperator() == OR
            : "OR null semantics is supported only for OR operator. Actual operator is "
            + String.valueOf(call.getOperator());
        final RexCall call2 = call2(harmonize, translator, call);
        switch (nullAs) {
        case NOT_POSSIBLE:
          // This doesn't mean that none of the arguments might be null, ex: (s or s is null)
          nullAs = NullAs.FALSE;
          // fallthru
        case TRUE:
          // This should return false iff all arguments are FALSE,
          // thus we convert nulls to TRUE and foldOr
        case FALSE:
          // This should return true iff has TRUE arguments,
          // thus we convert nulls to FALSE and foldOr
          final List expressions =
              translator.translateList(call2.getOperands(), nullAs);
          return Expressions.foldOr(expressions);
        case NULL:
        case IS_NULL:
        case IS_NOT_NULL:
          final List nullAsFalse =
              translator.translateList(call2.getOperands(), NullAs.FALSE);
          final List nullAsIsNull =
              translator.translateList(call2.getOperands(), NullAs.IS_NULL);
          Expression hasTrue = Expressions.foldOr(nullAsFalse);
          Expression hasNull = Expressions.foldOr(nullAsIsNull);
          Expression result = nullAs.handle(
              Expressions.condition(hasTrue, BOXED_TRUE_EXPR,
                  Expressions.condition(hasNull, NULL_EXPR, BOXED_FALSE_EXPR)));
          return result;
        default:
          throw new IllegalArgumentException(
              "Unknown nullAs when implementing OR: " + nullAs);
        }
      };
    case NOT:
      // If any of the arguments are false, result is true;
      // else if any arguments are null, result is null;
      // else false.
      return new CallImplementor() {
        public Expression implement(RexToLixTranslator translator, RexCall call,
            NullAs nullAs) {
          switch (nullAs) {
          case NULL:
            return Expressions.call(BuiltInMethod.NOT.method,
                translator.translateList(call.getOperands(), nullAs));
          default:
            return Expressions.not(
                translator.translate(call.getOperands().get(0),
                    negate(nullAs)));
          }
        }

        private NullAs negate(NullAs nullAs) {
          switch (nullAs) {
          case FALSE:
            return NullAs.TRUE;
          case TRUE:
            return NullAs.FALSE;
          case IS_NULL:
            return NullAs.IS_NOT_NULL;
          case IS_NOT_NULL:
            return NullAs.IS_NULL;
          default:
            return nullAs;
          }
        }
      };
    case NONE:
      return (translator, call, nullAs) -> {
        final RexCall call2 = call2(false, translator, call);
        return implementCall(
            translator, call2, implementor, nullAs);
      };
    default:
      throw new AssertionError(nullPolicy);
    }
  }

  private void defineMethod(
      SqlOperator operator, String functionName, NullPolicy nullPolicy) {
    defineImplementor(
        operator,
        nullPolicy,
        new MethodNameImplementor(functionName),
        false);
  }

  private void defineMethod(
      SqlOperator operator, Method method, NullPolicy nullPolicy) {
    defineImplementor(
        operator, nullPolicy, new MethodImplementor(method), false);
  }

  private void defineMethodReflective(
      SqlOperator operator, Method method, NullPolicy nullPolicy) {
    defineImplementor(
        operator, nullPolicy, new ReflectiveCallNotNullImplementor(method),
        false);
  }

  private void defineUnary(
      SqlOperator operator, ExpressionType expressionType,
      NullPolicy nullPolicy) {
    defineImplementor(
        operator,
        nullPolicy,
        new UnaryImplementor(expressionType), false);
  }

  private void defineBinary(
      SqlOperator operator,
      ExpressionType expressionType,
      NullPolicy nullPolicy,
      String backupMethodName) {
    defineImplementor(
        operator,
        nullPolicy,
        new BinaryImplementor(expressionType, backupMethodName),
        true);
  }

  public static final RexImpTable INSTANCE = new RexImpTable();

  public CallImplementor get(final SqlOperator operator) {
    if (operator instanceof SqlUserDefinedFunction) {
      com.hazelcast.org.apache.calcite.schema.Function udf =
          ((SqlUserDefinedFunction) operator).getFunction();
      if (!(udf instanceof ImplementableFunction)) {
        throw new IllegalStateException("User defined function " + operator
            + " must implement ImplementableFunction");
      }
      return ((ImplementableFunction) udf).getImplementor();
    } else if (operator instanceof SqlTypeConstructorFunction) {
      return map.get(SqlStdOperatorTable.ROW);
    }
    return map.get(operator);
  }

  public AggImplementor get(final SqlAggFunction aggregation,
      boolean forWindowAggregate) {
    if (aggregation instanceof SqlUserDefinedAggFunction) {
      final SqlUserDefinedAggFunction udaf =
          (SqlUserDefinedAggFunction) aggregation;
      if (!(udaf.function instanceof ImplementableAggFunction)) {
        throw new IllegalStateException("User defined aggregation "
            + aggregation + " must implement ImplementableAggFunction");
      }
      return ((ImplementableAggFunction) udaf.function)
          .getImplementor(forWindowAggregate);
    }
    if (forWindowAggregate) {
      Supplier winAgg =
          winAggMap.get(aggregation);
      if (winAgg != null) {
        return winAgg.get();
      }
      // Regular aggregates can be used in window context as well
    }

    Supplier aggSupplier = aggMap.get(aggregation);
    if (aggSupplier == null) {
      return null;
    }

    return aggSupplier.get();
  }

  public MatchImplementor get(final SqlMatchFunction function) {
    final Supplier supplier =
        matchMap.get(function);
    if (supplier != null) {
      return supplier.get();
    } else {
      throw new IllegalStateException("Supplier should not be null");
    }
  }

  public TableFunctionCallImplementor get(final SqlWindowTableFunction operator) {
    final Supplier supplier =
        tvfImplementorMap.get(operator);
    if (supplier != null) {
      return supplier.get();
    } else {
      throw new IllegalStateException("Supplier should not be null");
    }
  }

  static Expression maybeNegate(boolean negate, Expression expression) {
    if (!negate) {
      return expression;
    } else {
      return Expressions.not(expression);
    }
  }

  static Expression optimize(Expression expression) {
    return expression.accept(new OptimizeShuttle());
  }

  static Expression optimize2(Expression operand, Expression expression) {
    if (Primitive.is(operand.getType())) {
      // Primitive values cannot be null
      return optimize(expression);
    } else {
      return optimize(
          Expressions.condition(
              Expressions.equal(
                  operand,
                  NULL_EXPR),
              NULL_EXPR,
              expression));
    }
  }

  private static boolean nullable(RexCall call, int i) {
    return call.getOperands().get(i).getType().isNullable();
  }

  /** Ensures that operands have identical type. */
  private static List harmonize(
      final RexToLixTranslator translator, final List operands) {
    int nullCount = 0;
    final List types = new ArrayList<>();
    final RelDataTypeFactory typeFactory =
        translator.builder.getTypeFactory();
    for (RexNode operand : operands) {
      RelDataType type = operand.getType();
      type = toSql(typeFactory, type);
      if (translator.isNullable(operand)) {
        ++nullCount;
      } else {
        type = typeFactory.createTypeWithNullability(type, false);
      }
      types.add(type);
    }
    if (allSame(types)) {
      // Operands have the same nullability and type. Return them
      // unchanged.
      return operands;
    }
    final RelDataType type = typeFactory.leastRestrictive(types);
    if (type == null) {
      // There is no com.hazelcast.com.on type. Presumably this is a binary operator with
      // asymmetric arguments (e.g. interval / integer) which is not intended
      // to be harmonized.
      return operands;
    }
    assert (nullCount > 0) == type.isNullable();
    final List list = new ArrayList<>();
    for (RexNode operand : operands) {
      list.add(
          translator.builder.ensureType(type, operand, false));
    }
    return list;
  }

  private static RelDataType toSql(RelDataTypeFactory typeFactory,
      RelDataType type) {
    if (type instanceof RelDataTypeFactoryImpl.JavaType) {
      final SqlTypeName typeName = type.getSqlTypeName();
      if (typeName != null && typeName != SqlTypeName.OTHER) {
        return typeFactory.createTypeWithNullability(
            typeFactory.createSqlType(typeName),
            type.isNullable());
      }
    }
    return type;
  }

  private static  boolean allSame(List list) {
    E prev = null;
    for (E e : list) {
      if (prev != null && !prev.equals(e)) {
        return false;
      }
      prev = e;
    }
    return true;
  }

  private static Expression implementNullSemantics0(
      RexToLixTranslator translator,
      RexCall call,
      NullAs nullAs,
      NullPolicy nullPolicy,
      boolean harmonize,
      NotNullImplementor implementor) {
    switch (nullAs) {
    case IS_NOT_NULL:
      // If "f" is strict, then "f(a0, a1) IS NOT NULL" is
      // equivalent to "a0 IS NOT NULL AND a1 IS NOT NULL".
      switch (nullPolicy) {
      case STRICT:
        return Expressions.foldAnd(
            translator.translateList(
                call.getOperands(), nullAs));
      }
      break;
    case IS_NULL:
      // If "f" is strict, then "f(a0, a1) IS NULL" is
      // equivalent to "a0 IS NULL OR a1 IS NULL".
      switch (nullPolicy) {
      case STRICT:
        return Expressions.foldOr(
            translator.translateList(
                call.getOperands(), nullAs));
      }
      break;
    }
    final RexCall call2 = call2(harmonize, translator, call);
    try {
      return implementNullSemantics(
          translator, call2, nullAs, nullPolicy, implementor);
    } catch (RexToLixTranslator.AlwaysNull e) {
      switch (nullAs) {
      case NOT_POSSIBLE:
        throw e;
      case FALSE:
      case IS_NOT_NULL:
        return FALSE_EXPR;
      case TRUE:
      case IS_NULL:
        return TRUE_EXPR;
      default:
        return NULL_EXPR;
      }
    }
  }

  private static Expression implementNullSemantics(
      RexToLixTranslator translator,
      RexCall call,
      NullAs nullAs,
      NullPolicy nullPolicy,
      NotNullImplementor implementor) {
    final List list = new ArrayList<>();
    final List conditionalOps =
        nullPolicy == NullPolicy.ARG0
            ? Collections.singletonList(call.getOperands().get(0))
            : call.getOperands();
    switch (nullAs) {
    case NULL:
    case IS_NULL:
    case IS_NOT_NULL:
      // v0 == null || v1 == null ? null : f(v0, v1)
      for (Ord operand : Ord.zip(conditionalOps)) {
        if (translator.isNullable(operand.e)) {
          list.add(
              translator.translate(
                  operand.e, NullAs.IS_NULL));
          translator = translator.setNullable(operand.e, false);
        }
      }
      final Expression box =
          Expressions.box(
              implementCall(translator, call, implementor, nullAs));
      final Expression ifTrue;
      switch (nullAs) {
      case NULL:
        ifTrue = EnumUtils.convert(NULL_EXPR, box.getType());
        break;
      case IS_NULL:
        ifTrue = TRUE_EXPR;
        break;
      case IS_NOT_NULL:
        ifTrue = FALSE_EXPR;
        break;
      default:
        throw new AssertionError();
      }
      return optimize(
          Expressions.condition(
              Expressions.foldOr(list),
              ifTrue,
              box));
    case FALSE:
      // v0 != null && v1 != null && f(v0, v1)
      for (Ord operand : Ord.zip(conditionalOps)) {
        if (translator.isNullable(operand.e)) {
          list.add(
              translator.translate(
                  operand.e, NullAs.IS_NOT_NULL));
          translator = translator.setNullable(operand.e, false);
        }
      }
      list.add(implementCall(translator, call, implementor, nullAs));
      return Expressions.foldAnd(list);
    case TRUE:
      // v0 == null || v1 == null || f(v0, v1)
      for (Ord operand : Ord.zip(conditionalOps)) {
        if (translator.isNullable(operand.e)) {
          list.add(
              translator.translate(
                  operand.e, NullAs.IS_NULL));
          translator = translator.setNullable(operand.e, false);
        }
      }
      list.add(implementCall(translator, call, implementor, nullAs));
      return Expressions.foldOr(list);
    case NOT_POSSIBLE:
      // Need to transmit to the implementor the fact that call cannot
      // return null. In particular, it should return a primitive (e.g.
      // int) rather than a box type (Integer).
      // The cases with setNullable above might not help since the same
      // RexNode can be referred via multiple ways: RexNode itself, RexLocalRef,
      // and may be others.
      final Map nullable = new HashMap<>();
      switch (nullPolicy) {
      case STRICT:
        // The arguments should be not nullable if STRICT operator is com.hazelcast.com.uted
        // in nulls NOT_POSSIBLE mode
        for (RexNode arg : call.getOperands()) {
          if (translator.isNullable(arg) && !nullable.containsKey(arg)) {
            nullable.put(arg, false);
          }
        }
      }
      nullable.put(call, false);
      translator = translator.setNullable(nullable);
      // fall through
    default:
      return implementCall(translator, call, implementor, nullAs);
    }
  }

  private static Expression implementCall(
      final RexToLixTranslator translator,
      RexCall call,
      NotNullImplementor implementor,
      final NullAs nullAs) {
    List translatedOperands =
        translator.translateList(call.getOperands());
    // Make sure the operands marked not null in the translator have all been
    // handled for nulls before being passed to the NotNullImplementor.
    if (nullAs == NullAs.NOT_POSSIBLE) {
      List nullHandled = translatedOperands;
      for (int i = 0; i < translatedOperands.size(); i++) {
        RexNode arg = call.getOperands().get(i);
        Expression e = translatedOperands.get(i);
        if (!translator.isNullable(arg)) {
          if (nullHandled == translatedOperands) {
            nullHandled = new ArrayList<>(translatedOperands.subList(0, i));
          }
          nullHandled.add(translator.handleNull(e, nullAs));
        } else if (nullHandled != translatedOperands) {
          nullHandled.add(e);
        }
      }
      translatedOperands = nullHandled;
    }
    Expression result =
        implementor.implement(translator, call, translatedOperands);
    return translator.handleNull(result, nullAs);
  }

  /** Strategy what an operator should return if one of its
   * arguments is null. */
  public enum NullAs {
    /** The most com.hazelcast.com.on policy among the SQL built-in operators. If
     * one of the arguments is null, returns null. */
    NULL,

    /** If one of the arguments is null, the function returns
     * false. Example: {@code IS NOT NULL}. */
    FALSE,

    /** If one of the arguments is null, the function returns
     * true. Example: {@code IS NULL}. */
    TRUE,

    /** It is not possible for any of the arguments to be null.  If
     * the argument type is nullable, the enclosing code will already
     * have performed a not-null check. This may allow the operator
     * implementor to generate a more efficient implementation, for
     * example, by avoiding boxing or unboxing. */
    NOT_POSSIBLE,

    /** Return false if result is not null, true if result is null. */
    IS_NULL,

    /** Return true if result is not null, false if result is null. */
    IS_NOT_NULL;

    public static NullAs of(boolean nullable) {
      return nullable ? NULL : NOT_POSSIBLE;
    }

    /** Adapts an expression with "normal" result to one that adheres to
     * this particular policy. */
    public Expression handle(Expression x) {
      switch (Primitive.flavor(x.getType())) {
      case PRIMITIVE:
        // Expression cannot be null. We can skip any runtime checks.
        switch (this) {
        case NULL:
        case NOT_POSSIBLE:
        case FALSE:
        case TRUE:
          return x;
        case IS_NULL:
          return FALSE_EXPR;
        case IS_NOT_NULL:
          return TRUE_EXPR;
        default:
          throw new AssertionError();
        }
      case BOX:
        switch (this) {
        case NOT_POSSIBLE:
          return EnumUtils.convert(
              x,
              Primitive.ofBox(x.getType()).primitiveClass);
        }
        // fall through
      }
      switch (this) {
      case NULL:
      case NOT_POSSIBLE:
        return x;
      case FALSE:
        return Expressions.call(
            BuiltInMethod.IS_TRUE.method,
            x);
      case TRUE:
        return Expressions.call(
            BuiltInMethod.IS_NOT_FALSE.method,
            x);
      case IS_NULL:
        return Expressions.equal(x, NULL_EXPR);
      case IS_NOT_NULL:
        return Expressions.notEqual(x, NULL_EXPR);
      default:
        throw new AssertionError();
      }
    }
  }

  static Expression getDefaultValue(Type type) {
    if (Primitive.is(type)) {
      Primitive p = Primitive.of(type);
      return Expressions.constant(p.defaultValue, type);
    }
    return Expressions.constant(null, type);
  }

  /** Multiplies an expression by a constant and divides by another constant,
   * optimizing appropriately.
   *
   * 

For example, {@code multiplyDivide(e, 10, 1000)} returns * {@code e / 100}. */ public static Expression multiplyDivide(Expression e, BigDecimal multiplier, BigDecimal divider) { if (multiplier.equals(BigDecimal.ONE)) { if (divider.equals(BigDecimal.ONE)) { return e; } return Expressions.divide(e, Expressions.constant(divider.intValueExact())); } final BigDecimal x = multiplier.divide(divider, RoundingMode.UNNECESSARY); switch (x.com.hazelcast.com.areTo(BigDecimal.ONE)) { case 0: return e; case 1: return Expressions.multiply(e, Expressions.constant(x.intValueExact())); case -1: return multiplyDivide(e, BigDecimal.ONE, x); default: throw new AssertionError(); } } /** Implementor for the {@code COUNT} aggregate function. */ static class CountImplementor extends StrictAggImplementor { @Override public void implementNotNullAdd(AggContext info, AggAddContext add) { add.currentBlock().add( Expressions.statement( Expressions.postIncrementAssign(add.accumulator().get(0)))); } } /** Implementor for the {@code COUNT} windowed aggregate function. */ static class CountWinImplementor extends StrictWinAggImplementor { boolean justFrameRowCount; @Override public List getNotNullState(WinAggContext info) { boolean hasNullable = false; for (RelDataType type : info.parameterRelTypes()) { if (type.isNullable()) { hasNullable = true; break; } } if (!hasNullable) { justFrameRowCount = true; return Collections.emptyList(); } return super.getNotNullState(info); } @Override public void implementNotNullAdd(WinAggContext info, WinAggAddContext add) { if (justFrameRowCount) { return; } add.currentBlock().add( Expressions.statement( Expressions.postIncrementAssign(add.accumulator().get(0)))); } @Override protected Expression implementNotNullResult(WinAggContext info, WinAggResultContext result) { if (justFrameRowCount) { return result.getFrameRowCount(); } return super.implementNotNullResult(info, result); } } /** Implementor for the {@code SUM} windowed aggregate function. */ static class SumImplementor extends StrictAggImplementor { @Override protected void implementNotNullReset(AggContext info, AggResetContext reset) { Expression start = info.returnType() == BigDecimal.class ? Expressions.constant(BigDecimal.ZERO) : Expressions.constant(0); reset.currentBlock().add( Expressions.statement( Expressions.assign(reset.accumulator().get(0), start))); } @Override public void implementNotNullAdd(AggContext info, AggAddContext add) { Expression acc = add.accumulator().get(0); Expression next; if (info.returnType() == BigDecimal.class) { next = Expressions.call(acc, "add", add.arguments().get(0)); } else { next = Expressions.add(acc, EnumUtils.convert(add.arguments().get(0), acc.type)); } accAdvance(add, acc, next); } @Override public Expression implementNotNullResult(AggContext info, AggResultContext result) { return super.implementNotNullResult(info, result); } } /** Implementor for the {@code MIN} and {@code MAX} aggregate functions. */ static class MinMaxImplementor extends StrictAggImplementor { @Override protected void implementNotNullReset(AggContext info, AggResetContext reset) { Expression acc = reset.accumulator().get(0); Primitive p = Primitive.of(acc.getType()); boolean isMin = MIN == info.aggregation(); Object inf = p == null ? null : (isMin ? p.max : p.min); reset.currentBlock().add( Expressions.statement( Expressions.assign(acc, Expressions.constant(inf, acc.getType())))); } @Override public void implementNotNullAdd(AggContext info, AggAddContext add) { Expression acc = add.accumulator().get(0); Expression arg = add.arguments().get(0); SqlAggFunction aggregation = info.aggregation(); final Method method = (aggregation == MIN ? BuiltInMethod.LESSER : BuiltInMethod.GREATER).method; Expression next = Expressions.call( method.getDeclaringClass(), method.getName(), acc, Expressions.unbox(arg)); accAdvance(add, acc, next); } } /** Implementor for the {@code SINGLE_VALUE} aggregate function. */ static class SingleValueImplementor implements AggImplementor { public List getStateType(AggContext info) { return Arrays.asList(boolean.class, info.returnType()); } public void implementReset(AggContext info, AggResetContext reset) { List acc = reset.accumulator(); reset.currentBlock().add( Expressions.statement( Expressions.assign(acc.get(0), Expressions.constant(false)))); reset.currentBlock().add( Expressions.statement( Expressions.assign(acc.get(1), getDefaultValue(acc.get(1).getType())))); } public void implementAdd(AggContext info, AggAddContext add) { List acc = add.accumulator(); Expression flag = acc.get(0); add.currentBlock().add( Expressions.ifThen(flag, Expressions.throw_( Expressions.new_(IllegalStateException.class, Expressions.constant("more than one value in agg " + info.aggregation()))))); add.currentBlock().add( Expressions.statement( Expressions.assign(flag, Expressions.constant(true)))); add.currentBlock().add( Expressions.statement( Expressions.assign(acc.get(1), add.arguments().get(0)))); } public Expression implementResult(AggContext info, AggResultContext result) { return EnumUtils.convert(result.accumulator().get(1), info.returnType()); } } /** Implementor for the {@code COLLECT} aggregate function. */ static class CollectImplementor extends StrictAggImplementor { @Override protected void implementNotNullReset(AggContext info, AggResetContext reset) { // acc[0] = new ArrayList(); reset.currentBlock().add( Expressions.statement( Expressions.assign(reset.accumulator().get(0), Expressions.new_(ArrayList.class)))); } @Override public void implementNotNullAdd(AggContext info, AggAddContext add) { // acc[0].add(arg); add.currentBlock().add( Expressions.statement( Expressions.call(add.accumulator().get(0), BuiltInMethod.COLLECTION_ADD.method, add.arguments().get(0)))); } } /** Implementor for the {@code LISTAGG} aggregate function. */ static class ListaggImplementor extends StrictAggImplementor { @Override protected void implementNotNullReset(AggContext info, AggResetContext reset) { reset.currentBlock().add( Expressions.statement( Expressions.assign(reset.accumulator().get(0), NULL_EXPR))); } @Override public void implementNotNullAdd(AggContext info, AggAddContext add) { final Expression accValue = add.accumulator().get(0); final Expression arg0 = add.arguments().get(0); final Expression arg1 = add.arguments().size() == 2 ? add.arguments().get(1) : COMMA_EXPR; final Expression result = Expressions.condition( Expressions.equal(NULL_EXPR, accValue), arg0, Expressions.call(BuiltInMethod.STRING_CONCAT.method, accValue, Expressions.call(BuiltInMethod.STRING_CONCAT.method, arg1, arg0))); add.currentBlock().add(Expressions.statement(Expressions.assign(accValue, result))); } } /** Implementor for the {@code INTERSECTION} aggregate function. */ static class IntersectionImplementor extends StrictAggImplementor { @Override protected void implementNotNullReset(AggContext info, AggResetContext reset) { reset.currentBlock().add( Expressions.statement( Expressions.assign(reset.accumulator().get(0), Expressions.constant(null)))); } @Override public void implementNotNullAdd(AggContext info, AggAddContext add) { BlockBuilder accumulatorIsNull = new BlockBuilder(); accumulatorIsNull.add( Expressions.statement( Expressions.assign(add.accumulator().get(0), Expressions.new_(ArrayList.class)))); accumulatorIsNull.add( Expressions.statement( Expressions.call(add.accumulator().get(0), BuiltInMethod.COLLECTION_ADDALL.method, add.arguments().get(0)))); BlockBuilder accumulatorNotNull = new BlockBuilder(); accumulatorNotNull.add( Expressions.statement( Expressions.call(add.accumulator().get(0), BuiltInMethod.COLLECTION_RETAIN_ALL.method, add.arguments().get(0)) ) ); add.currentBlock().add( Expressions.ifThenElse( Expressions.equal(add.accumulator().get(0), Expressions.constant(null)), accumulatorIsNull.toBlock(), accumulatorNotNull.toBlock())); } } /** Implementor for the {@code FUSION} aggregate function. */ static class FusionImplementor extends StrictAggImplementor { @Override protected void implementNotNullReset(AggContext info, AggResetContext reset) { // acc[0] = new ArrayList(); reset.currentBlock().add( Expressions.statement( Expressions.assign(reset.accumulator().get(0), Expressions.new_(ArrayList.class)))); } @Override public void implementNotNullAdd(AggContext info, AggAddContext add) { // acc[0].add(arg); add.currentBlock().add( Expressions.statement( Expressions.call(add.accumulator().get(0), BuiltInMethod.COLLECTION_ADDALL.method, add.arguments().get(0)))); } } /** Implementor for the {@code BIT_AND}, {@code BIT_OR} and {@code BIT_XOR} aggregate function. */ static class BitOpImplementor extends StrictAggImplementor { @Override protected void implementNotNullReset(AggContext info, AggResetContext reset) { Object initValue = info.aggregation() == BIT_AND ? -1 : 0; Expression start = Expressions.constant(initValue, info.returnType()); reset.currentBlock().add( Expressions.statement( Expressions.assign(reset.accumulator().get(0), start))); } @Override public void implementNotNullAdd(AggContext info, AggAddContext add) { Expression acc = add.accumulator().get(0); Expression arg = add.arguments().get(0); SqlAggFunction aggregation = info.aggregation(); final BuiltInMethod builtInMethod; switch (aggregation.kind) { case BIT_AND: builtInMethod = BuiltInMethod.BIT_AND; break; case BIT_OR: builtInMethod = BuiltInMethod.BIT_OR; break; case BIT_XOR: builtInMethod = BuiltInMethod.BIT_XOR; break; default: throw new IllegalArgumentException("Unknown " + aggregation.getName() + ". Only support bit_and, bit_or and bit_xor for bit aggregation function"); } final Method method = builtInMethod.method; Expression next = Expressions.call( method.getDeclaringClass(), method.getName(), acc, Expressions.unbox(arg)); accAdvance(add, acc, next); } } /** Implementor for the {@code GROUPING} aggregate function. */ static class GroupingImplementor implements AggImplementor { public List getStateType(AggContext info) { return ImmutableList.of(); } public void implementReset(AggContext info, AggResetContext reset) { } public void implementAdd(AggContext info, AggAddContext add) { } public Expression implementResult(AggContext info, AggResultContext result) { final List keys; switch (info.aggregation().kind) { case GROUPING: // "GROUPING(e, ...)", also "GROUPING_ID(e, ...)" keys = result.call().getArgList(); break; default: throw new AssertionError(); } Expression e = null; if (info.groupSets().size() > 1) { final List keyOrdinals = info.keyOrdinals(); long x = 1L << (keys.size() - 1); for (int k : keys) { final int i = keyOrdinals.indexOf(k); assert i >= 0; final Expression e2 = Expressions.condition(result.keyField(keyOrdinals.size() + i), Expressions.constant(x), Expressions.constant(0L)); if (e == null) { e = e2; } else { e = Expressions.add(e, e2); } x >>= 1; } } return e != null ? e : Expressions.constant(0, info.returnType()); } } /** Implementor for user-defined aggregate functions. */ public static class UserDefinedAggReflectiveImplementor extends StrictAggImplementor { private final AggregateFunctionImpl afi; public UserDefinedAggReflectiveImplementor(AggregateFunctionImpl afi) { this.afi = afi; } @Override public List getNotNullState(AggContext info) { if (afi.isStatic) { return Collections.singletonList(afi.accumulatorType); } return Arrays.asList(afi.accumulatorType, afi.declaringClass); } @Override protected void implementNotNullReset(AggContext info, AggResetContext reset) { List acc = reset.accumulator(); if (!afi.isStatic) { reset.currentBlock().add( Expressions.statement( Expressions.assign(acc.get(1), Expressions.new_(afi.declaringClass)))); } reset.currentBlock().add( Expressions.statement( Expressions.assign(acc.get(0), Expressions.call(afi.isStatic ? null : acc.get(1), afi.initMethod)))); } @Override protected void implementNotNullAdd(AggContext info, AggAddContext add) { List acc = add.accumulator(); List aggArgs = add.arguments(); List args = new ArrayList<>(aggArgs.size() + 1); args.add(acc.get(0)); args.addAll(aggArgs); add.currentBlock().add( Expressions.statement( Expressions.assign(acc.get(0), Expressions.call(afi.isStatic ? null : acc.get(1), afi.addMethod, args)))); } @Override protected Expression implementNotNullResult(AggContext info, AggResultContext result) { List acc = result.accumulator(); return Expressions.call( afi.isStatic ? null : acc.get(1), afi.resultMethod, acc.get(0)); } } /** Implementor for the {@code RANK} windowed aggregate function. */ static class RankImplementor extends StrictWinAggImplementor { @Override protected void implementNotNullAdd(WinAggContext info, WinAggAddContext add) { Expression acc = add.accumulator().get(0); // This is an example of the generated code if (false) { new Object() { int curentPosition; // position in for-win-agg-loop int startIndex; // index of start of window Comparable[] rows; // accessed via WinAggAddContext.com.hazelcast.com.areRows { if (curentPosition > startIndex) { if (rows[curentPosition - 1].com.hazelcast.com.areTo(rows[curentPosition]) > 0) { // update rank } } } }; } BlockBuilder builder = add.nestBlock(); add.currentBlock().add( Expressions.ifThen( Expressions.lessThan( add.com.hazelcast.com.areRows( Expressions.subtract(add.currentPosition(), Expressions.constant(1)), add.currentPosition()), Expressions.constant(0)), Expressions.statement( Expressions.assign(acc, com.hazelcast.com.uteNewRank(acc, add))))); add.exitBlock(); add.currentBlock().add( Expressions.ifThen( Expressions.greaterThan(add.currentPosition(), add.startIndex()), builder.toBlock())); } protected Expression com.hazelcast.com.uteNewRank(Expression acc, WinAggAddContext add) { Expression pos = add.currentPosition(); if (!add.startIndex().equals(Expressions.constant(0))) { // In general, currentPosition-startIndex should be used // However, rank/dense_rank does not allow preceding/following clause // so we always result in startIndex==0. pos = Expressions.subtract(pos, add.startIndex()); } return pos; } @Override protected Expression implementNotNullResult( WinAggContext info, WinAggResultContext result) { // Rank is 1-based return Expressions.add(super.implementNotNullResult(info, result), Expressions.constant(1)); } } /** Implementor for the {@code DENSE_RANK} windowed aggregate function. */ static class DenseRankImplementor extends RankImplementor { @Override protected Expression com.hazelcast.com.uteNewRank(Expression acc, WinAggAddContext add) { return Expressions.add(acc, Expressions.constant(1)); } } /** Implementor for the {@code FIRST_VALUE} and {@code LAST_VALUE} * windowed aggregate functions. */ static class FirstLastValueImplementor implements WinAggImplementor { private final SeekType seekType; protected FirstLastValueImplementor(SeekType seekType) { this.seekType = seekType; } public List getStateType(AggContext info) { return Collections.emptyList(); } public void implementReset(AggContext info, AggResetContext reset) { // no op } public void implementAdd(AggContext info, AggAddContext add) { // no op } public boolean needCacheWhenFrameIntact() { return true; } public Expression implementResult(AggContext info, AggResultContext result) { WinAggResultContext winResult = (WinAggResultContext) result; return Expressions.condition(winResult.hasRows(), winResult.rowTranslator( winResult.com.hazelcast.com.uteIndex(Expressions.constant(0), seekType)) .translate(winResult.rexArguments().get(0), info.returnType()), getDefaultValue(info.returnType())); } } /** Implementor for the {@code FIRST_VALUE} windowed aggregate function. */ static class FirstValueImplementor extends FirstLastValueImplementor { protected FirstValueImplementor() { super(SeekType.START); } } /** Implementor for the {@code LAST_VALUE} windowed aggregate function. */ static class LastValueImplementor extends FirstLastValueImplementor { protected LastValueImplementor() { super(SeekType.END); } } /** Implementor for the {@code NTH_VALUE} * windowed aggregate function. */ static class NthValueImplementor implements WinAggImplementor { public List getStateType(AggContext info) { return Collections.emptyList(); } public void implementReset(AggContext info, AggResetContext reset) { // no op } public void implementAdd(AggContext info, AggAddContext add) { // no op } public boolean needCacheWhenFrameIntact() { return true; } public Expression implementResult(AggContext info, AggResultContext result) { WinAggResultContext winResult = (WinAggResultContext) result; List rexArgs = winResult.rexArguments(); ParameterExpression res = Expressions.parameter(0, info.returnType(), result.currentBlock().newName("nth")); RexToLixTranslator currentRowTranslator = winResult.rowTranslator( winResult.com.hazelcast.com.uteIndex(Expressions.constant(0), SeekType.START)); Expression dstIndex = winResult.com.hazelcast.com.uteIndex( Expressions.subtract( currentRowTranslator.translate(rexArgs.get(1), int.class), Expressions.constant(1)), SeekType.START); Expression rowInRange = winResult.rowInPartition(dstIndex); BlockBuilder thenBlock = result.nestBlock(); Expression nthValue = winResult.rowTranslator(dstIndex) .translate(rexArgs.get(0), res.type); thenBlock.add(Expressions.statement(Expressions.assign(res, nthValue))); result.exitBlock(); BlockStatement thenBranch = thenBlock.toBlock(); Expression defaultValue = getDefaultValue(res.type); result.currentBlock().add(Expressions.declare(0, res, null)); result.currentBlock().add( Expressions.ifThenElse(rowInRange, thenBranch, Expressions.statement(Expressions.assign(res, defaultValue)))); return res; } } /** Implementor for the {@code LEAD} and {@code LAG} windowed * aggregate functions. */ static class LeadLagImplementor implements WinAggImplementor { private final boolean isLead; protected LeadLagImplementor(boolean isLead) { this.isLead = isLead; } public List getStateType(AggContext info) { return Collections.emptyList(); } public void implementReset(AggContext info, AggResetContext reset) { // no op } public void implementAdd(AggContext info, AggAddContext add) { // no op } public boolean needCacheWhenFrameIntact() { return false; } public Expression implementResult(AggContext info, AggResultContext result) { WinAggResultContext winResult = (WinAggResultContext) result; List rexArgs = winResult.rexArguments(); ParameterExpression res = Expressions.parameter(0, info.returnType(), result.currentBlock().newName(isLead ? "lead" : "lag")); Expression offset; RexToLixTranslator currentRowTranslator = winResult.rowTranslator( winResult.com.hazelcast.com.uteIndex(Expressions.constant(0), SeekType.SET)); if (rexArgs.size() >= 2) { // lead(x, offset) or lead(x, offset, default) offset = currentRowTranslator.translate( rexArgs.get(1), int.class); } else { offset = Expressions.constant(1); } if (!isLead) { offset = Expressions.negate(offset); } Expression dstIndex = winResult.com.hazelcast.com.uteIndex(offset, SeekType.SET); Expression rowInRange = winResult.rowInPartition(dstIndex); BlockBuilder thenBlock = result.nestBlock(); Expression lagResult = winResult.rowTranslator(dstIndex).translate( rexArgs.get(0), res.type); thenBlock.add(Expressions.statement(Expressions.assign(res, lagResult))); result.exitBlock(); BlockStatement thenBranch = thenBlock.toBlock(); Expression defaultValue = rexArgs.size() == 3 ? currentRowTranslator.translate(rexArgs.get(2), res.type) : getDefaultValue(res.type); result.currentBlock().add(Expressions.declare(0, res, null)); result.currentBlock().add( Expressions.ifThenElse(rowInRange, thenBranch, Expressions.statement(Expressions.assign(res, defaultValue)))); return res; } } /** Implementor for the {@code LEAD} windowed aggregate function. */ public static class LeadImplementor extends LeadLagImplementor { protected LeadImplementor() { super(true); } } /** Implementor for the {@code LAG} windowed aggregate function. */ public static class LagImplementor extends LeadLagImplementor { protected LagImplementor() { super(false); } } /** Implementor for the {@code NTILE} windowed aggregate function. */ static class NtileImplementor implements WinAggImplementor { public List getStateType(AggContext info) { return Collections.emptyList(); } public void implementReset(AggContext info, AggResetContext reset) { // no op } public void implementAdd(AggContext info, AggAddContext add) { // no op } public boolean needCacheWhenFrameIntact() { return false; } public Expression implementResult(AggContext info, AggResultContext result) { WinAggResultContext winResult = (WinAggResultContext) result; List rexArgs = winResult.rexArguments(); Expression tiles = winResult.rowTranslator(winResult.index()).translate( rexArgs.get(0), int.class); Expression ntile = Expressions.add(Expressions.constant(1), Expressions.divide( Expressions.multiply( tiles, Expressions.subtract( winResult.index(), winResult.startIndex())), winResult.getPartitionRowCount())); return ntile; } } /** Implementor for the {@code ROW_NUMBER} windowed aggregate function. */ static class RowNumberImplementor extends StrictWinAggImplementor { @Override public List getNotNullState(WinAggContext info) { return Collections.emptyList(); } @Override protected void implementNotNullAdd(WinAggContext info, WinAggAddContext add) { // no op } @Override protected Expression implementNotNullResult( WinAggContext info, WinAggResultContext result) { // Window cannot be empty since ROWS/RANGE is not possible for ROW_NUMBER return Expressions.add( Expressions.subtract(result.index(), result.startIndex()), Expressions.constant(1)); } } /** Implementor for the {@code JSON_OBJECTAGG} aggregate function. */ static class JsonObjectAggImplementor implements AggImplementor { private final Method m; JsonObjectAggImplementor(Method m) { this.m = m; } static Supplier supplierFor(Method m) { return () -> new JsonObjectAggImplementor(m); } @Override public List getStateType(AggContext info) { return Collections.singletonList(Map.class); } @Override public void implementReset(AggContext info, AggResetContext reset) { reset.currentBlock().add( Expressions.statement( Expressions.assign(reset.accumulator().get(0), Expressions.new_(HashMap.class)))); } @Override public void implementAdd(AggContext info, AggAddContext add) { final SqlJsonObjectAggAggFunction function = (SqlJsonObjectAggAggFunction) info.aggregation(); add.currentBlock().add( Expressions.statement( Expressions.call(m, Iterables.concat( Collections.singletonList(add.accumulator().get(0)), add.arguments(), Collections.singletonList( Expressions.constant(function.getNullClause())))))); } @Override public Expression implementResult(AggContext info, AggResultContext result) { return Expressions.call(BuiltInMethod.JSONIZE.method, result.accumulator().get(0)); } } /** Implementor for the {@code JSON_ARRAYAGG} aggregate function. */ static class JsonArrayAggImplementor implements AggImplementor { private final Method m; JsonArrayAggImplementor(Method m) { this.m = m; } static Supplier supplierFor(Method m) { return () -> new JsonArrayAggImplementor(m); } @Override public List getStateType(AggContext info) { return Collections.singletonList(List.class); } @Override public void implementReset(AggContext info, AggResetContext reset) { reset.currentBlock().add( Expressions.statement( Expressions.assign(reset.accumulator().get(0), Expressions.new_(ArrayList.class)))); } @Override public void implementAdd(AggContext info, AggAddContext add) { final SqlJsonArrayAggAggFunction function = (SqlJsonArrayAggAggFunction) info.aggregation(); add.currentBlock().add( Expressions.statement( Expressions.call(m, Iterables.concat( Collections.singletonList(add.accumulator().get(0)), add.arguments(), Collections.singletonList( Expressions.constant(function.getNullClause())))))); } @Override public Expression implementResult(AggContext info, AggResultContext result) { return Expressions.call(BuiltInMethod.JSONIZE.method, result.accumulator().get(0)); } } /** Implementor for the {@code TRIM} function. */ private static class TrimImplementor implements NotNullImplementor { public Expression implement(RexToLixTranslator translator, RexCall call, List translatedOperands) { final boolean strict = !translator.conformance.allowExtendedTrim(); final Object value = ((ConstantExpression) translatedOperands.get(0)).value; SqlTrimFunction.Flag flag = (SqlTrimFunction.Flag) value; return Expressions.call( BuiltInMethod.TRIM.method, Expressions.constant( flag == SqlTrimFunction.Flag.BOTH || flag == SqlTrimFunction.Flag.LEADING), Expressions.constant( flag == SqlTrimFunction.Flag.BOTH || flag == SqlTrimFunction.Flag.TRAILING), translatedOperands.get(1), translatedOperands.get(2), Expressions.constant(strict)); } } /** Implementor for the {@code MONTHNAME} and {@code DAYNAME} functions. * Each takes a {@link java.util.Locale} argument. */ private static class PeriodNameImplementor extends MethodNameImplementor { private final BuiltInMethod timestampMethod; private final BuiltInMethod dateMethod; PeriodNameImplementor(String methodName, BuiltInMethod timestampMethod, BuiltInMethod dateMethod) { super(methodName); this.timestampMethod = timestampMethod; this.dateMethod = dateMethod; } @Override public Expression implement(RexToLixTranslator translator, RexCall call, List translatedOperands) { Expression operand = translatedOperands.get(0); final RelDataType type = call.operands.get(0).getType(); switch (type.getSqlTypeName()) { case TIMESTAMP: return getExpression(translator, operand, timestampMethod); case DATE: return getExpression(translator, operand, dateMethod); default: throw new AssertionError("unknown type " + type); } } protected Expression getExpression(RexToLixTranslator translator, Expression operand, BuiltInMethod builtInMethod) { final MethodCallExpression locale = Expressions.call(BuiltInMethod.LOCALE.method, translator.getRoot()); return Expressions.call(builtInMethod.method.getDeclaringClass(), builtInMethod.method.getName(), operand, locale); } } /** Implementor for the {@code FLOOR} and {@code CEIL} functions. */ private static class FloorImplementor extends MethodNameImplementor { final Method timestampMethod; final Method dateMethod; FloorImplementor(String methodName, Method timestampMethod, Method dateMethod) { super(methodName); this.timestampMethod = timestampMethod; this.dateMethod = dateMethod; } public Expression implement(RexToLixTranslator translator, RexCall call, List translatedOperands) { switch (call.getOperands().size()) { case 1: switch (call.getType().getSqlTypeName()) { case BIGINT: case INTEGER: case SMALLINT: case TINYINT: return translatedOperands.get(0); default: return super.implement(translator, call, translatedOperands); } case 2: final Type type; final Method floorMethod; final boolean preFloor; Expression operand = translatedOperands.get(0); switch (call.getType().getSqlTypeName()) { case TIMESTAMP_WITH_LOCAL_TIME_ZONE: operand = Expressions.call( BuiltInMethod.TIMESTAMP_WITH_LOCAL_TIME_ZONE_TO_TIMESTAMP.method, operand, Expressions.call(BuiltInMethod.TIME_ZONE.method, translator.getRoot())); // fall through case TIMESTAMP: type = long.class; floorMethod = timestampMethod; preFloor = true; break; default: type = int.class; floorMethod = dateMethod; preFloor = false; } final ConstantExpression tur = (ConstantExpression) translatedOperands.get(1); final TimeUnitRange timeUnitRange = (TimeUnitRange) tur.value; switch (timeUnitRange) { case YEAR: case QUARTER: case MONTH: case WEEK: case DAY: final Expression operand1 = preFloor ? call(operand, type, TimeUnit.DAY) : operand; return Expressions.call(floorMethod, tur, operand1); case NANOSECOND: default: return call(operand, type, timeUnitRange.startUnit); } default: throw new AssertionError(); } } private Expression call(Expression operand, Type type, TimeUnit timeUnit) { return Expressions.call(SqlFunctions.class, methodName, EnumUtils.convert(operand, type), EnumUtils.convert( Expressions.constant(timeUnit.multiplier), type)); } } /** Implementor for a function that generates calls to a given method. */ private static class MethodImplementor implements NotNullImplementor { protected final Method method; MethodImplementor(Method method) { this.method = method; } public Expression implement( RexToLixTranslator translator, RexCall call, List translatedOperands) { final Expression expression; Class clazz = method.getDeclaringClass(); if (Modifier.isStatic(method.getModifiers())) { expression = EnumUtils.call(clazz, method.getName(), translatedOperands); } else { expression = EnumUtils.call(clazz, method.getName(), Util.skip(translatedOperands, 1), translatedOperands.get(0)); } final Type returnType = translator.typeFactory.getJavaClass(call.getType()); return EnumUtils.convert(expression, returnType); } } /** * Implementor for JSON_VALUE function, convert to solid format * "JSON_VALUE(json_doc, path, empty_behavior, empty_default, error_behavior, error default)" * in order to simplify the runtime implementation. * *

We should avoid this when we support * variable arguments function. */ private static class JsonValueImplementor extends MethodImplementor { JsonValueImplementor(Method method) { super(method); } public Expression implement( RexToLixTranslator translator, RexCall call, List translatedOperands) { final Expression expression; final List newOperands = new ArrayList<>(); newOperands.add(translatedOperands.get(0)); newOperands.add(translatedOperands.get(1)); List leftExprs = Util.skip(translatedOperands, 2); // Default value for JSON_VALUE behaviors. Expression emptyBehavior = Expressions.constant(SqlJsonValueEmptyOrErrorBehavior.NULL); Expression defaultValueOnEmpty = Expressions.constant(null); Expression errorBehavior = Expressions.constant(SqlJsonValueEmptyOrErrorBehavior.NULL); Expression defaultValueOnError = Expressions.constant(null); // Patched up with user defines. if (leftExprs.size() > 0) { for (int i = 0; i < leftExprs.size(); i++) { Expression expr = leftExprs.get(i); if (expr instanceof ConstantExpression) { final ConstantExpression constExpr = (ConstantExpression) expr; final Object exprVal = constExpr.value; int defaultSymbolIdx = i - 2; if (exprVal == SqlJsonEmptyOrError.EMPTY) { if (defaultSymbolIdx >= 0 && unwrapConstExprVal(leftExprs.get(defaultSymbolIdx)) == SqlJsonValueEmptyOrErrorBehavior.DEFAULT) { defaultValueOnEmpty = leftExprs.get(i - 1); emptyBehavior = leftExprs.get(defaultSymbolIdx); } else { emptyBehavior = leftExprs.get(i - 1); } } else if (exprVal == SqlJsonEmptyOrError.ERROR) { if (defaultSymbolIdx >= 0 && unwrapConstExprVal(leftExprs.get(defaultSymbolIdx)) == SqlJsonValueEmptyOrErrorBehavior.DEFAULT) { defaultValueOnError = leftExprs.get(i - 1); errorBehavior = leftExprs.get(defaultSymbolIdx); } else { errorBehavior = leftExprs.get(i - 1); } } } } } newOperands.add(emptyBehavior); newOperands.add(defaultValueOnEmpty); newOperands.add(errorBehavior); newOperands.add(defaultValueOnError); Class clazz = method.getDeclaringClass(); expression = EnumUtils.call(clazz, method.getName(), newOperands); final Type returnType = translator.typeFactory.getJavaClass(call.getType()); return EnumUtils.convert(expression, returnType); } } private static Object unwrapConstExprVal(Expression expression) { if (expression instanceof ConstantExpression) { return ((ConstantExpression) expression).value; } return null; } /** Implementor for SQL functions that generates calls to a given method name. * *

Use this, as opposed to {@link MethodImplementor}, if the SQL function * is overloaded; then you can use one implementor for several overloads. */ private static class MethodNameImplementor implements NotNullImplementor { protected final String methodName; MethodNameImplementor(String methodName) { this.methodName = methodName; } public Expression implement( RexToLixTranslator translator, RexCall call, List translatedOperands) { return EnumUtils.call( SqlFunctions.class, methodName, translatedOperands); } } /** Implementor for binary operators. */ private static class BinaryImplementor implements NotNullImplementor { /** Types that can be arguments to com.hazelcast.com.arison operators such as * {@code <}. */ private static final List COMP_OP_TYPES = ImmutableList.of( Primitive.BYTE, Primitive.CHAR, Primitive.SHORT, Primitive.INT, Primitive.LONG, Primitive.FLOAT, Primitive.DOUBLE); private static final List COMPARISON_OPERATORS = ImmutableList.of( SqlStdOperatorTable.LESS_THAN, SqlStdOperatorTable.LESS_THAN_OR_EQUAL, SqlStdOperatorTable.GREATER_THAN, SqlStdOperatorTable.GREATER_THAN_OR_EQUAL); private static final List EQUALS_OPERATORS = ImmutableList.of( SqlStdOperatorTable.EQUALS, SqlStdOperatorTable.NOT_EQUALS); public static final String METHOD_POSTFIX_FOR_ANY_TYPE = "Any"; private final ExpressionType expressionType; private final String backupMethodName; BinaryImplementor( ExpressionType expressionType, String backupMethodName) { this.expressionType = expressionType; this.backupMethodName = backupMethodName; } public Expression implement( RexToLixTranslator translator, RexCall call, List expressions) { // neither nullable: // return x OP y // x nullable // null_returns_null // return x == null ? null : x OP y // ignore_null // return x == null ? null : y // x, y both nullable // null_returns_null // return x == null || y == null ? null : x OP y // ignore_null // return x == null ? y : y == null ? x : x OP y if (backupMethodName != null) { // If one or both operands have ANY type, use the late-binding backup // method. if (anyAnyOperands(call)) { return callBackupMethodAnyType(translator, call, expressions); } final Type type0 = expressions.get(0).getType(); final Type type1 = expressions.get(1).getType(); final SqlBinaryOperator op = (SqlBinaryOperator) call.getOperator(); final Primitive primitive = Primitive.ofBoxOr(type0); if (primitive == null || type1 == BigDecimal.class || COMPARISON_OPERATORS.contains(op) && !COMP_OP_TYPES.contains(primitive)) { return Expressions.call(SqlFunctions.class, backupMethodName, expressions); } // When checking equals or not equals on two primitive boxing classes // (i.e. Long x, Long y), we should fall back to call `SqlFunctions.eq(x, y)` // or `SqlFunctions.ne(x, y)`, rather than `x == y` final Primitive boxPrimitive0 = Primitive.ofBox(type0); final Primitive boxPrimitive1 = Primitive.ofBox(type1); if (EQUALS_OPERATORS.contains(op) && boxPrimitive0 != null && boxPrimitive1 != null) { return Expressions.call(SqlFunctions.class, backupMethodName, expressions); } } final Type returnType = translator.typeFactory.getJavaClass(call.getType()); return EnumUtils.convert( Expressions.makeBinary(expressionType, expressions.get(0), expressions.get(1)), returnType); } /** Returns whether any of a call's operands have ANY type. */ private boolean anyAnyOperands(RexCall call) { for (RexNode operand : call.operands) { if (operand.getType().getSqlTypeName() == SqlTypeName.ANY) { return true; } } return false; } private Expression callBackupMethodAnyType(RexToLixTranslator translator, RexCall call, List expressions) { final String backupMethodNameForAnyType = backupMethodName + METHOD_POSTFIX_FOR_ANY_TYPE; // one or both of parameter(s) is(are) ANY type final Expression expression0 = maybeBox(expressions.get(0)); final Expression expression1 = maybeBox(expressions.get(1)); return Expressions.call(SqlFunctions.class, backupMethodNameForAnyType, expression0, expression1); } private Expression maybeBox(Expression expression) { final Primitive primitive = Primitive.of(expression.getType()); if (primitive != null) { expression = Expressions.box(expression, primitive); } return expression; } } /** Implementor for unary operators. */ private static class UnaryImplementor implements NotNullImplementor { private final ExpressionType expressionType; UnaryImplementor(ExpressionType expressionType) { this.expressionType = expressionType; } public Expression implement( RexToLixTranslator translator, RexCall call, List translatedOperands) { final Expression operand = translatedOperands.get(0); final UnaryExpression e = Expressions.makeUnary(expressionType, operand); if (e.type.equals(operand.type)) { return e; } // Certain unary operators do not preserve type. For example, the "-" // operator applied to a "byte" expression returns an "int". return Expressions.convert_(e, operand.type); } } /** Implementor for the {@code EXTRACT(unit FROM datetime)} function. */ private static class ExtractImplementor implements NotNullImplementor { public Expression implement(RexToLixTranslator translator, RexCall call, List translatedOperands) { final TimeUnitRange timeUnitRange = (TimeUnitRange) ((ConstantExpression) translatedOperands.get(0)).value; final TimeUnit unit = timeUnitRange.startUnit; Expression operand = translatedOperands.get(1); final SqlTypeName sqlTypeName = call.operands.get(1).getType().getSqlTypeName(); switch (unit) { case MILLENNIUM: case CENTURY: case YEAR: case QUARTER: case MONTH: case DAY: case DOW: case DECADE: case DOY: case ISODOW: case ISOYEAR: case WEEK: switch (sqlTypeName) { case INTERVAL_YEAR: case INTERVAL_YEAR_MONTH: case INTERVAL_MONTH: case INTERVAL_DAY: case INTERVAL_DAY_HOUR: case INTERVAL_DAY_MINUTE: case INTERVAL_DAY_SECOND: case INTERVAL_HOUR: case INTERVAL_HOUR_MINUTE: case INTERVAL_HOUR_SECOND: case INTERVAL_MINUTE: case INTERVAL_MINUTE_SECOND: case INTERVAL_SECOND: break; case TIMESTAMP_WITH_LOCAL_TIME_ZONE: operand = Expressions.call( BuiltInMethod.TIMESTAMP_WITH_LOCAL_TIME_ZONE_TO_TIMESTAMP.method, operand, Expressions.call(BuiltInMethod.TIME_ZONE.method, translator.getRoot())); // fall through case TIMESTAMP: operand = Expressions.call(BuiltInMethod.FLOOR_DIV.method, operand, Expressions.constant(TimeUnit.DAY.multiplier.longValue())); // fall through case DATE: return Expressions.call(BuiltInMethod.UNIX_DATE_EXTRACT.method, translatedOperands.get(0), operand); default: throw new AssertionError("unexpected " + sqlTypeName); } break; case MILLISECOND: case MICROSECOND: case NANOSECOND: if (sqlTypeName == SqlTypeName.DATE) { return Expressions.constant(0L); } operand = mod(operand, TimeUnit.MINUTE.multiplier.longValue()); return Expressions.multiply( operand, Expressions.constant((long) (1 / unit.multiplier.doubleValue()))); case EPOCH: switch (sqlTypeName) { case DATE: // convert to milliseconds operand = Expressions.multiply(operand, Expressions.constant(TimeUnit.DAY.multiplier.longValue())); // fall through case TIMESTAMP: // convert to seconds return Expressions.divide(operand, Expressions.constant(TimeUnit.SECOND.multiplier.longValue())); case TIMESTAMP_WITH_LOCAL_TIME_ZONE: operand = Expressions.call( BuiltInMethod.TIMESTAMP_WITH_LOCAL_TIME_ZONE_TO_TIMESTAMP.method, operand, Expressions.call(BuiltInMethod.TIME_ZONE.method, translator.getRoot())); return Expressions.divide(operand, Expressions.constant(TimeUnit.SECOND.multiplier.longValue())); case INTERVAL_YEAR: case INTERVAL_YEAR_MONTH: case INTERVAL_MONTH: case INTERVAL_DAY: case INTERVAL_DAY_HOUR: case INTERVAL_DAY_MINUTE: case INTERVAL_DAY_SECOND: case INTERVAL_HOUR: case INTERVAL_HOUR_MINUTE: case INTERVAL_HOUR_SECOND: case INTERVAL_MINUTE: case INTERVAL_MINUTE_SECOND: case INTERVAL_SECOND: // no convertlet conversion, pass it as extract throw new AssertionError("unexpected " + sqlTypeName); } break; case HOUR: case MINUTE: case SECOND: switch (sqlTypeName) { case DATE: return Expressions.multiply(operand, Expressions.constant(0L)); } break; } operand = mod(operand, getFactor(unit)); if (unit == TimeUnit.QUARTER) { operand = Expressions.subtract(operand, Expressions.constant(1L)); } operand = Expressions.divide(operand, Expressions.constant(unit.multiplier.longValue())); if (unit == TimeUnit.QUARTER) { operand = Expressions.add(operand, Expressions.constant(1L)); } return operand; } } private static Expression mod(Expression operand, long factor) { if (factor == 1L) { return operand; } else { return Expressions.call(BuiltInMethod.FLOOR_MOD.method, operand, Expressions.constant(factor)); } } private static long getFactor(TimeUnit unit) { switch (unit) { case DAY: return 1L; case HOUR: return TimeUnit.DAY.multiplier.longValue(); case MINUTE: return TimeUnit.HOUR.multiplier.longValue(); case SECOND: return TimeUnit.MINUTE.multiplier.longValue(); case MILLISECOND: return TimeUnit.SECOND.multiplier.longValue(); case MONTH: return TimeUnit.YEAR.multiplier.longValue(); case QUARTER: return TimeUnit.YEAR.multiplier.longValue(); case YEAR: case DECADE: case CENTURY: case MILLENNIUM: return 1L; default: throw Util.unexpected(unit); } } /** Implementor for the SQL {@code CASE} operator. */ private static class CaseImplementor implements CallImplementor { public Expression implement(RexToLixTranslator translator, RexCall call, NullAs nullAs) { return implementRecurse(translator, call, nullAs, 0); } private Expression implementRecurse(RexToLixTranslator translator, RexCall call, NullAs nullAs, int i) { List operands = call.getOperands(); if (i == operands.size() - 1) { // the "else" clause return translator.translate( translator.builder.ensureType( call.getType(), operands.get(i), false), nullAs); } else { Expression ifTrue; try { ifTrue = translator.translate( translator.builder.ensureType(call.getType(), operands.get(i + 1), false), nullAs); } catch (RexToLixTranslator.AlwaysNull e) { ifTrue = null; } Expression ifFalse; try { ifFalse = implementRecurse(translator, call, nullAs, i + 2); } catch (RexToLixTranslator.AlwaysNull e) { if (ifTrue == null) { throw RexToLixTranslator.AlwaysNull.INSTANCE; } ifFalse = null; } Expression test = translator.translate(operands.get(i), NullAs.FALSE); return ifTrue == null || ifFalse == null ? Util.first(ifTrue, ifFalse) : Expressions.condition(test, ifTrue, ifFalse); } } } /** Implementor for the SQL {@code COALESCE} operator. */ private static class CoalesceImplementor implements CallImplementor { public Expression implement(RexToLixTranslator translator, RexCall call, NullAs nullAs) { return implementRecurse(translator, call.operands, nullAs); } private Expression implementRecurse(RexToLixTranslator translator, List operands, NullAs nullAs) { if (operands.size() == 1) { return translator.translate(operands.get(0), nullAs); } else { return Expressions.condition( translator.translate(operands.get(0), NullAs.IS_NOT_NULL), translator.translate(operands.get(0), nullAs), implementRecurse(translator, Util.skip(operands), nullAs)); } } } /** Implementor for the SQL {@code CAST} function that optimizes if, say, the * argument is already of the desired type. */ private static class CastOptimizedImplementor implements CallImplementor { private final CallImplementor accurate; private CastOptimizedImplementor() { accurate = createImplementor(new CastImplementor(), NullPolicy.STRICT, false); } public Expression implement(RexToLixTranslator translator, RexCall call, NullAs nullAs) { // Short-circuit if no cast is required RexNode arg = call.getOperands().get(0); if (call.getType().equals(arg.getType())) { // No cast required, omit cast return translator.translate(arg, nullAs); } if (SqlTypeUtil.equalSansNullability(translator.typeFactory, call.getType(), arg.getType()) && nullAs == NullAs.NULL && translator.deref(arg) instanceof RexLiteral) { return RexToLixTranslator.translateLiteral( (RexLiteral) translator.deref(arg), call.getType(), translator.typeFactory, nullAs); } return accurate.implement(translator, call, nullAs); } } /** Implementor for the SQL {@code CAST} operator. */ private static class CastImplementor implements NotNullImplementor { public Expression implement( RexToLixTranslator translator, RexCall call, List translatedOperands) { assert call.getOperands().size() == 1; final RelDataType sourceType = call.getOperands().get(0).getType(); // It's only possible for the result to be null if both expression // and target type are nullable. We assume that the caller did not // make a mistake. If expression looks nullable, caller WILL have // checked that expression is not null before calling us. final boolean nullable = translator.isNullable(call) && sourceType.isNullable() && !Primitive.is(translatedOperands.get(0).getType()); final RelDataType targetType = translator.nullifyType(call.getType(), nullable); return translator.translateCast(sourceType, targetType, translatedOperands.get(0)); } } /** Implementor for the {@code REINTERPRET} internal SQL operator. */ private static class ReinterpretImplementor implements NotNullImplementor { public Expression implement( RexToLixTranslator translator, RexCall call, List translatedOperands) { assert call.getOperands().size() == 1; return translatedOperands.get(0); } } /** Implementor for a value-constructor. */ private static class ValueConstructorImplementor implements CallImplementor { public Expression implement( RexToLixTranslator translator, RexCall call, NullAs nullAs) { return translator.translateConstructor(call.getOperands(), call.getOperator().getKind()); } } /** Implementor for the {@code ITEM} SQL operator. */ private static class ItemImplementor implements CallImplementor { public Expression implement( RexToLixTranslator translator, RexCall call, NullAs nullAs) { final MethodImplementor implementor = getImplementor( call.getOperands().get(0).getType().getSqlTypeName()); // Since we follow PostgreSQL's semantics that an out-of-bound reference // returns NULL, x[y] can return null even if x and y are both NOT NULL. // (In SQL standard semantics, an out-of-bound reference to an array // throws an exception.) final NullPolicy nullPolicy = NullPolicy.ANY; return implementNullSemantics0(translator, call, nullAs, nullPolicy, false, implementor); } private MethodImplementor getImplementor(SqlTypeName sqlTypeName) { switch (sqlTypeName) { case ARRAY: return new MethodImplementor(BuiltInMethod.ARRAY_ITEM.method); case MAP: return new MethodImplementor(BuiltInMethod.MAP_ITEM.method); default: return new MethodImplementor(BuiltInMethod.ANY_ITEM.method); } } } /** Implementor for SQL system functions. * *

Several of these are represented internally as constant values, set * per execution. */ private static class SystemFunctionImplementor implements CallImplementor { public Expression implement( RexToLixTranslator translator, RexCall call, NullAs nullAs) { switch (nullAs) { case IS_NULL: return Expressions.constant(false); case IS_NOT_NULL: return Expressions.constant(true); } final SqlOperator op = call.getOperator(); final Expression root = translator.getRoot(); if (op == CURRENT_USER || op == SESSION_USER || op == USER) { return Expressions.call(BuiltInMethod.USER.method, root); } else if (op == SYSTEM_USER) { return Expressions.call(BuiltInMethod.SYSTEM_USER.method, root); } else if (op == CURRENT_PATH || op == CURRENT_ROLE || op == CURRENT_CATALOG) { // By default, the CURRENT_ROLE and CURRENT_CATALOG functions return the // empty string because a role or a catalog has to be set explicitly. return Expressions.constant(""); } else if (op == CURRENT_TIMESTAMP) { return Expressions.call(BuiltInMethod.CURRENT_TIMESTAMP.method, root); } else if (op == CURRENT_TIME) { return Expressions.call(BuiltInMethod.CURRENT_TIME.method, root); } else if (op == CURRENT_DATE) { return Expressions.call(BuiltInMethod.CURRENT_DATE.method, root); } else if (op == LOCALTIMESTAMP) { return Expressions.call(BuiltInMethod.LOCAL_TIMESTAMP.method, root); } else if (op == LOCALTIME) { return Expressions.call(BuiltInMethod.LOCAL_TIME.method, root); } else { throw new AssertionError("unknown function " + op); } } } /** Implements "IS XXX" operations such as "IS NULL" * or "IS NOT TRUE". * *

What these operators have in com.hazelcast.com.on:

* 1. They return TRUE or FALSE, never NULL. * 2. Of the 3 input values (TRUE, FALSE, NULL) they return TRUE for 1 or 2, * FALSE for the other 2 or 1. */ private static class IsXxxImplementor implements CallImplementor { private final Boolean seek; private final boolean negate; IsXxxImplementor(Boolean seek, boolean negate) { this.seek = seek; this.negate = negate; } public Expression implement( RexToLixTranslator translator, RexCall call, NullAs nullAs) { List operands = call.getOperands(); assert operands.size() == 1; switch (nullAs) { case IS_NOT_NULL: return BOXED_TRUE_EXPR; case IS_NULL: return BOXED_FALSE_EXPR; } if (seek == null) { return translator.translate(operands.get(0), negate ? NullAs.IS_NOT_NULL : NullAs.IS_NULL); } else { return maybeNegate(negate == seek, translator.translate(operands.get(0), seek ? NullAs.FALSE : NullAs.TRUE)); } } } /** Implementor for the {@code NOT} operator. */ private static class NotImplementor implements NotNullImplementor { private final NotNullImplementor implementor; NotImplementor(NotNullImplementor implementor) { this.implementor = implementor; } private static NotNullImplementor of(NotNullImplementor implementor) { return new NotImplementor(implementor); } public Expression implement( RexToLixTranslator translator, RexCall call, List translatedOperands) { final Expression expression = implementor.implement(translator, call, translatedOperands); return Expressions.not(expression); } } /** Implementor for various datetime arithmetic. */ private static class DatetimeArithmeticImplementor implements NotNullImplementor { public Expression implement(RexToLixTranslator translator, RexCall call, List translatedOperands) { final RexNode operand0 = call.getOperands().get(0); Expression trop0 = translatedOperands.get(0); final SqlTypeName typeName1 = call.getOperands().get(1).getType().getSqlTypeName(); Expression trop1 = translatedOperands.get(1); final SqlTypeName typeName = call.getType().getSqlTypeName(); switch (operand0.getType().getSqlTypeName()) { case DATE: switch (typeName) { case TIMESTAMP: trop0 = Expressions.convert_( Expressions.multiply(trop0, Expressions.constant(DateTimeUtils.MILLIS_PER_DAY)), long.class); break; default: switch (typeName1) { case INTERVAL_DAY: case INTERVAL_DAY_HOUR: case INTERVAL_DAY_MINUTE: case INTERVAL_DAY_SECOND: case INTERVAL_HOUR: case INTERVAL_HOUR_MINUTE: case INTERVAL_HOUR_SECOND: case INTERVAL_MINUTE: case INTERVAL_MINUTE_SECOND: case INTERVAL_SECOND: trop1 = Expressions.convert_( Expressions.divide(trop1, Expressions.constant(DateTimeUtils.MILLIS_PER_DAY)), int.class); } } break; case TIME: trop1 = Expressions.convert_(trop1, int.class); break; } switch (typeName1) { case INTERVAL_YEAR: case INTERVAL_YEAR_MONTH: case INTERVAL_MONTH: switch (call.getKind()) { case MINUS: trop1 = Expressions.negate(trop1); } switch (typeName) { case TIME: return Expressions.convert_(trop0, long.class); default: final BuiltInMethod method = operand0.getType().getSqlTypeName() == SqlTypeName.TIMESTAMP ? BuiltInMethod.ADD_MONTHS : BuiltInMethod.ADD_MONTHS_INT; return Expressions.call(method.method, trop0, trop1); } case INTERVAL_DAY: case INTERVAL_DAY_HOUR: case INTERVAL_DAY_MINUTE: case INTERVAL_DAY_SECOND: case INTERVAL_HOUR: case INTERVAL_HOUR_MINUTE: case INTERVAL_HOUR_SECOND: case INTERVAL_MINUTE: case INTERVAL_MINUTE_SECOND: case INTERVAL_SECOND: switch (call.getKind()) { case MINUS: return normalize(typeName, Expressions.subtract(trop0, trop1)); default: return normalize(typeName, Expressions.add(trop0, trop1)); } default: switch (call.getKind()) { case MINUS: switch (typeName) { case INTERVAL_YEAR: case INTERVAL_YEAR_MONTH: case INTERVAL_MONTH: return Expressions.call(BuiltInMethod.SUBTRACT_MONTHS.method, trop0, trop1); } TimeUnit fromUnit = typeName1 == SqlTypeName.DATE ? TimeUnit.DAY : TimeUnit.MILLISECOND; TimeUnit toUnit = TimeUnit.MILLISECOND; return multiplyDivide( Expressions.convert_(Expressions.subtract(trop0, trop1), (Class) long.class), fromUnit.multiplier, toUnit.multiplier); default: throw new AssertionError(call); } } } /** Normalizes a TIME value into 00:00:00..23:59:39. */ private Expression normalize(SqlTypeName typeName, Expression e) { switch (typeName) { case TIME: return Expressions.call(BuiltInMethod.FLOOR_MOD.method, e, Expressions.constant(DateTimeUtils.MILLIS_PER_DAY)); default: return e; } } } /** Implements CLASSIFIER match-recognize function. */ private static class ClassifierImplementor implements MatchImplementor { @Override public Expression implement(RexToLixTranslator translator, RexCall call, ParameterExpression row, ParameterExpression rows, ParameterExpression symbols, ParameterExpression i) { return EnumUtils.convert( Expressions.call(symbols, BuiltInMethod.LIST_GET.method, i), String.class); } } /** Implements the LAST match-recognize function. */ private static class LastImplementor implements MatchImplementor { @Override public Expression implement(RexToLixTranslator translator, RexCall call, ParameterExpression row, ParameterExpression rows, ParameterExpression symbols, ParameterExpression i) { final RexNode node = call.getOperands().get(0); final String alpha = ((RexPatternFieldRef) call.getOperands().get(0)).getAlpha(); final BinaryExpression lastIndex = Expressions.subtract( Expressions.call(rows, BuiltInMethod.COLLECTION_SIZE.method), Expressions.constant(1)); // Just take the last one, if exists if ("*".equals(alpha)) { ((EnumerableMatch.PassedRowsInputGetter) translator.inputGetter).setIndex(i); // Important, unbox the node / expression to avoid NullAs.NOT_POSSIBLE final RexPatternFieldRef ref = (RexPatternFieldRef) node; final RexPatternFieldRef newRef = new RexPatternFieldRef(ref.getAlpha(), ref.getIndex(), translator.typeFactory.createTypeWithNullability(ref.getType(), true)); final Expression expression = translator.translate(newRef, NullAs.NULL); ((EnumerableMatch.PassedRowsInputGetter) translator.inputGetter).setIndex(null); return expression; } else { // Alpha != "*" so we have to search for a specific one to find and use that, if found ((EnumerableMatch.PassedRowsInputGetter) translator.inputGetter).setIndex( Expressions.call(BuiltInMethod.MATCH_UTILS_LAST_WITH_SYMBOL.method, Expressions.constant(alpha), rows, symbols, i)); // Important, unbox the node / expression to avoid NullAs.NOT_POSSIBLE final RexPatternFieldRef ref = (RexPatternFieldRef) node; final RexPatternFieldRef newRef = new RexPatternFieldRef(ref.getAlpha(), ref.getIndex(), translator.typeFactory.createTypeWithNullability(ref.getType(), true)); final Expression expression = translator.translate(newRef, NullAs.NULL); ((EnumerableMatch.PassedRowsInputGetter) translator.inputGetter).setIndex(null); return expression; } } } /** Implements PREV match-recognize function. */ private static class PrevImplementor implements CallImplementor { @Override public Expression implement(RexToLixTranslator translator, RexCall call, NullAs nullAs) { final RexNode node = call.getOperands().get(0); final RexNode offset = call.getOperands().get(1); final Expression offs = Expressions.multiply(translator.translate(offset), Expressions.constant(-1)); ((EnumerableMatch.PrevInputGetter) translator.inputGetter).setOffset(offs); return translator.translate(node, nullAs); } } /** Implements tumbling. */ private static class TumbleImplementor implements TableFunctionCallImplementor { @Override public Expression implement(RexToLixTranslator translator, Expression inputEnumerable, RexCall call, PhysType inputPhysType, PhysType outputPhysType) { // The table operand is removed from the RexCall because it // represents the input, see StandardConvertletTable#convertWindowFunction. Expression intervalExpression = translator.translate(call.getOperands().get(1)); RexCall descriptor = (RexCall) call.getOperands().get(0); List translatedOperands = new ArrayList<>(); final ParameterExpression parameter = Expressions.parameter(Primitive.box(inputPhysType.getJavaRowType()), "_input"); Expression wmColExpr = inputPhysType.fieldReference( parameter, ((RexInputRef) descriptor.getOperands().get(0)).getIndex(), outputPhysType.getJavaFieldType(inputPhysType.getRowType().getFieldCount())); translatedOperands.add(wmColExpr); translatedOperands.add(intervalExpression); return Expressions.call( BuiltInMethod.TUMBLING.method, inputEnumerable, EnumUtils.tumblingWindowSelector( inputPhysType, outputPhysType, translatedOperands.get(0), translatedOperands.get(1))); } } /** Implements hopping. */ private static class HopImplementor implements TableFunctionCallImplementor { @Override public Expression implement(RexToLixTranslator translator, Expression inputEnumerable, RexCall call, PhysType inputPhysType, PhysType outputPhysType) { Expression slidingInterval = translator.translate(call.getOperands().get(1)); Expression windowSize = translator.translate(call.getOperands().get(2)); RexCall descriptor = (RexCall) call.getOperands().get(0); List translatedOperands = new ArrayList<>(); Expression wmColIndexExpr = Expressions.constant(((RexInputRef) descriptor.getOperands().get(0)).getIndex()); translatedOperands.add(wmColIndexExpr); translatedOperands.add(slidingInterval); translatedOperands.add(windowSize); return Expressions.call( BuiltInMethod.HOPPING.method, Expressions.list( Expressions.call(inputEnumerable, BuiltInMethod.ENUMERABLE_ENUMERATOR.method), translatedOperands.get(0), translatedOperands.get(1), translatedOperands.get(2))); } } /** Implements per-key sessionization. */ private static class SessionImplementor implements TableFunctionCallImplementor { @Override public Expression implement(RexToLixTranslator translator, Expression inputEnumerable, RexCall call, PhysType inputPhysType, PhysType outputPhysType) { RexCall timestampDescriptor = (RexCall) call.getOperands().get(0); RexCall keyDescriptor = (RexCall) call.getOperands().get(1); Expression gapInterval = translator.translate(call.getOperands().get(2)); List translatedOperands = new ArrayList<>(); Expression wmColIndexExpr = Expressions.constant(((RexInputRef) timestampDescriptor.getOperands().get(0)).getIndex()); Expression keyColIndexExpr = Expressions.constant(((RexInputRef) keyDescriptor.getOperands().get(0)).getIndex()); translatedOperands.add(wmColIndexExpr); translatedOperands.add(keyColIndexExpr); translatedOperands.add(gapInterval); return Expressions.call( BuiltInMethod.SESSIONIZATION.method, Expressions.list( Expressions.call(inputEnumerable, BuiltInMethod.ENUMERABLE_ENUMERATOR.method), translatedOperands.get(0), translatedOperands.get(1), translatedOperands.get(2))); } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy