in asterixdb/asterix-app/src/main/java/org/apache/asterix/app/translator/QueryTranslator.java [3224:3419]
protected CreateResult doCreateFunction(MetadataProvider metadataProvider, CreateFunctionStatement cfs,
FunctionSignature functionSignature, IStatementRewriter stmtRewriter, IRequestParameters requestParameters,
Creator creator) throws Exception {
DataverseName dataverseName = functionSignature.getDataverseName();
String databaseName = functionSignature.getDatabaseName();
SourceLocation sourceLoc = cfs.getSourceLocation();
MetadataTransactionContext mdTxnCtx = MetadataManager.INSTANCE.beginTransaction();
metadataProvider.setMetadataTxnContext(mdTxnCtx);
try {
Dataverse dv = MetadataManager.INSTANCE.getDataverse(mdTxnCtx, databaseName, dataverseName);
if (dv == null) {
throw new CompilationException(ErrorCode.UNKNOWN_DATAVERSE, sourceLoc,
MetadataUtil.dataverseName(databaseName, dataverseName, metadataProvider.isUsingDatabase()));
}
Namespace ns = new Namespace(dv.getDatabaseName(), dv.getDataverseName());
List<TypeSignature> existingInlineTypes;
Function existingFunction = MetadataManager.INSTANCE.getFunction(mdTxnCtx, functionSignature);
if (existingFunction != null) {
if (cfs.getIfNotExists()) {
MetadataManager.INSTANCE.commitTransaction(mdTxnCtx);
return CreateResult.NOOP;
} else if (!cfs.getReplaceIfExists()) {
throw new CompilationException(ErrorCode.FUNCTION_EXISTS, cfs.getSourceLocation(),
functionSignature.toString(false));
}
existingInlineTypes = TypeUtil.getFunctionInlineTypes(existingFunction);
} else {
existingInlineTypes = Collections.emptyList();
}
IQueryRewriter queryRewriter = rewriterFactory.createQueryRewriter();
Map<TypeSignature, Datatype> newInlineTypes;
Function function;
if (cfs.isExternal()) {
if (functionSignature.getArity() == FunctionIdentifier.VARARGS) {
throw new CompilationException(ErrorCode.COMPILATION_ERROR, cfs.getSourceLocation(),
"Variable number of parameters is not supported for external functions");
}
List<Pair<VarIdentifier, TypeExpression>> paramList = cfs.getParameters();
int paramCount = paramList.size();
List<String> paramNames = new ArrayList<>(paramCount);
List<TypeSignature> paramTypes = new ArrayList<>(paramCount);
LinkedHashSet<TypeSignature> depTypes = new LinkedHashSet<>();
newInlineTypes = new HashMap<>();
for (int i = 0; i < paramCount; i++) {
Pair<VarIdentifier, TypeExpression> paramPair = paramList.get(i);
TypeSignature paramTypeSignature;
TypeSignature paramDepTypeSignature;
Datatype paramInlineTypeEntity;
TypeExpression paramTypeExpr = paramPair.getSecond();
if (paramTypeExpr != null) {
Triple<TypeSignature, TypeSignature, Datatype> paramTypeInfo = translateFunctionParameterType(
functionSignature, i, paramTypeExpr, sourceLoc, metadataProvider, mdTxnCtx);
paramTypeSignature = paramTypeInfo.first;
paramDepTypeSignature = paramTypeInfo.second;
paramInlineTypeEntity = paramTypeInfo.third;
} else {
paramTypeSignature = null; // == any
paramDepTypeSignature = null;
paramInlineTypeEntity = null;
}
paramTypes.add(paramTypeSignature); // null == any
if (paramDepTypeSignature != null) {
depTypes.add(paramDepTypeSignature);
}
if (paramInlineTypeEntity != null) {
newInlineTypes.put(paramTypeSignature, paramInlineTypeEntity);
}
VarIdentifier paramName = paramPair.getFirst();
paramNames.add(queryRewriter.toFunctionParameterName(paramName));
}
TypeSignature returnTypeSignature;
TypeSignature returnDepTypeSignature;
Datatype returnInlineTypeEntity;
TypeExpression returnTypeExpr = cfs.getReturnType();
if (returnTypeExpr != null) {
Triple<TypeSignature, TypeSignature, Datatype> returnTypeInfo = translateFunctionParameterType(
functionSignature, -1, returnTypeExpr, sourceLoc, metadataProvider, mdTxnCtx);
returnTypeSignature = returnTypeInfo.first;
returnDepTypeSignature = returnTypeInfo.second;
returnInlineTypeEntity = returnTypeInfo.third;
} else {
returnTypeSignature = null; // == any
returnDepTypeSignature = null;
returnInlineTypeEntity = null;
}
if (returnDepTypeSignature != null) {
depTypes.add(returnDepTypeSignature);
}
if (returnInlineTypeEntity != null) {
newInlineTypes.put(returnTypeSignature, returnInlineTypeEntity);
}
DataverseName libraryDataverseName = cfs.getLibraryDataverseName();
String libraryDatabaseName = cfs.getLibraryDatabaseName();
if (libraryDataverseName == null) {
libraryDataverseName = dataverseName;
libraryDatabaseName = databaseName;
}
String libraryName = cfs.getLibraryName();
Library library = MetadataManager.INSTANCE.getLibrary(mdTxnCtx, libraryDatabaseName,
libraryDataverseName, libraryName);
if (library == null) {
throw new CompilationException(ErrorCode.UNKNOWN_LIBRARY, sourceLoc, libraryName);
}
ExternalFunctionLanguage language =
ExternalFunctionCompilerUtil.getExternalFunctionLanguage(library.getLanguage());
List<String> externalIdentifier = cfs.getExternalIdentifier();
ExternalFunctionCompilerUtil.validateExternalIdentifier(externalIdentifier, language,
cfs.getSourceLocation());
List<List<DependencyFullyQualifiedName>> dependencies =
FunctionUtil.getExternalFunctionDependencies(depTypes);
function = new Function(functionSignature, paramNames, paramTypes, returnTypeSignature, null,
FunctionKind.SCALAR.toString(), library.getLanguage(), libraryDatabaseName,
libraryDataverseName, libraryName, externalIdentifier, cfs.getNullCall(),
cfs.getDeterministic(), cfs.getResources(), dependencies, creator);
} else {
List<Pair<VarIdentifier, TypeExpression>> paramList = cfs.getParameters();
int paramCount = paramList.size();
List<VarIdentifier> paramVars = new ArrayList<>(paramCount);
List<String> paramNames = new ArrayList<>(paramCount);
for (Pair<VarIdentifier, TypeExpression> paramPair : paramList) {
VarIdentifier paramName = paramPair.getFirst();
paramVars.add(paramName);
paramNames.add(queryRewriter.toFunctionParameterName(paramName));
if (paramPair.getSecond() != null) {
throw new CompilationException(ErrorCode.COMPILATION_ILLEGAL_STATE, sourceLoc,
paramName.toString());
}
}
// Check whether the function is usable:
// create a function declaration for this function,
// and a query body calls this function with each argument set to 'missing'
FunctionDecl fd = new FunctionDecl(functionSignature, paramVars, cfs.getFunctionBodyExpression(), true);
fd.setSourceLocation(sourceLoc);
Query wrappedQuery = queryRewriter.createFunctionAccessorQuery(fd);
List<FunctionDecl> fdList = new ArrayList<>(declaredFunctions.size() + 1);
fdList.addAll(declaredFunctions);
fdList.add(fd);
metadataProvider.setDefaultNamespace(ns);
LangRewritingContext langRewritingContext = createLangRewritingContext(metadataProvider, fdList, null,
null, warningCollector, wrappedQuery.getVarCounter());
apiFramework.reWriteQuery(langRewritingContext, wrappedQuery, sessionOutput, false, false,
Collections.emptyList());
List<List<DependencyFullyQualifiedName>> dependencies =
FunctionUtil.getFunctionDependencies(metadataProvider, fd, queryRewriter);
appCtx.getReceptionist().ensureAuthorized(requestParameters, metadataProvider);
newInlineTypes = Collections.emptyMap();
function = new Function(functionSignature, paramNames, null, null, cfs.getFunctionBody(),
FunctionKind.SCALAR.toString(), compilationProvider.getParserFactory().getLanguage(), null,
null, null, null, null, null, null, dependencies, creator);
}
if (existingFunction == null) {
// add new function and its inline types
for (Datatype newInlineType : newInlineTypes.values()) {
MetadataManager.INSTANCE.addDatatype(mdTxnCtx, newInlineType);
}
MetadataManager.INSTANCE.addFunction(mdTxnCtx, function);
} else {
// replace existing function and its inline types
for (TypeSignature existingInlineType : existingInlineTypes) {
Datatype newInlineType =
newInlineTypes.isEmpty() ? null : newInlineTypes.remove(existingInlineType);
if (newInlineType == null) {
MetadataManager.INSTANCE.dropDatatype(mdTxnCtx, existingInlineType.getDatabaseName(),
existingInlineType.getDataverseName(), existingInlineType.getName());
} else {
MetadataManager.INSTANCE.updateDatatype(mdTxnCtx, newInlineType);
}
}
for (Datatype inlineType : newInlineTypes.values()) {
MetadataManager.INSTANCE.addDatatype(mdTxnCtx, inlineType);
}
MetadataManager.INSTANCE.updateFunction(mdTxnCtx, function);
}
beforeTxnCommit(metadataProvider, creator, EntityDetails.newFunction(functionSignature));
MetadataManager.INSTANCE.commitTransaction(mdTxnCtx);
if (LOGGER.isInfoEnabled()) {
LOGGER.info("Installed function: " + functionSignature);
}
return existingFunction != null ? CreateResult.REPLACED : CreateResult.CREATED;
} catch (Exception e) {
abort(e, e, mdTxnCtx);
throw e;
}
}