use of io.crate.expression.symbol.Function in project crate by crate.
the class RelationAnalyzer method visitValues.
@Override
public AnalyzedRelation visitValues(Values values, StatementAnalysisContext context) {
var expressionAnalyzer = new ExpressionAnalyzer(context.transactionContext(), nodeCtx, context.paramTyeHints(), FieldProvider.UNSUPPORTED, new SubqueryAnalyzer(this, context));
var expressionAnalysisContext = new ExpressionAnalysisContext(context.sessionContext());
// prevent normalization of the values array, otherwise the value literals are converted to an array literal
// and a special per-value-literal casting logic won't be executed (e.g. FloatLiteral.cast())
expressionAnalysisContext.allowEagerNormalize(false);
java.util.function.Function<Expression, Symbol> expressionToSymbol = e -> expressionAnalyzer.convert(e, expressionAnalysisContext);
// There is a first pass to convert expressions from row oriented format:
// `[[1, a], [2, b]]` to columns `[[1, 2], [a, b]]`
//
// At the same time we determine the column type with the highest precedence,
// so that we don't fail with slight type miss-matches (long vs. int)
List<ValuesList> rows = values.rows();
assert rows.size() > 0 : "Parser grammar enforces at least 1 row";
ValuesList firstRow = rows.get(0);
int numColumns = firstRow.values().size();
ArrayList<List<Symbol>> columns = new ArrayList<>();
ArrayList<DataType<?>> targetTypes = new ArrayList<>(numColumns);
var parentOutputColumns = context.parentOutputColumns();
for (int c = 0; c < numColumns; c++) {
ArrayList<Symbol> columnValues = new ArrayList<>();
DataType<?> targetType;
boolean usePrecedence = true;
if (parentOutputColumns.size() > c) {
targetType = parentOutputColumns.get(c).valueType();
usePrecedence = false;
} else {
targetType = DataTypes.UNDEFINED;
}
for (int r = 0; r < rows.size(); r++) {
List<Expression> row = rows.get(r).values();
if (row.size() != numColumns) {
throw new IllegalArgumentException("VALUES lists must all be the same length. " + "Found row with " + numColumns + " items and another with " + columns.size() + " items");
}
Symbol cell = expressionToSymbol.apply(row.get(c));
columnValues.add(cell);
var cellType = cell.valueType();
if (// skip first cell, we don't have to check for self-conversion
r > 0 && !cellType.isConvertableTo(targetType, false) && targetType.id() != DataTypes.UNDEFINED.id()) {
throw new IllegalArgumentException("The types of the columns within VALUES lists must match. " + "Found `" + targetType + "` and `" + cellType + "` at position: " + c);
}
if (usePrecedence && cellType.precedes(targetType)) {
targetType = cellType;
} else if (targetType == DataTypes.UNDEFINED) {
targetType = cellType;
}
}
targetTypes.add(targetType);
columns.add(columnValues);
}
var normalizer = EvaluatingNormalizer.functionOnlyNormalizer(nodeCtx, f -> f.isDeterministic());
ArrayList<Symbol> arrays = new ArrayList<>(columns.size());
for (int c = 0; c < numColumns; c++) {
DataType<?> targetType = targetTypes.get(c);
ArrayType<?> arrayType = new ArrayType<>(targetType);
List<Symbol> columnValues = Lists2.map(columns.get(c), s -> normalizer.normalize(s.cast(targetType), context.transactionContext()));
arrays.add(new Function(ArrayFunction.SIGNATURE, columnValues, arrayType));
}
FunctionImplementation implementation = nodeCtx.functions().getQualified(ValuesFunction.SIGNATURE, Symbols.typeView(arrays), RowType.EMPTY);
Function function = new Function(implementation.signature(), arrays, RowType.EMPTY);
TableFunctionImplementation<?> tableFunc = TableFunctionFactory.from(implementation);
TableFunctionRelation relation = new TableFunctionRelation(tableFunc, function);
context.startRelation();
context.currentRelationContext().addSourceRelation(relation);
context.endRelation();
return relation;
}
use of io.crate.expression.symbol.Function in project crate by crate.
the class ExpressionAnalyzer method allocateBuiltinOrUdfFunction.
/**
* Creates a function symbol and tries to normalize the new function's
* {@link FunctionImplementation} iff only Literals are supplied as arguments.
* This folds any constant expressions like '1 + 1' => '2'.
* @param schema The schema for udf functions
* @param functionName The function name of the new function.
* @param arguments The arguments to provide to the {@link Function}.
* @param filter The filter clause to filter {@link Function}'s input values.
* @param context Context holding the state for the current translation.
* @param windowDefinition The definition of the window the allocated function will be executed against.
* @param coordinatorTxnCtx {@link CoordinatorTxnCtx} for this transaction.
* @param nodeCtx The {@link NodeContext} to normalize constant expressions.
* @return The supplied {@link Function} or a {@link Literal} in case of constant folding.
*/
private static Symbol allocateBuiltinOrUdfFunction(@Nullable String schema, String functionName, List<Symbol> arguments, @Nullable Symbol filter, @Nullable Boolean ignoreNulls, ExpressionAnalysisContext context, @Nullable WindowDefinition windowDefinition, CoordinatorTxnCtx coordinatorTxnCtx, NodeContext nodeCtx) {
FunctionImplementation funcImpl = nodeCtx.functions().get(schema, functionName, arguments, coordinatorTxnCtx.sessionContext().searchPath());
Signature signature = funcImpl.signature();
Signature boundSignature = funcImpl.boundSignature();
List<Symbol> castArguments = cast(arguments, boundSignature.getArgumentDataTypes());
Function newFunction;
if (windowDefinition == null) {
if (signature.getKind() == FunctionType.AGGREGATE) {
context.indicateAggregates();
} else if (filter != null) {
throw new UnsupportedOperationException("Only aggregate functions allow a FILTER clause");
}
if (ignoreNulls != null) {
throw new IllegalArgumentException(String.format(Locale.ENGLISH, "%s cannot accept RESPECT or IGNORE NULLS flag.", functionName));
}
newFunction = new Function(signature, castArguments, boundSignature.getReturnType().createType(), filter);
} else {
if (signature.getKind() != FunctionType.WINDOW) {
if (signature.getKind() != FunctionType.AGGREGATE) {
throw new IllegalArgumentException(String.format(Locale.ENGLISH, "OVER clause was specified, but %s is neither a window nor an aggregate function.", functionName));
} else {
if (ignoreNulls != null) {
throw new IllegalArgumentException(String.format(Locale.ENGLISH, "%s cannot accept RESPECT or IGNORE NULLS flag.", functionName));
}
}
}
newFunction = new WindowFunction(signature, castArguments, boundSignature.getReturnType().createType(), filter, windowDefinition, ignoreNulls);
}
return newFunction;
}
use of io.crate.expression.symbol.Function in project crate by crate.
the class ExpressionAnalyzer method convertFunctionCall.
private Symbol convertFunctionCall(FunctionCall node, ExpressionAnalysisContext context) {
List<Symbol> arguments = new ArrayList<>(node.getArguments().size());
for (Expression expression : node.getArguments()) {
Symbol argSymbol = expression.accept(innerAnalyzer, context);
arguments.add(argSymbol);
}
List<String> parts = node.getName().getParts();
// We don't set a default schema here because no supplied schema
// means that we first try to lookup builtin functions, followed
// by a lookup in the default schema for UDFs.
String schema = null;
String name;
if (parts.size() == 1) {
name = parts.get(0);
} else {
schema = parts.get(0);
name = parts.get(1);
}
Symbol filter = node.filter().map(expression -> convert(expression, context)).orElse(null);
WindowDefinition windowDefinition = getWindowDefinition(node.getWindow(), context);
if (node.isDistinct()) {
if (arguments.size() > 1) {
throw new UnsupportedOperationException(String.format(Locale.ENGLISH, "%s(DISTINCT x) does not accept more than one argument", node.getName()));
}
Symbol collectSetFunction = allocateFunction(CollectSetAggregation.NAME, arguments, filter, context, coordinatorTxnCtx, nodeCtx);
// define the outer function which contains the inner function as argument.
String nodeName = "collection_" + name;
List<Symbol> outerArguments = List.of(collectSetFunction);
try {
return allocateBuiltinOrUdfFunction(schema, nodeName, outerArguments, null, node.ignoreNulls(), windowDefinition, context);
} catch (UnsupportedOperationException ex) {
throw new UnsupportedOperationException(String.format(Locale.ENGLISH, "unknown function %s(DISTINCT %s)", name, arguments.get(0).valueType()), ex);
}
} else {
return allocateBuiltinOrUdfFunction(schema, name, arguments, filter, node.ignoreNulls(), windowDefinition, context);
}
}
use of io.crate.expression.symbol.Function in project crate by crate.
the class SelectStatementAnalyzerTest method testWhereInSelect.
@Test
public void testWhereInSelect() throws Exception {
var executor = SQLExecutor.builder(clusterService).build();
QueriedSelectRelation relation = executor.analyze("select load from sys.nodes where load['1'] in (1.0, 2.0, 4.0, 8.0, 16.0)");
Function whereClause = (Function) relation.where();
assertThat(whereClause.name(), is(AnyEqOperator.NAME));
}
use of io.crate.expression.symbol.Function in project crate by crate.
the class SelectStatementAnalyzerTest method testNonDeterministicFunctionsAreNotAllocated.
@Test
public void testNonDeterministicFunctionsAreNotAllocated() throws Exception {
var executor = SQLExecutor.builder(clusterService).addTable(TableDefinitions.TEST_DOC_TRANSACTIONS_TABLE_DEFINITION).build();
QueriedSelectRelation relation = executor.analyze("select random(), random(), random() " + "from transactions " + "where random() = 13.2 " + "order by 1, random(), random()");
List<Symbol> outputs = relation.outputs();
List<Symbol> orderBySymbols = relation.orderBy().orderBySymbols();
// non deterministic, all equal
assertThat(outputs.get(0), allOf(equalTo(outputs.get(2)), equalTo(orderBySymbols.get(1))));
// different instances
assertThat(outputs.get(0), allOf(not(Matchers.sameInstance(outputs.get(2))), not(Matchers.sameInstance(orderBySymbols.get(1)))));
assertThat(outputs.get(1), equalTo(orderBySymbols.get(2)));
// "order by 1" references output 1, its the same
assertThat(outputs.get(0), is(equalTo(orderBySymbols.get(0))));
assertThat(outputs.get(0), is(Matchers.sameInstance(orderBySymbols.get(0))));
assertThat(orderBySymbols.get(0), is(equalTo(orderBySymbols.get(1))));
// check where clause
Function eqFunction = (Function) relation.where();
Symbol whereClauseSleepFn = eqFunction.arguments().get(0);
assertThat(outputs.get(0), is(equalTo(whereClauseSleepFn)));
}
Aggregations