use of io.crate.metadata.FunctionImplementation in project crate by crate.
the class RelationAnalyzer method visitTableFunction.
@Override
public AnalyzedRelation visitTableFunction(TableFunction node, StatementAnalysisContext statementContext) {
RelationAnalysisContext context = statementContext.currentRelationContext();
ExpressionAnalyzer expressionAnalyzer = new ExpressionAnalyzer(statementContext.transactionContext(), nodeCtx, statementContext.paramTyeHints(), FieldProvider.UNSUPPORTED, null);
ExpressionAnalysisContext expressionContext = context.expressionAnalysisContext();
// we support `FROM scalar()` but not `FROM 'literal'` -> we turn off eager normalization
// so we can distinguish between Function and Literal.
final boolean allowEagerNormalizeOriginalValue = expressionContext.isEagerNormalizationAllowed();
expressionContext.allowEagerNormalize(false);
Symbol symbol = expressionAnalyzer.convert(node.functionCall(), expressionContext);
expressionContext.allowEagerNormalize(allowEagerNormalizeOriginalValue);
if (!(symbol instanceof Function)) {
throw new UnsupportedOperationException(String.format(Locale.ENGLISH, "Symbol '%s' is not supported in FROM clause", node.name()));
}
Function function = (Function) symbol;
FunctionImplementation functionImplementation = nodeCtx.functions().getQualified(function, statementContext.sessionContext().searchPath());
assert functionImplementation != null : "Function implementation not found using full qualified lookup";
TableFunctionImplementation<?> tableFunction = TableFunctionFactory.from(functionImplementation);
TableFunctionRelation tableRelation = new TableFunctionRelation(tableFunction, function);
context.addSourceRelation(tableRelation);
return tableRelation;
}
use of io.crate.metadata.FunctionImplementation 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.metadata.FunctionImplementation 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.metadata.FunctionImplementation in project crate by crate.
the class BaseImplementationSymbolVisitor method visitFunction.
@Override
public Input<?> visitFunction(Function function, C context) {
Signature signature = function.signature();
FunctionImplementation functionImplementation = nodeCtx.functions().getQualified(function, txnCtx.sessionSettings().searchPath());
assert functionImplementation != null : "Function implementation not found using full qualified lookup";
if (functionImplementation instanceof Scalar<?, ?>) {
List<Symbol> arguments = function.arguments();
Scalar<?, ?> scalarImpl = ((Scalar) functionImplementation).compile(arguments);
Input[] argumentInputs = new Input[arguments.size()];
int i = 0;
for (Symbol argument : function.arguments()) {
argumentInputs[i++] = argument.accept(this, context);
}
return new FunctionExpression<>(txnCtx, nodeCtx, scalarImpl, argumentInputs);
} else {
throw new UnsupportedFeatureException(String.format(Locale.ENGLISH, "Function %s(%s) is not a scalar function.", signature.getName(), Lists2.joinOn(", ", function.arguments(), x -> x.valueType().getName())));
}
}
use of io.crate.metadata.FunctionImplementation in project crate by crate.
the class ScalarTestCase method assertNormalize.
public void assertNormalize(String functionExpression, Matcher<? super Symbol> expectedSymbol, boolean evaluate) {
// Explicit normalization happens further below
sqlExpressions.context().allowEagerNormalize(false);
Symbol functionSymbol = sqlExpressions.asSymbol(functionExpression);
if (functionSymbol instanceof Literal) {
assertThat(functionSymbol, expectedSymbol);
return;
}
Function function = (Function) functionSymbol;
FunctionImplementation impl = sqlExpressions.nodeCtx.functions().getQualified(function, txnCtx.sessionSettings().searchPath());
assertThat("Function implementation not found using full qualified lookup", impl, Matchers.notNullValue());
Symbol normalized = sqlExpressions.normalize(function);
assertThat(String.format(Locale.ENGLISH, "expected <%s> to normalize to %s", functionExpression, expectedSymbol), normalized, expectedSymbol);
if (evaluate && normalized instanceof Input && allArgsAreInputs(function.arguments())) {
Input[] inputs = new Input[function.arguments().size()];
for (int i = 0; i < inputs.length; i++) {
inputs[i] = ((Input) function.arguments().get(i));
}
Object expectedValue = ((Input) normalized).value();
assertThat(((Scalar) impl).evaluate(txnCtx, null, inputs), is(expectedValue));
assertThat(((Scalar) impl).compile(function.arguments()).evaluate(txnCtx, sqlExpressions.nodeCtx, inputs), is(expectedValue));
}
}
Aggregations