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..0d0d890d140 --- /dev/null +++ b/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/function/FunctionMetadataDatasource.java @@ -0,0 +1,82 @@ +/* + * 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.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; + +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) 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()), 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 + 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..78059272f7a --- /dev/null +++ b/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/function/FunctionMetadataFunction.java @@ -0,0 +1,156 @@ +/* + * 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.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; +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 = 2L; + + 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(toBuiltinRecord(fi, aliasIndex)); + } + records.addAll(udfRecords); + return new FunctionMetadataReader(records.toArray(new String[0])); + } + + private static String toBuiltinRecord(FunctionIdentifier fi, Map> aliasIndex) { + BuiltinFunctionInfo info = BuiltinFunctions.getBuiltinFunctionInfo(fi); + boolean isPrivate = info != null && info.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(); + } + + 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"; + } + // 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++) { + 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..ae6027f7225 --- /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/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 new file mode 100644 index 00000000000..19218204be0 --- /dev/null +++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/function_metadata/function_metadata.1.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. + */ + +// 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 new file mode 100644 index 00000000000..3dd1308278d --- /dev/null +++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/misc/function_metadata/function_metadata.1.adm @@ -0,0 +1,2 @@ +"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-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-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); } 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;