From 66a7ededd32b10211e453c3292b6fbb5cdd6b1b0 Mon Sep 17 00:00:00 2001 From: Vivek1106-04 Date: Wed, 10 Jun 2026 10:58:44 +0530 Subject: [PATCH 1/3] feat: add function_metadata() datasource function Adds a builtin datasource function that exposes the runtime function registry as queryable rows: { name, arity, category, private }. Category is derived from existing engine predicates (window, aggregate, aggregate-scalar, unnest, datasource, scalar), letting clients filter instead of hardcoding function lists. - BuiltinFunctions.getBuiltinFunctionIdentifiers(): read-only registry accessor - FunctionMetadata{Rewriter,Datasource,Function,Reader}: modeled on jobs()/active_requests() - registered (public) in MetadataBuiltinFunctions - runtime test under misc/function_metadata Change-Id: I58db212ca4876e634e1e28fca37f75f96f087c6d --- .../function/FunctionMetadataDatasource.java | 53 +++++++++++ .../function/FunctionMetadataFunction.java | 93 +++++++++++++++++++ .../app/function/FunctionMetadataReader.java | 47 ++++++++++ .../function/FunctionMetadataRewriter.java | 42 +++++++++ .../util/MetadataBuiltinFunctions.java | 7 ++ .../function_metadata.1.query.sqlpp | 23 +++++ .../function_metadata/function_metadata.1.adm | 2 + .../resources/runtimets/sqlpp_queries.xml | 5 + .../om/functions/BuiltinFunctions.java | 4 + 9 files changed, 276 insertions(+) create mode 100644 asterixdb/asterix-app/src/main/java/org/apache/asterix/app/function/FunctionMetadataDatasource.java create mode 100644 asterixdb/asterix-app/src/main/java/org/apache/asterix/app/function/FunctionMetadataFunction.java create mode 100644 asterixdb/asterix-app/src/main/java/org/apache/asterix/app/function/FunctionMetadataReader.java create mode 100644 asterixdb/asterix-app/src/main/java/org/apache/asterix/app/function/FunctionMetadataRewriter.java create mode 100644 asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/function_metadata/function_metadata.1.query.sqlpp create mode 100644 asterixdb/asterix-app/src/test/resources/runtimets/results/misc/function_metadata/function_metadata.1.adm diff --git a/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/function/FunctionMetadataDatasource.java b/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/function/FunctionMetadataDatasource.java new file mode 100644 index 00000000000..f9b68515e3d --- /dev/null +++ b/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/function/FunctionMetadataDatasource.java @@ -0,0 +1,53 @@ +/* + * 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.app.function; + +import java.util.Objects; + +import org.apache.asterix.metadata.api.IDatasourceFunction; +import org.apache.asterix.metadata.declared.DataSourceId; +import org.apache.asterix.metadata.declared.FunctionDataSource; +import org.apache.asterix.metadata.declared.MetadataProvider; +import org.apache.hyracks.algebricks.common.constraints.AlgebricksAbsolutePartitionConstraint; +import org.apache.hyracks.algebricks.common.exceptions.AlgebricksException; +import org.apache.hyracks.algebricks.core.algebra.properties.INodeDomain; + +public class FunctionMetadataDatasource extends FunctionDataSource { + + private static final DataSourceId FUNCTION_METADATA_DATASOURCE_ID = + createDataSourceId(FunctionMetadataRewriter.FUNCTION_METADATA); + + public FunctionMetadataDatasource(INodeDomain domain) throws AlgebricksException { + super(FUNCTION_METADATA_DATASOURCE_ID, FunctionMetadataRewriter.FUNCTION_METADATA, domain); + } + + @Override + protected IDatasourceFunction createFunction(MetadataProvider metadataProvider, + AlgebricksAbsolutePartitionConstraint locations) { + // The builtin function registry is identical on every node, so run on a single + // location to avoid emitting duplicate rows. + return new FunctionMetadataFunction( + AlgebricksAbsolutePartitionConstraint.randomLocation(locations.getLocations())); + } + + @Override + protected boolean sameFunctionDatasource(FunctionDataSource other) { + return Objects.equals(this.functionId, other.getFunctionId()); + } +} diff --git a/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/function/FunctionMetadataFunction.java b/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/function/FunctionMetadataFunction.java new file mode 100644 index 00000000000..d8c9ba22217 --- /dev/null +++ b/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/function/FunctionMetadataFunction.java @@ -0,0 +1,93 @@ +/* + * 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.app.function; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.asterix.external.api.IRecordReader; +import org.apache.asterix.metadata.declared.AbstractDatasourceFunction; +import org.apache.asterix.om.functions.BuiltinFunctionInfo; +import org.apache.asterix.om.functions.BuiltinFunctions; +import org.apache.hyracks.algebricks.common.constraints.AlgebricksAbsolutePartitionConstraint; +import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier; +import org.apache.hyracks.api.context.IHyracksTaskContext; +import org.apache.hyracks.api.exceptions.HyracksDataException; + +public class FunctionMetadataFunction extends AbstractDatasourceFunction { + + private static final long serialVersionUID = 1L; + + public FunctionMetadataFunction(AlgebricksAbsolutePartitionConstraint locations) { + super(locations); + } + + @Override + public IRecordReader createRecordReader(IHyracksTaskContext ctx, int partition) + throws HyracksDataException { + List records = new ArrayList<>(); + for (FunctionIdentifier fi : BuiltinFunctions.getBuiltinFunctionIdentifiers()) { + records.add(toRecord(fi)); + } + return new FunctionMetadataReader(records.toArray(new String[0])); + } + + private static String toRecord(FunctionIdentifier fi) { + BuiltinFunctionInfo info = BuiltinFunctions.getBuiltinFunctionInfo(fi); + boolean isPrivate = info != null && info.isPrivate(); + return "{\"name\":\"" + escape(fi.getName()) + "\",\"arity\":" + fi.getArity() + ",\"category\":\"" + + category(fi) + "\",\"private\":" + isPrivate + "}"; + } + + private static String category(FunctionIdentifier fi) { + if (BuiltinFunctions.isWindowFunction(fi) || BuiltinFunctions.getWindowFunction(fi) != null) { + // isWindowFunction matches the internal *-impl ids; getWindowFunction matches the + // user-facing names (row_number, rank, ...) that map to those impls. + return "window"; + } + if (BuiltinFunctions.isBuiltinAggregateFunction(fi)) { + return "aggregate"; + } + if (BuiltinFunctions.isBuiltinScalarAggregateFunction(fi)) { + return "aggregate-scalar"; + } + if (BuiltinFunctions.isBuiltinUnnestingFunction(fi)) { + return "unnest"; + } + if (BuiltinFunctions.getDatasourceTransformer(fi) != null) { + return "datasource"; + } + return "scalar"; + } + + private static String escape(String s) { + StringBuilder sb = new StringBuilder(s.length()); + for (int i = 0; i < s.length(); i++) { + char c = s.charAt(i); + if (c == '"' || c == '\\') { + sb.append('\\').append(c); + } else if (c < 0x20) { + sb.append(String.format("\\u%04x", (int) c)); + } else { + sb.append(c); + } + } + return sb.toString(); + } +} diff --git a/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/function/FunctionMetadataReader.java b/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/function/FunctionMetadataReader.java new file mode 100644 index 00000000000..27f6531c5dd --- /dev/null +++ b/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/function/FunctionMetadataReader.java @@ -0,0 +1,47 @@ +/* + * 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.app.function; + +import java.io.IOException; + +import org.apache.asterix.external.api.IRawRecord; +import org.apache.asterix.external.input.record.CharArrayRecord; + +public class FunctionMetadataReader extends FunctionReader { + + private final String[] functions; + private int pos = 0; + + public FunctionMetadataReader(String[] functions) { + this.functions = functions; + } + + @Override + public boolean hasNext() throws Exception { + return pos < functions.length; + } + + @Override + public IRawRecord next() throws IOException, InterruptedException { + CharArrayRecord record = new CharArrayRecord(); + record.set(functions[pos++]); + record.endRecord(); + return record; + } +} diff --git a/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/function/FunctionMetadataRewriter.java b/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/function/FunctionMetadataRewriter.java new file mode 100644 index 00000000000..2683f01c764 --- /dev/null +++ b/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/function/FunctionMetadataRewriter.java @@ -0,0 +1,42 @@ +/* + * 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.app.function; + +import org.apache.asterix.common.functions.FunctionConstants; +import org.apache.asterix.metadata.declared.FunctionDataSource; +import org.apache.hyracks.algebricks.common.exceptions.AlgebricksException; +import org.apache.hyracks.algebricks.core.algebra.base.IOptimizationContext; +import org.apache.hyracks.algebricks.core.algebra.expressions.AbstractFunctionCallExpression; +import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier; + +public class FunctionMetadataRewriter extends FunctionRewriter { + + public static final FunctionIdentifier FUNCTION_METADATA = FunctionConstants.newAsterix("function_metadata", 0); + public static final FunctionMetadataRewriter INSTANCE = new FunctionMetadataRewriter(FUNCTION_METADATA); + + private FunctionMetadataRewriter(FunctionIdentifier functionId) { + super(functionId); + } + + @Override + protected FunctionDataSource toDatasource(IOptimizationContext context, AbstractFunctionCallExpression f) + throws AlgebricksException { + return new FunctionMetadataDatasource(context.getComputationNodeDomain()); + } +} diff --git a/asterixdb/asterix-app/src/main/java/org/apache/asterix/util/MetadataBuiltinFunctions.java b/asterixdb/asterix-app/src/main/java/org/apache/asterix/util/MetadataBuiltinFunctions.java index e86eb8a0adb..9866ee0561a 100644 --- a/asterixdb/asterix-app/src/main/java/org/apache/asterix/util/MetadataBuiltinFunctions.java +++ b/asterixdb/asterix-app/src/main/java/org/apache/asterix/util/MetadataBuiltinFunctions.java @@ -24,6 +24,7 @@ import org.apache.asterix.app.function.DatasetRewriter; import org.apache.asterix.app.function.DumpIndexRewriter; import org.apache.asterix.app.function.FeedRewriter; +import org.apache.asterix.app.function.FunctionMetadataRewriter; import org.apache.asterix.app.function.JobSummariesRewriter; import org.apache.asterix.app.function.PingRewriter; import org.apache.asterix.app.function.QueryIndexRewriter; @@ -119,6 +120,12 @@ public class MetadataBuiltinFunctions { BuiltinFunctions.addUnnestFun(CollectionEstimateColumnCountRewriter.ESTIMATED_COLLECTION_COLUMN_COUNT, true); BuiltinFunctions.addDatasourceFunction(CollectionEstimateColumnCountRewriter.ESTIMATED_COLLECTION_COLUMN_COUNT, CollectionEstimateColumnCountRewriter.INSTANCE); + // Function metadata function + BuiltinFunctions.addFunction(FunctionMetadataRewriter.FUNCTION_METADATA, + (expression, env, mp) -> RecordUtil.FULLY_OPEN_RECORD_TYPE, true); + BuiltinFunctions.addUnnestFun(FunctionMetadataRewriter.FUNCTION_METADATA, true); + BuiltinFunctions.addDatasourceFunction(FunctionMetadataRewriter.FUNCTION_METADATA, + FunctionMetadataRewriter.INSTANCE, BuiltinFunctions.DataSourceFunctionProperty.MIN_MEMORY_BUDGET); } private MetadataBuiltinFunctions() { diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/function_metadata/function_metadata.1.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/function_metadata/function_metadata.1.query.sqlpp new file mode 100644 index 00000000000..59c2cc48538 --- /dev/null +++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/function_metadata/function_metadata.1.query.sqlpp @@ -0,0 +1,23 @@ +/* + * 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. + */ + +select value f +from function_metadata() f +where f.name in ["rank", "row_number"] +order by f.name; diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/misc/function_metadata/function_metadata.1.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/misc/function_metadata/function_metadata.1.adm new file mode 100644 index 00000000000..8f5580025d2 --- /dev/null +++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/misc/function_metadata/function_metadata.1.adm @@ -0,0 +1,2 @@ +{ "name": "rank", "arity": 0, "category": "window", "private": false } +{ "name": "row_number", "arity": 0, "category": "window", "private": false } diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/sqlpp_queries.xml b/asterixdb/asterix-app/src/test/resources/runtimets/sqlpp_queries.xml index b4672e17cdb..9dd96d13048 100644 --- a/asterixdb/asterix-app/src/test/resources/runtimets/sqlpp_queries.xml +++ b/asterixdb/asterix-app/src/test/resources/runtimets/sqlpp_queries.xml @@ -7548,6 +7548,11 @@ jobs + + + function_metadata + + completed_requests diff --git a/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/functions/BuiltinFunctions.java b/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/functions/BuiltinFunctions.java index 63543dd551a..e6fb45a570d 100644 --- a/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/functions/BuiltinFunctions.java +++ b/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/functions/BuiltinFunctions.java @@ -3079,6 +3079,10 @@ public static BuiltinFunctionInfo getBuiltinFunctionInfo(FunctionIdentifier fi) return registeredFunctions.get(fi); } + public static Set getBuiltinFunctionIdentifiers() { + return Collections.unmodifiableSet(registeredFunctions.keySet()); + } + public static BuiltinFunctionInfo resolveBuiltinFunction(String name, int arity) { //TODO:optimize BuiltinFunctionInfo finfo; From f9e1f743f41ef5e2deb034f697a14a008a5c9d86 Mon Sep 17 00:00:00 2001 From: Vivek1106-04 Date: Wed, 10 Jun 2026 15:54:29 +0530 Subject: [PATCH 2/3] fix: use hyphen in function-metadata identifier so resolver finds it Change-Id: I67524655033cfeeb86e026b4a3cacdb8b8bfd244 --- .../apache/asterix/app/function/FunctionMetadataRewriter.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/function/FunctionMetadataRewriter.java b/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/function/FunctionMetadataRewriter.java index 2683f01c764..ae6027f7225 100644 --- a/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/function/FunctionMetadataRewriter.java +++ b/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/function/FunctionMetadataRewriter.java @@ -27,7 +27,7 @@ public class FunctionMetadataRewriter extends FunctionRewriter { - public static final FunctionIdentifier FUNCTION_METADATA = FunctionConstants.newAsterix("function_metadata", 0); + public static final FunctionIdentifier FUNCTION_METADATA = FunctionConstants.newAsterix("function-metadata", 0); public static final FunctionMetadataRewriter INSTANCE = new FunctionMetadataRewriter(FUNCTION_METADATA); private FunctionMetadataRewriter(FunctionIdentifier functionId) { From efafffac22fc00ea9051c5ae480bd6b80290c44f Mon Sep 17 00:00:00 2001 From: Vivek1106-04 Date: Wed, 17 Jun 2026 14:55:51 +0530 Subject: [PATCH 3/3] feat: enrich function_metadata() schema (name, aliases, kind/udf) and fix datasource category Change-Id: I4c8647c30caf34a8b302830e7cb567629719c852 --- .../function/FunctionMetadataDatasource.java | 33 ++++++- .../function/FunctionMetadataFunction.java | 83 +++++++++++++++--- .../FunctionMetadataFunctionTest.java | 87 +++++++++++++++++++ .../function_metadata.1.query.sqlpp | 4 +- .../function_metadata.2.query.sqlpp | 25 ++++++ .../function_metadata.3.query.sqlpp | 24 +++++ .../function_metadata.4.query.sqlpp | 24 +++++ .../function_metadata.5.query.sqlpp | 24 +++++ .../function_metadata.6.ddl.sqlpp | 24 +++++ .../function_metadata.7.query.sqlpp | 24 +++++ .../function_metadata.8.ddl.sqlpp | 20 +++++ .../function_metadata/function_metadata.1.adm | 4 +- .../function_metadata/function_metadata.2.adm | 1 + .../function_metadata/function_metadata.3.adm | 1 + .../function_metadata/function_metadata.4.adm | 2 + .../function_metadata/function_metadata.5.adm | 1 + .../function_metadata/function_metadata.7.adm | 1 + .../common/util/CommonFunctionMapUtil.java | 9 ++ 18 files changed, 376 insertions(+), 15 deletions(-) create mode 100644 asterixdb/asterix-app/src/test/java/org/apache/asterix/app/function/FunctionMetadataFunctionTest.java create mode 100644 asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/function_metadata/function_metadata.2.query.sqlpp create mode 100644 asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/function_metadata/function_metadata.3.query.sqlpp create mode 100644 asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/function_metadata/function_metadata.4.query.sqlpp create mode 100644 asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/function_metadata/function_metadata.5.query.sqlpp create mode 100644 asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/function_metadata/function_metadata.6.ddl.sqlpp create mode 100644 asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/function_metadata/function_metadata.7.query.sqlpp create mode 100644 asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/function_metadata/function_metadata.8.ddl.sqlpp create mode 100644 asterixdb/asterix-app/src/test/resources/runtimets/results/misc/function_metadata/function_metadata.2.adm create mode 100644 asterixdb/asterix-app/src/test/resources/runtimets/results/misc/function_metadata/function_metadata.3.adm create mode 100644 asterixdb/asterix-app/src/test/resources/runtimets/results/misc/function_metadata/function_metadata.4.adm create mode 100644 asterixdb/asterix-app/src/test/resources/runtimets/results/misc/function_metadata/function_metadata.5.adm create mode 100644 asterixdb/asterix-app/src/test/resources/runtimets/results/misc/function_metadata/function_metadata.7.adm diff --git a/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/function/FunctionMetadataDatasource.java b/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/function/FunctionMetadataDatasource.java index f9b68515e3d..0d0d890d140 100644 --- a/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/function/FunctionMetadataDatasource.java +++ b/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/function/FunctionMetadataDatasource.java @@ -18,12 +18,20 @@ */ package org.apache.asterix.app.function; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Locale; import java.util.Objects; +import org.apache.asterix.metadata.MetadataManager; +import org.apache.asterix.metadata.MetadataTransactionContext; import org.apache.asterix.metadata.api.IDatasourceFunction; import org.apache.asterix.metadata.declared.DataSourceId; import org.apache.asterix.metadata.declared.FunctionDataSource; import org.apache.asterix.metadata.declared.MetadataProvider; +import org.apache.asterix.metadata.entities.Dataverse; +import org.apache.asterix.metadata.entities.Function; import org.apache.hyracks.algebricks.common.constraints.AlgebricksAbsolutePartitionConstraint; import org.apache.hyracks.algebricks.common.exceptions.AlgebricksException; import org.apache.hyracks.algebricks.core.algebra.properties.INodeDomain; @@ -39,11 +47,32 @@ public FunctionMetadataDatasource(INodeDomain domain) throws AlgebricksException @Override protected IDatasourceFunction createFunction(MetadataProvider metadataProvider, - AlgebricksAbsolutePartitionConstraint locations) { + AlgebricksAbsolutePartitionConstraint locations) throws AlgebricksException { + // UDFs live in the metadata catalog (only reachable from the CC), so resolve them here and + // ship the pre-built rows to the node. Builtins are read from the static registry on the node. + List udfRecords = collectUdfRecords(metadataProvider); // The builtin function registry is identical on every node, so run on a single // location to avoid emitting duplicate rows. return new FunctionMetadataFunction( - AlgebricksAbsolutePartitionConstraint.randomLocation(locations.getLocations())); + AlgebricksAbsolutePartitionConstraint.randomLocation(locations.getLocations()), udfRecords); + } + + private static List collectUdfRecords(MetadataProvider metadataProvider) throws AlgebricksException { + MetadataTransactionContext mdTxnCtx = metadataProvider.getMetadataTxnContext(); + List records = new ArrayList<>(); + if (mdTxnCtx == null) { + return records; + } + for (Dataverse dv : MetadataManager.INSTANCE.getDataverses(mdTxnCtx)) { + List functions = MetadataManager.INSTANCE.getDataverseFunctions(mdTxnCtx, dv.getDatabaseName(), + dv.getDataverseName()); + for (Function fn : functions) { + String category = fn.getKind() == null ? "scalar" : fn.getKind().toLowerCase(Locale.ROOT); + records.add(FunctionMetadataFunction.buildRecord(fn.getName(), fn.getArity(), category, false, + FunctionMetadataFunction.KIND_UDF, fn.getDataverseName().toString(), Collections.emptyList())); + } + } + return records; } @Override diff --git a/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/function/FunctionMetadataFunction.java b/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/function/FunctionMetadataFunction.java index d8c9ba22217..78059272f7a 100644 --- a/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/function/FunctionMetadataFunction.java +++ b/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/function/FunctionMetadataFunction.java @@ -19,9 +19,13 @@ package org.apache.asterix.app.function; import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; import java.util.List; +import java.util.Map; import org.apache.asterix.external.api.IRecordReader; +import org.apache.asterix.lang.common.util.CommonFunctionMapUtil; import org.apache.asterix.metadata.declared.AbstractDatasourceFunction; import org.apache.asterix.om.functions.BuiltinFunctionInfo; import org.apache.asterix.om.functions.BuiltinFunctions; @@ -32,30 +36,66 @@ public class FunctionMetadataFunction extends AbstractDatasourceFunction { - private static final long serialVersionUID = 1L; + private static final long serialVersionUID = 2L; - public FunctionMetadataFunction(AlgebricksAbsolutePartitionConstraint locations) { + static final String KIND_BUILTIN = "builtin"; + static final String KIND_UDF = "udf"; + + // UDF rows are resolved on the CC (they live in the metadata catalog) and shipped here. + private final List udfRecords; + + public FunctionMetadataFunction(AlgebricksAbsolutePartitionConstraint locations, List udfRecords) { super(locations); + this.udfRecords = udfRecords; } @Override public IRecordReader createRecordReader(IHyracksTaskContext ctx, int partition) throws HyracksDataException { List records = new ArrayList<>(); + Map> aliasIndex = buildAliasIndex(); for (FunctionIdentifier fi : BuiltinFunctions.getBuiltinFunctionIdentifiers()) { - records.add(toRecord(fi)); + records.add(toBuiltinRecord(fi, aliasIndex)); } + records.addAll(udfRecords); return new FunctionMetadataReader(records.toArray(new String[0])); } - private static String toRecord(FunctionIdentifier fi) { + private static String toBuiltinRecord(FunctionIdentifier fi, Map> aliasIndex) { BuiltinFunctionInfo info = BuiltinFunctions.getBuiltinFunctionInfo(fi); boolean isPrivate = info != null && info.isPrivate(); - return "{\"name\":\"" + escape(fi.getName()) + "\",\"arity\":" + fi.getArity() + ",\"category\":\"" - + category(fi) + "\",\"private\":" + isPrivate + "}"; + List aliases = aliasIndex.getOrDefault(fi.getName(), Collections.emptyList()); + return buildRecord(underscore(fi.getName()), fi.getArity(), category(fi), isPrivate, KIND_BUILTIN, null, + aliases); + } + + /** + * Builds a single JSON row. Shared by builtin rows (here) and UDF rows (resolved on the CC in + * {@link FunctionMetadataDatasource}) so both sides emit an identical schema. + */ + static String buildRecord(String name, int arity, String category, boolean isPrivate, String kind, String dataverse, + List aliases) { + StringBuilder sb = new StringBuilder(); + sb.append("{\"name\":\"").append(escape(name)).append("\",\"arity\":").append(arity).append(",\"category\":\"") + .append(escape(category)).append("\",\"private\":").append(isPrivate).append(",\"kind\":\"") + .append(escape(kind)).append("\",\"dataverse\":"); + if (dataverse == null) { + sb.append("null"); + } else { + sb.append('"').append(escape(dataverse)).append('"'); + } + sb.append(",\"aliases\":["); + for (int i = 0; i < aliases.size(); i++) { + if (i > 0) { + sb.append(','); + } + sb.append('"').append(escape(aliases.get(i))).append('"'); + } + sb.append("]}"); + return sb.toString(); } - private static String category(FunctionIdentifier fi) { + static String category(FunctionIdentifier fi) { if (BuiltinFunctions.isWindowFunction(fi) || BuiltinFunctions.getWindowFunction(fi) != null) { // isWindowFunction matches the internal *-impl ids; getWindowFunction matches the // user-facing names (row_number, rank, ...) that map to those impls. @@ -67,15 +107,38 @@ private static String category(FunctionIdentifier fi) { if (BuiltinFunctions.isBuiltinScalarAggregateFunction(fi)) { return "aggregate-scalar"; } - if (BuiltinFunctions.isBuiltinUnnestingFunction(fi)) { - return "unnest"; - } + // Datasource (table-valued) functions are registered as BOTH unnest and datasource, so the + // datasource check MUST come before the unnest check; otherwise every datasource function + // (function_metadata, active_requests, jobs, ...) would be mislabeled "unnest". if (BuiltinFunctions.getDatasourceTransformer(fi) != null) { return "datasource"; } + if (BuiltinFunctions.isBuiltinUnnestingFunction(fi)) { + return "unnest"; + } return "scalar"; } + /** + * Builds an index from an internal (hyphenated) function name to its callable aliases, derived + * from {@link CommonFunctionMapUtil}. Alias spellings are normalized to the underscore form so + * they line up with the {@code name} column. + */ + static Map> buildAliasIndex() { + Map> index = new HashMap<>(); + for (Map.Entry e : CommonFunctionMapUtil.getFunctionMappings().entrySet()) { + index.computeIfAbsent(e.getValue(), k -> new ArrayList<>()).add(underscore(e.getKey())); + } + for (List aliases : index.values()) { + Collections.sort(aliases); + } + return index; + } + + static String underscore(String name) { + return name.replace('-', '_'); + } + private static String escape(String s) { StringBuilder sb = new StringBuilder(s.length()); for (int i = 0; i < s.length(); i++) { diff --git a/asterixdb/asterix-app/src/test/java/org/apache/asterix/app/function/FunctionMetadataFunctionTest.java b/asterixdb/asterix-app/src/test/java/org/apache/asterix/app/function/FunctionMetadataFunctionTest.java new file mode 100644 index 00000000000..444595e6b12 --- /dev/null +++ b/asterixdb/asterix-app/src/test/java/org/apache/asterix/app/function/FunctionMetadataFunctionTest.java @@ -0,0 +1,87 @@ +/* + * 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.app.function; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +import org.apache.asterix.om.functions.BuiltinFunctions; +import org.junit.Test; + +public class FunctionMetadataFunctionTest { + + @Test + public void buildRecordBuiltinWithAlias() { + String row = FunctionMetadataFunction.buildRecord("string_length", 2, "scalar", false, + FunctionMetadataFunction.KIND_BUILTIN, null, Collections.singletonList("length")); + assertEquals("{\"name\":\"string_length\",\"arity\":2,\"category\":\"scalar\",\"private\":false," + + "\"kind\":\"builtin\",\"dataverse\":null,\"aliases\":[\"length\"]}", row); + } + + @Test + public void buildRecordUdfWithDataverseAndNoAlias() { + String row = FunctionMetadataFunction.buildRecord("fn_meta_udf", 1, "scalar", false, + FunctionMetadataFunction.KIND_UDF, "Default", Collections.emptyList()); + assertEquals("{\"name\":\"fn_meta_udf\",\"arity\":1,\"category\":\"scalar\",\"private\":false," + + "\"kind\":\"udf\",\"dataverse\":\"Default\",\"aliases\":[]}", row); + } + + @Test + public void buildRecordRendersMultipleAliasesAndVarargs() { + String row = FunctionMetadataFunction.buildRecord("array_concat", -1, "scalar", true, + FunctionMetadataFunction.KIND_BUILTIN, null, Arrays.asList("a", "b")); + assertEquals("{\"name\":\"array_concat\",\"arity\":-1,\"category\":\"scalar\",\"private\":true," + + "\"kind\":\"builtin\",\"dataverse\":null,\"aliases\":[\"a\",\"b\"]}", row); + } + + @Test + public void buildRecordEscapesQuotesBackslashesAndControlChars() { + String name = "a\"b\\c" + (char) 1; + String row = FunctionMetadataFunction.buildRecord(name, 0, "scalar", false, + FunctionMetadataFunction.KIND_BUILTIN, null, Collections.emptyList()); + assertTrue(row.contains("\"name\":\"a\\\"b\\\\c\\u0001\"")); + } + + @Test + public void categoryDistinguishesAllBuiltinKinds() { + assertEquals("window", FunctionMetadataFunction.category(BuiltinFunctions.RANK)); + assertEquals("aggregate", FunctionMetadataFunction.category(BuiltinFunctions.COUNT)); + assertEquals("aggregate-scalar", FunctionMetadataFunction.category(BuiltinFunctions.SCALAR_COUNT)); + assertEquals("unnest", FunctionMetadataFunction.category(BuiltinFunctions.SCAN_COLLECTION)); + assertEquals("scalar", FunctionMetadataFunction.category(BuiltinFunctions.STRING_LENGTH)); + } + + @Test + public void underscoreNormalizesHyphenatedNames() { + assertEquals("a_b_c", FunctionMetadataFunction.underscore("a-b-c")); + assertEquals("plain", FunctionMetadataFunction.underscore("plain")); + } + + @Test + public void aliasIndexIsSortedAndUnderscoreNormalized() { + Map> index = FunctionMetadataFunction.buildAliasIndex(); + assertEquals(Collections.singletonList("length"), index.get("string-length")); + assertEquals(Arrays.asList("pos", "pos0", "position0"), index.get("position")); + } +} diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/function_metadata/function_metadata.1.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/function_metadata/function_metadata.1.query.sqlpp index 59c2cc48538..19218204be0 100644 --- a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/function_metadata/function_metadata.1.query.sqlpp +++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/function_metadata/function_metadata.1.query.sqlpp @@ -17,7 +17,9 @@ * under the License. */ -select value f +// Window-function rows are present and the name is emitted in the underscore (callable) form. + +select value f.name from function_metadata() f where f.name in ["rank", "row_number"] order by f.name; diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/function_metadata/function_metadata.2.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/function_metadata/function_metadata.2.query.sqlpp new file mode 100644 index 00000000000..9989bd12eda --- /dev/null +++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/function_metadata/function_metadata.2.query.sqlpp @@ -0,0 +1,25 @@ +/* + * 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. + */ + +// Datasource (table-valued) functions report "datasource", not "unnest". function_metadata() +// itself is registered as both unnest and datasource; the datasource check must win. + +select value f.category +from function_metadata() f +where f.name = "function_metadata"; diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/function_metadata/function_metadata.3.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/function_metadata/function_metadata.3.query.sqlpp new file mode 100644 index 00000000000..0ec4b594444 --- /dev/null +++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/function_metadata/function_metadata.3.query.sqlpp @@ -0,0 +1,24 @@ +/* + * 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. + */ + +// Variadic functions report arity -1. + +select value f.arity +from function_metadata() f +where f.name = "array_concat"; diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/function_metadata/function_metadata.4.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/function_metadata/function_metadata.4.query.sqlpp new file mode 100644 index 00000000000..0176d3be17b --- /dev/null +++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/function_metadata/function_metadata.4.query.sqlpp @@ -0,0 +1,24 @@ +/* + * 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. + */ + +// The private flag is populated for both public and private functions. + +select distinct value p +from (select value f.private from function_metadata() f) p +order by p; diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/function_metadata/function_metadata.5.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/function_metadata/function_metadata.5.query.sqlpp new file mode 100644 index 00000000000..f0dc7c143b8 --- /dev/null +++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/function_metadata/function_metadata.5.query.sqlpp @@ -0,0 +1,24 @@ +/* + * 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. + */ + +// Aliases are listed in the underscore (callable) form. "length" is an alias of string-length. + +select value f.aliases +from function_metadata() f +where f.name = "string_length"; diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/function_metadata/function_metadata.6.ddl.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/function_metadata/function_metadata.6.ddl.sqlpp new file mode 100644 index 00000000000..8985d76b30f --- /dev/null +++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/function_metadata/function_metadata.6.ddl.sqlpp @@ -0,0 +1,24 @@ +/* + * 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. + */ + +// Define a user-defined function so the next query can assert UDFs are listed. + +create function fn_meta_udf(a) { + a + 1 +}; diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/function_metadata/function_metadata.7.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/function_metadata/function_metadata.7.query.sqlpp new file mode 100644 index 00000000000..06c1171f499 --- /dev/null +++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/function_metadata/function_metadata.7.query.sqlpp @@ -0,0 +1,24 @@ +/* + * 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. + */ + +// User-defined functions are listed with kind "udf" and their arity. + +select f.kind, f.arity +from function_metadata() f +where f.name = "fn_meta_udf"; diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/function_metadata/function_metadata.8.ddl.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/function_metadata/function_metadata.8.ddl.sqlpp new file mode 100644 index 00000000000..12e436a6da1 --- /dev/null +++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/function_metadata/function_metadata.8.ddl.sqlpp @@ -0,0 +1,20 @@ +/* + * 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. + */ + +drop function fn_meta_udf@1; diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/misc/function_metadata/function_metadata.1.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/misc/function_metadata/function_metadata.1.adm index 8f5580025d2..3dd1308278d 100644 --- a/asterixdb/asterix-app/src/test/resources/runtimets/results/misc/function_metadata/function_metadata.1.adm +++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/misc/function_metadata/function_metadata.1.adm @@ -1,2 +1,2 @@ -{ "name": "rank", "arity": 0, "category": "window", "private": false } -{ "name": "row_number", "arity": 0, "category": "window", "private": false } +"rank" +"row_number" diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/misc/function_metadata/function_metadata.2.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/misc/function_metadata/function_metadata.2.adm new file mode 100644 index 00000000000..bfb1ffaaff3 --- /dev/null +++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/misc/function_metadata/function_metadata.2.adm @@ -0,0 +1 @@ +"datasource" diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/misc/function_metadata/function_metadata.3.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/misc/function_metadata/function_metadata.3.adm new file mode 100644 index 00000000000..3a2e3f4984a --- /dev/null +++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/misc/function_metadata/function_metadata.3.adm @@ -0,0 +1 @@ +-1 diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/misc/function_metadata/function_metadata.4.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/misc/function_metadata/function_metadata.4.adm new file mode 100644 index 00000000000..1d474d52557 --- /dev/null +++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/misc/function_metadata/function_metadata.4.adm @@ -0,0 +1,2 @@ +false +true diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/misc/function_metadata/function_metadata.5.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/misc/function_metadata/function_metadata.5.adm new file mode 100644 index 00000000000..ef8b09b2ca1 --- /dev/null +++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/misc/function_metadata/function_metadata.5.adm @@ -0,0 +1 @@ +[ "length" ] diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/misc/function_metadata/function_metadata.7.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/misc/function_metadata/function_metadata.7.adm new file mode 100644 index 00000000000..e19c13f694e --- /dev/null +++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/misc/function_metadata/function_metadata.7.adm @@ -0,0 +1 @@ +{ "kind": "udf", "arity": 1 } diff --git a/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/util/CommonFunctionMapUtil.java b/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/util/CommonFunctionMapUtil.java index f13f777d241..aa7f872afb7 100644 --- a/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/util/CommonFunctionMapUtil.java +++ b/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/util/CommonFunctionMapUtil.java @@ -19,6 +19,7 @@ package org.apache.asterix.lang.common.util; +import java.util.Collections; import java.util.HashMap; import java.util.Map; @@ -249,6 +250,14 @@ public static String getFunctionMapping(String alias) { return FUNCTION_NAME_MAP.get(alias); } + /** + * Returns a read-only view of the alias-to-internal-name mappings. The keys are the + * user-facing alias spellings; the values are the internal (hyphenated) function names. + */ + public static Map getFunctionMappings() { + return Collections.unmodifiableMap(FUNCTION_NAME_MAP); + } + public static void addFunctionMapping(String alias, String functionName) { FUNCTION_NAME_MAP.put(alias, functionName); }