blob: 0ce09e54ab6a13e8b2354c541a3d59701361154a [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.asterix.lang.sqlpp.rewrites;
import java.util.ArrayDeque;
import java.util.Collection;
import java.util.Collections;
import java.util.Deque;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.asterix.common.exceptions.CompilationException;
import org.apache.asterix.common.exceptions.ErrorCode;
import org.apache.asterix.common.functions.FunctionSignature;
import org.apache.asterix.common.metadata.DatasetFullyQualifiedName;
import org.apache.asterix.common.metadata.DataverseName;
import org.apache.asterix.common.metadata.Namespace;
import org.apache.asterix.lang.common.base.AbstractExpression;
import org.apache.asterix.lang.common.base.Expression;
import org.apache.asterix.lang.common.base.IParserFactory;
import org.apache.asterix.lang.common.base.IQueryRewriter;
import org.apache.asterix.lang.common.base.IReturningStatement;
import org.apache.asterix.lang.common.expression.AbstractCallExpression;
import org.apache.asterix.lang.common.expression.CallExpr;
import org.apache.asterix.lang.common.expression.FieldAccessor;
import org.apache.asterix.lang.common.expression.LiteralExpr;
import org.apache.asterix.lang.common.expression.VariableExpr;
import org.apache.asterix.lang.common.literal.MissingLiteral;
import org.apache.asterix.lang.common.rewrites.LangRewritingContext;
import org.apache.asterix.lang.common.statement.FunctionDecl;
import org.apache.asterix.lang.common.statement.Query;
import org.apache.asterix.lang.common.statement.ViewDecl;
import org.apache.asterix.lang.common.struct.Identifier;
import org.apache.asterix.lang.common.struct.VarIdentifier;
import org.apache.asterix.lang.common.util.ExpressionUtils;
import org.apache.asterix.lang.common.util.FunctionUtil;
import org.apache.asterix.lang.common.util.ViewUtil;
import org.apache.asterix.lang.common.visitor.base.ILangVisitor;
import org.apache.asterix.lang.sqlpp.rewrites.visitor.GenerateColumnNameVisitor;
import org.apache.asterix.lang.sqlpp.rewrites.visitor.InlineColumnAliasVisitor;
import org.apache.asterix.lang.sqlpp.rewrites.visitor.InlineWithExpressionVisitor;
import org.apache.asterix.lang.sqlpp.rewrites.visitor.OperatorExpressionVisitor;
import org.apache.asterix.lang.sqlpp.rewrites.visitor.SelectExcludeRewriteSugarVisitor;
import org.apache.asterix.lang.sqlpp.rewrites.visitor.SetOperationVisitor;
import org.apache.asterix.lang.sqlpp.rewrites.visitor.SqlCompatRewriteVisitor;
import org.apache.asterix.lang.sqlpp.rewrites.visitor.SqlppCaseAggregateExtractionVisitor;
import org.apache.asterix.lang.sqlpp.rewrites.visitor.SqlppCaseExpressionVisitor;
import org.apache.asterix.lang.sqlpp.rewrites.visitor.SqlppFunctionCallResolverVisitor;
import org.apache.asterix.lang.sqlpp.rewrites.visitor.SqlppGatherFunctionCallsVisitor;
import org.apache.asterix.lang.sqlpp.rewrites.visitor.SqlppGroupByAggregationSugarVisitor;
import org.apache.asterix.lang.sqlpp.rewrites.visitor.SqlppGroupByVisitor;
import org.apache.asterix.lang.sqlpp.rewrites.visitor.SqlppGroupingSetsVisitor;
import org.apache.asterix.lang.sqlpp.rewrites.visitor.SqlppInlineUdfsVisitor;
import org.apache.asterix.lang.sqlpp.rewrites.visitor.SqlppListInputFunctionRewriteVisitor;
import org.apache.asterix.lang.sqlpp.rewrites.visitor.SqlppRightJoinRewriteVisitor;
import org.apache.asterix.lang.sqlpp.rewrites.visitor.SqlppSpecialFunctionNameRewriteVisitor;
import org.apache.asterix.lang.sqlpp.rewrites.visitor.SqlppWindowAggregationSugarVisitor;
import org.apache.asterix.lang.sqlpp.rewrites.visitor.SqlppWindowRewriteVisitor;
import org.apache.asterix.lang.sqlpp.rewrites.visitor.SubstituteGroupbyExpressionWithVariableVisitor;
import org.apache.asterix.lang.sqlpp.rewrites.visitor.VariableCheckAndRewriteVisitor;
import org.apache.asterix.lang.sqlpp.util.SqlppAstPrintUtil;
import org.apache.asterix.lang.sqlpp.util.SqlppVariableUtil;
import org.apache.asterix.metadata.bootstrap.MetadataBuiltinEntities;
import org.apache.asterix.metadata.declared.MetadataProvider;
import org.apache.asterix.metadata.entities.Dataset;
import org.apache.asterix.metadata.entities.Dataverse;
import org.apache.asterix.metadata.entities.Function;
import org.apache.asterix.metadata.entities.ViewDetails;
import org.apache.asterix.metadata.utils.DatasetUtil;
import org.apache.asterix.metadata.utils.TypeUtil;
import org.apache.asterix.om.types.IAType;
import org.apache.hyracks.algebricks.common.exceptions.AlgebricksException;
import org.apache.hyracks.algebricks.common.utils.Pair;
import org.apache.hyracks.algebricks.common.utils.Triple;
import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
import org.apache.hyracks.api.exceptions.SourceLocation;
import org.apache.hyracks.util.LogRedactionUtil;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
public class SqlppQueryRewriter implements IQueryRewriter {
private static final Logger LOGGER = LogManager.getLogger(SqlppQueryRewriter.class);
public static final String INLINE_WITH_OPTION = "inline_with";
private static final boolean INLINE_WITH_OPTION_DEFAULT = true;
public static final String SQL_COMPAT_OPTION = "sql_compat";
private static final boolean SQL_COMPAT_OPTION_DEFAULT = false;
private final IParserFactory parserFactory;
private SqlppFunctionBodyRewriter functionAndViewBodyRewriter;
private IReturningStatement topStatement;
private LangRewritingContext context;
private MetadataProvider metadataProvider;
private Collection<VarIdentifier> externalVars;
private boolean allowNonStoredUdfCalls;
private boolean inlineUdfsAndViews;
private boolean isLogEnabled;
public SqlppQueryRewriter(IParserFactory parserFactory) {
this.parserFactory = parserFactory;
}
protected void setup(LangRewritingContext context, IReturningStatement topStatement,
Collection<VarIdentifier> externalVars, boolean allowNonStoredUdfCalls, boolean inlineUdfsAndViews)
throws CompilationException {
this.context = context;
this.metadataProvider = context.getMetadataProvider();
this.topStatement = topStatement;
this.externalVars = externalVars != null ? externalVars : Collections.emptyList();
this.allowNonStoredUdfCalls = allowNonStoredUdfCalls;
this.inlineUdfsAndViews = inlineUdfsAndViews;
this.isLogEnabled = LOGGER.isTraceEnabled();
logExpression("Starting AST rewrites on", "");
}
@Override
public void rewrite(LangRewritingContext context, IReturningStatement topStatement, boolean allowNonStoredUdfCalls,
boolean inlineUdfsAndViews, Collection<VarIdentifier> externalVars) throws CompilationException {
// Sets up parameters.
setup(context, topStatement, externalVars, allowNonStoredUdfCalls, inlineUdfsAndViews);
// Resolves function calls
resolveFunctionCalls();
// Generates column names.
generateColumnNames();
// SQL-compat mode rewrites
// Must run after generateColumnNames() because it might need to generate new column names
// for the new projections that it introduces
rewriteSqlCompat();
// Substitutes group-by key expressions.
substituteGroupbyKeyExpression();
// Group-by core rewrites
rewriteGroupBys();
// Rewrites set operations.
rewriteSetOperations();
// Inlines column aliases.
inlineColumnAlias();
// Rewrite SELECT EXCLUDE to use OBJECT_REMOVE_FIELDS.
rewriteSelectExcludeSugar();
// Window expression core rewrites.
rewriteWindowExpressions();
// Rewrites Group-By clauses with multiple grouping sets into UNION ALL
// Must run after rewriteSetOperations() and before variableCheckAndRewrite()
rewriteGroupingSets();
// Generate ids for variables (considering scopes) and replace global variable access with the dataset function.
variableCheckAndRewrite();
// Extracts SQL-92 aggregate functions from CASE/IF expressions into LET clauses
extractAggregatesFromCaseExpressions();
// Rewrites SQL-92 aggregate functions
rewriteGroupByAggregationSugar();
// Rewrites window expression aggregations.
rewriteWindowAggregationSugar();
// Rewrites like/not-like expressions.
rewriteOperatorExpression();
// Normalizes CASE expressions and rewrites simple ones into switch-case()
rewriteCaseExpressions();
// Rewrites several variable-arg functions into their corresponding internal list-input functions.
rewriteListInputFunctions();
// Rewrites RIGHT OUTER JOINs into LEFT OUTER JOINs if possible
rewriteRightJoins();
// Inlines functions and views
loadAndInlineUdfsAndViews();
// Rewrites SQL++ core aggregate function names into internal names
rewriteSpecialFunctionNames();
// Inlines WITH expressions after variableCheckAndRewrite(...) so that the variable scoping for WITH
// expression is correct.
//
// Must run after rewriteSpecialFunctionNames() because it needs to have FunctionInfo
// for all functions to avoid inlining non-deterministic expressions.
// (CallExprs with special function names do not have FunctionInfo)
//
// Must run after inlineDeclaredUdfs() because we only maintain deterministic modifiers for built-in
// and external UDFs, therefore need to inline SQL++ UDFs to check the deterministic property.
inlineWithExpressions();
// Sets the var counter of the query.
topStatement.setVarCounter(context.getVarCounter().get());
}
protected void rewriteGroupByAggregationSugar() throws CompilationException {
SqlppGroupByAggregationSugarVisitor visitor = new SqlppGroupByAggregationSugarVisitor(context, externalVars);
rewriteTopExpr(visitor, null);
}
protected void rewriteListInputFunctions() throws CompilationException {
SqlppListInputFunctionRewriteVisitor listInputFunctionVisitor = new SqlppListInputFunctionRewriteVisitor();
rewriteTopExpr(listInputFunctionVisitor, null);
}
protected void rewriteSqlCompat() throws CompilationException {
boolean sqlCompatMode = metadataProvider.getBooleanProperty(SQL_COMPAT_OPTION, SQL_COMPAT_OPTION_DEFAULT);
if (!sqlCompatMode) {
return;
}
SqlCompatRewriteVisitor visitor = new SqlCompatRewriteVisitor(context);
rewriteTopExpr(visitor, null);
}
protected void resolveFunctionCalls() throws CompilationException {
SqlppFunctionCallResolverVisitor visitor =
new SqlppFunctionCallResolverVisitor(context, allowNonStoredUdfCalls);
rewriteTopExpr(visitor, null);
}
protected void rewriteSpecialFunctionNames() throws CompilationException {
SqlppSpecialFunctionNameRewriteVisitor visitor = new SqlppSpecialFunctionNameRewriteVisitor();
rewriteTopExpr(visitor, null);
}
protected void inlineWithExpressions() throws CompilationException {
if (!metadataProvider.getBooleanProperty(INLINE_WITH_OPTION, INLINE_WITH_OPTION_DEFAULT)) {
return;
}
// Inlines with expressions.
InlineWithExpressionVisitor inlineWithExpressionVisitor =
new InlineWithExpressionVisitor(context, metadataProvider);
rewriteTopExpr(inlineWithExpressionVisitor, null);
}
protected void generateColumnNames() throws CompilationException {
// Generate column names if they are missing in the user query.
GenerateColumnNameVisitor generateColumnNameVisitor = new GenerateColumnNameVisitor(context);
rewriteTopExpr(generateColumnNameVisitor, null);
}
protected void substituteGroupbyKeyExpression() throws CompilationException {
// Substitute group-by key expressions that appear in the select clause.
SubstituteGroupbyExpressionWithVariableVisitor substituteGbyExprVisitor =
new SubstituteGroupbyExpressionWithVariableVisitor(context);
rewriteTopExpr(substituteGbyExprVisitor, null);
}
protected void rewriteSetOperations() throws CompilationException {
// Rewrites set operation queries that contain order-by and limit clauses.
SetOperationVisitor setOperationVisitor = new SetOperationVisitor(context);
rewriteTopExpr(setOperationVisitor, null);
}
protected void rewriteOperatorExpression() throws CompilationException {
// Rewrites like/not-like/in/not-in operators into function call expressions.
OperatorExpressionVisitor operatorExpressionVisitor = new OperatorExpressionVisitor(context);
rewriteTopExpr(operatorExpressionVisitor, null);
}
protected void inlineColumnAlias() throws CompilationException {
// Inline column aliases.
InlineColumnAliasVisitor inlineColumnAliasVisitor = new InlineColumnAliasVisitor(context);
rewriteTopExpr(inlineColumnAliasVisitor, null);
}
protected void variableCheckAndRewrite() throws CompilationException {
VariableCheckAndRewriteVisitor variableCheckAndRewriteVisitor =
new VariableCheckAndRewriteVisitor(context, metadataProvider, externalVars);
rewriteTopExpr(variableCheckAndRewriteVisitor, null);
}
protected void rewriteGroupBys() throws CompilationException {
SqlppGroupByVisitor groupByVisitor = new SqlppGroupByVisitor(context);
rewriteTopExpr(groupByVisitor, null);
}
protected void rewriteGroupingSets() throws CompilationException {
SqlppGroupingSetsVisitor groupingSetsVisitor = new SqlppGroupingSetsVisitor(context);
rewriteTopExpr(groupingSetsVisitor, null);
}
protected void rewriteWindowExpressions() throws CompilationException {
// Create window variables and extract aggregation inputs into LET clauses
SqlppWindowRewriteVisitor windowVisitor = new SqlppWindowRewriteVisitor(context);
rewriteTopExpr(windowVisitor, null);
}
protected void rewriteWindowAggregationSugar() throws CompilationException {
SqlppWindowAggregationSugarVisitor windowVisitor = new SqlppWindowAggregationSugarVisitor(context);
rewriteTopExpr(windowVisitor, null);
}
protected void extractAggregatesFromCaseExpressions() throws CompilationException {
SqlppCaseAggregateExtractionVisitor visitor = new SqlppCaseAggregateExtractionVisitor(context);
rewriteTopExpr(visitor, null);
}
protected void rewriteCaseExpressions() throws CompilationException {
// Normalizes CASE expressions and rewrites simple ones into switch-case()
SqlppCaseExpressionVisitor visitor = new SqlppCaseExpressionVisitor();
rewriteTopExpr(visitor, null);
}
protected void rewriteRightJoins() throws CompilationException {
// Rewrites RIGHT OUTER JOINs into LEFT OUTER JOINs if possible
SqlppRightJoinRewriteVisitor visitor = new SqlppRightJoinRewriteVisitor(context, externalVars);
rewriteTopExpr(visitor, null);
}
protected void loadAndInlineUdfsAndViews() throws CompilationException {
Pair<Map<FunctionSignature, FunctionDecl>, Map<DatasetFullyQualifiedName, ViewDecl>> udfAndViewDecls =
loadUdfsAndViews(topStatement);
Map<FunctionSignature, FunctionDecl> udfs = udfAndViewDecls.first;
Map<DatasetFullyQualifiedName, ViewDecl> views = udfAndViewDecls.second;
if (udfs.isEmpty() && views.isEmpty()) {
// nothing to do
return;
}
if (ExpressionUtils.hasFunctionOrViewRecursion(udfs, views, SqlppGatherFunctionCallsVisitor::new)) {
throw new CompilationException(ErrorCode.ILLEGAL_FUNCTION_OR_VIEW_RECURSION,
topStatement.getSourceLocation());
}
if (inlineUdfsAndViews) {
SqlppInlineUdfsVisitor visitor = new SqlppInlineUdfsVisitor(context, udfs, views);
while (rewriteTopExpr(visitor, null)) {
// loop until no more changes
}
}
}
protected void rewriteSelectExcludeSugar() throws CompilationException {
SelectExcludeRewriteSugarVisitor selectExcludeRewriteSugarVisitor =
new SelectExcludeRewriteSugarVisitor(context);
rewriteTopExpr(selectExcludeRewriteSugarVisitor, null);
}
private <R, T> R rewriteTopExpr(ILangVisitor<R, T> visitor, T arg) throws CompilationException {
R result = topStatement.accept(visitor, arg);
logExpression(">>>> AST After", visitor.getClass().getSimpleName());
return result;
}
private void logExpression(String p0, String p1) throws CompilationException {
if (isLogEnabled) {
LOGGER.trace("{} {}\n{}", p0, p1, LogRedactionUtil.userData(SqlppAstPrintUtil.toString(topStatement)));
}
}
@Override
public void getFunctionCalls(Expression expression, Collection<? super AbstractCallExpression> outCalls)
throws CompilationException {
SqlppGatherFunctionCallsVisitor gfc = new SqlppGatherFunctionCallsVisitor(outCalls);
expression.accept(gfc, null);
}
@Override
public Set<VariableExpr> getExternalVariables(Expression expr) throws CompilationException {
Set<VariableExpr> freeVars = SqlppVariableUtil.getFreeVariables(expr);
Set<VariableExpr> extVars = new HashSet<>();
for (VariableExpr ve : freeVars) {
if (SqlppVariableUtil.isExternalVariableReference(ve)) {
extVars.add(ve);
}
}
return extVars;
}
@Override
public VarIdentifier toExternalVariableName(String statementParameterName) {
return SqlppVariableUtil.toExternalVariableIdentifier(statementParameterName);
}
@Override
public String toFunctionParameterName(VarIdentifier paramVar) {
return SqlppVariableUtil.toUserDefinedName(paramVar.getValue());
}
private Pair<Map<FunctionSignature, FunctionDecl>, Map<DatasetFullyQualifiedName, ViewDecl>> loadUdfsAndViews(
IReturningStatement topExpr) throws CompilationException {
Map<FunctionSignature, FunctionDecl> udfs = new LinkedHashMap<>();
Map<DatasetFullyQualifiedName, ViewDecl> views = new LinkedHashMap<>();
Deque<AbstractCallExpression> workQueue = new ArrayDeque<>();
SqlppGatherFunctionCallsVisitor callVisitor = new SqlppGatherFunctionCallsVisitor(workQueue);
for (Expression expr : topExpr.getDirectlyEnclosedExpressions()) {
expr.accept(callVisitor, null);
}
AbstractCallExpression fnCall;
while ((fnCall = workQueue.poll()) != null) {
switch (fnCall.getKind()) {
case CALL_EXPRESSION:
FunctionSignature fs = fnCall.getFunctionSignature();
DataverseName fsDataverse = fs.getDataverseName();
if (fsDataverse == null) {
throw new CompilationException(ErrorCode.COMPILATION_ILLEGAL_STATE, fnCall.getSourceLocation(),
fs);
}
if (FunctionUtil.isBuiltinFunctionSignature(fs)) {
if (FunctionUtil.isBuiltinDatasetFunction(fs)) {
Triple<DatasetFullyQualifiedName, Boolean, DatasetFullyQualifiedName> dsArgs =
FunctionUtil.parseDatasetFunctionArguments(fnCall);
if (Boolean.TRUE.equals(dsArgs.second)) {
DatasetFullyQualifiedName viewName = dsArgs.first;
if (!views.containsKey(viewName)) {
ViewDecl viewDecl = fetchViewDecl(viewName, fnCall.getSourceLocation());
views.put(viewName, viewDecl);
viewDecl.getNormalizedViewBody().accept(callVisitor, null);
}
}
}
} else {
if (!udfs.containsKey(fs)) {
FunctionDecl fd = fetchFunctionDecl(fs, fnCall.getSourceLocation());
if (fd != null) {
udfs.put(fs, fd);
fd.getNormalizedFuncBody().accept(callVisitor, null);
}
}
}
break;
case WINDOW_EXPRESSION:
// there cannot be used-defined window functions
break;
default:
throw new CompilationException(ErrorCode.COMPILATION_ILLEGAL_STATE, fnCall.getSourceLocation(),
fnCall.getFunctionSignature().toString(false));
}
}
return new Pair<>(udfs, views);
}
private FunctionDecl fetchFunctionDecl(FunctionSignature fs, SourceLocation sourceLoc) throws CompilationException {
FunctionDecl fd = context.getDeclaredFunctions().get(fs);
if (fd == null) {
Function function;
try {
function = metadataProvider.lookupUserDefinedFunction(fs);
} catch (AlgebricksException e) {
throw new CompilationException(ErrorCode.UNKNOWN_FUNCTION, sourceLoc, fs.toString());
}
if (function == null) {
throw new CompilationException(ErrorCode.UNKNOWN_FUNCTION, sourceLoc, fs.toString());
}
if (function.isExternal()) {
return null;
}
fd = FunctionUtil.parseStoredFunction(function, parserFactory, context.getWarningCollector(), sourceLoc);
}
Expression normBody = fd.getNormalizedFuncBody();
if (normBody == null) {
normBody = rewriteFunctionBody(fd);
fd.setNormalizedFuncBody(normBody);
}
return fd;
}
private ViewDecl fetchViewDecl(DatasetFullyQualifiedName viewName, SourceLocation sourceLoc)
throws CompilationException {
IAType viewItemType = null;
Boolean defaultNull = false;
Triple<String, String, String> temporalDataFormat = null;
ViewDecl viewDecl = context.getDeclaredViews().get(viewName);
if (viewDecl == null) {
Dataset dataset;
try {
dataset = metadataProvider.findDataset(viewName.getDatabaseName(), viewName.getDataverseName(),
viewName.getDatasetName(), true);
} catch (AlgebricksException e) {
throw new CompilationException(ErrorCode.UNKNOWN_VIEW, e, sourceLoc, viewName);
}
if (dataset == null || DatasetUtil.isNotView(dataset)) {
throw new CompilationException(ErrorCode.UNKNOWN_VIEW, sourceLoc, viewName);
}
ViewDetails viewDetails = (ViewDetails) dataset.getDatasetDetails();
viewDecl = ViewUtil.parseStoredView(viewName, viewDetails, parserFactory, context.getWarningCollector(),
sourceLoc);
DataverseName itemTypeDataverseName = dataset.getItemTypeDataverseName();
String itemTypeDatabase = dataset.getItemTypeDatabaseName();
String itemTypeName = dataset.getItemTypeName();
boolean isAnyType =
MetadataBuiltinEntities.ANY_OBJECT_DATATYPE.getDataverseName().equals(itemTypeDataverseName)
&& MetadataBuiltinEntities.ANY_OBJECT_DATATYPE.getDatatypeName().equals(itemTypeName);
if (!isAnyType) {
try {
viewItemType = metadataProvider.findType(itemTypeDatabase, itemTypeDataverseName, itemTypeName);
} catch (AlgebricksException e) {
throw new CompilationException(ErrorCode.UNKNOWN_TYPE,
TypeUtil.getFullyQualifiedDisplayName(itemTypeDataverseName, itemTypeName));
}
defaultNull = viewDetails.getDefaultNull();
temporalDataFormat = new Triple<>(viewDetails.getDatetimeFormat(), viewDetails.getDateFormat(),
viewDetails.getTimeFormat());
}
}
Expression normBody = viewDecl.getNormalizedViewBody();
if (normBody == null) {
normBody = rewriteViewBody(viewDecl, viewItemType, defaultNull, temporalDataFormat);
viewDecl.setNormalizedViewBody(normBody);
}
return viewDecl;
}
private Expression rewriteFunctionBody(FunctionDecl fnDecl) throws CompilationException {
FunctionSignature fs = fnDecl.getSignature();
return rewriteFunctionOrViewBody(fs.getDatabaseName(), fs.getDataverseName(), fs, fnDecl.getFuncBody(),
fnDecl.getParamList(), !fnDecl.isStored(), fnDecl.getSourceLocation());
}
private Expression rewriteViewBody(ViewDecl viewDecl, IAType viewItemType, Boolean defaultNull,
Triple<String, String, String> temporalDataFormat) throws CompilationException {
DatasetFullyQualifiedName viewName = viewDecl.getViewName();
SourceLocation sourceLoc = viewDecl.getSourceLocation();
Expression rewrittenBodyExpr =
rewriteFunctionOrViewBody(viewName.getDatabaseName(), viewName.getDataverseName(), viewName,
viewDecl.getViewBody(), Collections.emptyList(), false, sourceLoc);
if (viewItemType != null) {
if (!Boolean.TRUE.equals(defaultNull)) {
throw new CompilationException(ErrorCode.COMPILATION_ILLEGAL_STATE, sourceLoc,
"Default Null is required");
}
rewrittenBodyExpr = SqlppFunctionBodyRewriter.castViewBodyAsType(context, rewrittenBodyExpr, viewItemType,
temporalDataFormat, viewName, sourceLoc);
}
return rewrittenBodyExpr;
}
private Expression rewriteFunctionOrViewBody(String entityDatabaseName, DataverseName entityDataverseName,
Object entityDisplayName, Expression bodyExpr, List<VarIdentifier> externalVars,
boolean allowNonStoredUdfCalls, SourceLocation sourceLoc) throws CompilationException {
Namespace defaultNamespace = metadataProvider.getDefaultNamespace();
Namespace targetNamespace = null;
if (entityDataverseName == null || (entityDatabaseName.equals(defaultNamespace.getDatabaseName())
&& entityDataverseName.equals(defaultNamespace.getDataverseName()))) {
targetNamespace = defaultNamespace;
} else {
try {
Dataverse dv = metadataProvider.findDataverse(entityDatabaseName, entityDataverseName);
if (dv != null) {
targetNamespace = new Namespace(dv.getDatabaseName(), dv.getDataverseName());
}
} catch (AlgebricksException e) {
throw new CompilationException(ErrorCode.UNKNOWN_DATAVERSE, e, sourceLoc, entityDataverseName);
}
}
metadataProvider.setDefaultNamespace(targetNamespace);
try {
Query wrappedQuery = ExpressionUtils.createWrappedQuery(bodyExpr, sourceLoc);
getFunctionAndViewBodyRewriter().rewrite(context, wrappedQuery, allowNonStoredUdfCalls, false,
externalVars);
return wrappedQuery.getBody();
} catch (CompilationException e) {
throw new CompilationException(ErrorCode.COMPILATION_BAD_FUNCTION_DEFINITION, e,
entityDisplayName.toString(), e.getMessage());
} finally {
metadataProvider.setDefaultNamespace(defaultNamespace);
}
}
protected SqlppFunctionBodyRewriter getFunctionAndViewBodyRewriter() {
if (functionAndViewBodyRewriter == null) {
functionAndViewBodyRewriter = new SqlppFunctionBodyRewriter(parserFactory);
}
return functionAndViewBodyRewriter;
}
@Override
public Query createFunctionAccessorQuery(FunctionDecl functionDecl) {
// dataverse_name.function_name(MISSING, ... MISSING)
FunctionSignature functionSignature = functionDecl.getSignature();
int arity = functionSignature.getArity();
List<Expression> args = arity == FunctionIdentifier.VARARGS ? Collections.emptyList()
: Collections.nCopies(arity, new LiteralExpr(MissingLiteral.INSTANCE));
CallExpr fcall = new CallExpr(functionSignature, args);
fcall.setSourceLocation(functionDecl.getSourceLocation());
return ExpressionUtils.createWrappedQuery(fcall, functionDecl.getSourceLocation());
}
@Override
public Query createViewAccessorQuery(ViewDecl viewDecl) {
// dataverse_name.view_name
DataverseName dataverseName = viewDecl.getViewName().getDataverseName();
String viewName = viewDecl.getViewName().getDatasetName();
Expression vAccessExpr = createDatasetAccessExpression(dataverseName, viewName, viewDecl.getSourceLocation());
return ExpressionUtils.createWrappedQuery(vAccessExpr, viewDecl.getSourceLocation());
}
private static Expression createDatasetAccessExpression(DataverseName dataverseName, String datasetName,
SourceLocation sourceLoc) {
AbstractExpression resultExpr = null;
List<String> dataverseNameParts = dataverseName.getParts();
for (int i = 0, n = dataverseNameParts.size(); i < n; i++) {
String part = dataverseNameParts.get(i);
resultExpr = i == 0 ? new VariableExpr(new VarIdentifier(SqlppVariableUtil.toInternalVariableName(part)))
: new FieldAccessor(resultExpr, new Identifier(part));
resultExpr.setSourceLocation(sourceLoc);
}
resultExpr = new FieldAccessor(resultExpr, new Identifier(datasetName));
resultExpr.setSourceLocation(sourceLoc);
return resultExpr;
}
}