This is an automated email from the ASF dual-hosted git repository.
peeyush pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/asterixdb.git
The following commit(s) were added to refs/heads/master by this push:
new b277226fac [ASTERIXDB-3603][FUN] Adding validation for transform
functions
b277226fac is described below
commit b277226fac010a1ae08eb4e299a627ec02b7fac6
Author: Peeyush Gupta <[email protected]>
AuthorDate: Thu May 1 14:46:21 2025 -0700
[ASTERIXDB-3603][FUN] Adding validation for transform functions
- user model changes: no
- storage format changes: no
- interface changes: no
Ext-ref: MB-63039
Change-Id: I524cbfb6685bd3c7ed269c2db48482e5aba1ce6c
Reviewed-on: https://asterix-gerrit.ics.uci.edu/c/asterixdb/+/19730
Tested-by: Jenkins <[email protected]>
Reviewed-by: Ali Alsuliman <[email protected]>
---
.../FunctionCardinalityInferenceVisitor.java | 61 ++++++++++++++++++++++
.../asterix/app/translator/QueryTranslator.java | 47 ++++++++++++++++-
.../transform/negative/transform.000.ddl.sqlpp | 31 +++++++++++
.../transform/negative/transform.001.ddl.sqlpp | 25 +++++++++
.../transform/negative/transform.002.ddl.sqlpp | 25 +++++++++
.../transform/negative/transform.003.ddl.sqlpp | 25 +++++++++
.../transform/negative/transform.004.ddl.sqlpp | 25 +++++++++
.../transform/negative/transform.005.ddl.sqlpp | 25 +++++++++
.../transform/positive/transform.000.ddl.sqlpp | 31 +++++++++++
.../transform/positive/transform.001.ddl.sqlpp | 25 +++++++++
.../transform/positive/transform.002.ddl.sqlpp | 25 +++++++++
.../transform/positive/transform.003.ddl.sqlpp | 25 +++++++++
.../transform/positive/transform.004.ddl.sqlpp | 28 ++++++++++
.../transform/positive/transform.005.ddl.sqlpp | 25 +++++++++
.../src/test/resources/runtimets/sqlpp_queries.xml | 15 ++++++
.../asterix/common/exceptions/ErrorCode.java | 1 +
.../src/main/resources/asx_errormsg/en.properties | 1 +
.../visitors/CardinalityInferenceVisitor.java | 14 ++---
18 files changed, 445 insertions(+), 9 deletions(-)
diff --git
a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/visitor/FunctionCardinalityInferenceVisitor.java
b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/visitor/FunctionCardinalityInferenceVisitor.java
new file mode 100644
index 0000000000..03ef29ea34
--- /dev/null
+++
b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/visitor/FunctionCardinalityInferenceVisitor.java
@@ -0,0 +1,61 @@
+/*
+ * 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.optimizer.rules.visitor;
+
+import org.apache.asterix.om.base.AInt64;
+import org.apache.asterix.om.constants.AsterixConstantValue;
+import org.apache.hyracks.algebricks.common.exceptions.AlgebricksException;
+import org.apache.hyracks.algebricks.core.algebra.base.ILogicalExpression;
+import org.apache.hyracks.algebricks.core.algebra.base.ILogicalOperator;
+import org.apache.hyracks.algebricks.core.algebra.base.LogicalExpressionTag;
+import
org.apache.hyracks.algebricks.core.algebra.expressions.ConstantExpression;
+import
org.apache.hyracks.algebricks.core.algebra.expressions.ScalarFunctionCallExpression;
+import
org.apache.hyracks.algebricks.core.algebra.operators.logical.LimitOperator;
+import
org.apache.hyracks.algebricks.core.algebra.operators.logical.visitors.CardinalityInferenceVisitor;
+
+public class FunctionCardinalityInferenceVisitor extends
CardinalityInferenceVisitor {
+
+ private FunctionCardinalityInferenceVisitor() {
+ super();
+ }
+
+ public static boolean isCardinalityZeroOrOne(ILogicalOperator operator)
throws AlgebricksException {
+ FunctionCardinalityInferenceVisitor visitor = new
FunctionCardinalityInferenceVisitor();
+ long cardinality = operator.accept(visitor, null);
+ return cardinality == ZERO_OR_ONE || cardinality == ONE;
+ }
+
+ @Override
+ public Long visitLimitOperator(LimitOperator op, Void arg) throws
AlgebricksException {
+ // While translation a limit expression is encapsulated as a
switch-case expression.
+ // switch-case(treat-as-integer(user_value_expr) > 0, true,
treat-as-integer(user_value_expr), 0)
+ // Here we extract the user_value_expr from the switch-case expression
+ ScalarFunctionCallExpression inputExpr =
(ScalarFunctionCallExpression) op.getMaxObjects().getValue();
+ ILogicalExpression expr = ((ScalarFunctionCallExpression)
inputExpr.getArguments().get(2).getValue())
+ .getArguments().get(0).getValue();
+ if (expr.getExpressionTag() == LogicalExpressionTag.CONSTANT) {
+ AsterixConstantValue constantValue = (AsterixConstantValue)
((ConstantExpression) expr).getValue();
+ if (constantValue.getObject() instanceof AInt64
+ && ((AInt64) constantValue.getObject()).getLongValue() <=
1) {
+ return ZERO_OR_ONE;
+ }
+ }
+ return
adjustCardinalityForTupleReductionOperator(op.getInputs().get(0).getValue().accept(this,
arg));
+ }
+}
diff --git
a/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/translator/QueryTranslator.java
b/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/translator/QueryTranslator.java
index dcfd810dc2..b50a911e31 100644
---
a/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/translator/QueryTranslator.java
+++
b/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/translator/QueryTranslator.java
@@ -48,6 +48,8 @@ import org.apache.asterix.active.ActivityState;
import org.apache.asterix.active.EntityId;
import org.apache.asterix.active.IActiveEntityEventsListener;
import org.apache.asterix.active.NoRetryPolicyFactory;
+import org.apache.asterix.algebra.base.ILangExpressionToPlanTranslator;
+import org.apache.asterix.algebra.base.ILangExpressionToPlanTranslatorFactory;
import org.apache.asterix.algebra.extension.ExtensionStatement;
import org.apache.asterix.api.common.APIFramework;
import org.apache.asterix.api.http.server.AbstractQueryApiServlet;
@@ -223,6 +225,7 @@ import org.apache.asterix.om.types.BuiltinTypeMap;
import org.apache.asterix.om.types.IAType;
import org.apache.asterix.om.types.TypeSignature;
import org.apache.asterix.om.utils.RecordUtil;
+import
org.apache.asterix.optimizer.rules.visitor.FunctionCardinalityInferenceVisitor;
import org.apache.asterix.runtime.fulltext.AbstractFullTextFilterDescriptor;
import org.apache.asterix.runtime.fulltext.FullTextConfigDescriptor;
import org.apache.asterix.runtime.fulltext.StopwordsFullTextFilterDescriptor;
@@ -258,6 +261,9 @@ import
org.apache.hyracks.algebricks.common.exceptions.AlgebricksException;
import org.apache.hyracks.algebricks.common.utils.Pair;
import org.apache.hyracks.algebricks.common.utils.Triple;
import org.apache.hyracks.algebricks.core.algebra.base.Counter;
+import org.apache.hyracks.algebricks.core.algebra.base.ILogicalOperator;
+import org.apache.hyracks.algebricks.core.algebra.base.ILogicalPlan;
+import org.apache.hyracks.algebricks.core.algebra.base.LogicalOperatorTag;
import
org.apache.hyracks.algebricks.core.algebra.expressions.AbstractFunctionCallExpression.FunctionKind;
import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
import org.apache.hyracks.algebricks.core.algebra.util.OperatorPropertiesUtil;
@@ -3344,6 +3350,10 @@ public class QueryTranslator extends
AbstractLangTranslator implements IStatemen
} else {
List<Pair<VarIdentifier, TypeExpression>> paramList =
cfs.getParameters();
int paramCount = paramList.size();
+ if (cfs.isTransform() && paramCount != 1) {
+ throw new
CompilationException(ErrorCode.INVALID_TRANSFORM_FUNCTION, sourceLoc,
+ "Transform function should have exactly one
parameter");
+ }
List<VarIdentifier> paramVars = new ArrayList<>(paramCount);
List<String> paramNames = new ArrayList<>(paramCount);
for (Pair<VarIdentifier, TypeExpression> paramPair :
paramList) {
@@ -3370,11 +3380,19 @@ public class QueryTranslator extends
AbstractLangTranslator implements IStatemen
metadataProvider.setDefaultNamespace(ns);
LangRewritingContext langRewritingContext =
createLangRewritingContext(metadataProvider, fdList, null,
null, warningCollector, wrappedQuery.getVarCounter());
- apiFramework.reWriteQuery(langRewritingContext, wrappedQuery,
sessionOutput, false, false,
- Collections.emptyList());
+ List<VarIdentifier> externalVars = new ArrayList<>();
+ Pair<IReturningStatement, Integer> rewrittenQuery =
apiFramework.reWriteQuery(langRewritingContext,
+ wrappedQuery, sessionOutput, false, true,
externalVars);
List<List<DependencyFullyQualifiedName>> dependencies =
FunctionUtil.getFunctionDependencies(metadataProvider,
fd, queryRewriter);
+ if (cfs.isTransform()) {
+ if (!dependencies.get(0).isEmpty()) {
+ throw new
CompilationException(ErrorCode.INVALID_TRANSFORM_FUNCTION, sourceLoc,
+ "Transform function definition can not use
collections/views");
+ }
+ validateTransformFunction(metadataProvider,
rewrittenQuery, sourceLoc);
+ }
appCtx.getReceptionist().ensureAuthorized(requestParameters,
metadataProvider);
newInlineTypes = Collections.emptyMap();
@@ -3419,6 +3437,31 @@ public class QueryTranslator extends
AbstractLangTranslator implements IStatemen
}
}
+ private void validateTransformFunction(MetadataProvider metadataProvider,
+ Pair<IReturningStatement, Integer> rewrittenResult, SourceLocation
sourceLoc) throws AlgebricksException {
+ ILangExpressionToPlanTranslatorFactory translatorFactory =
+ compilationProvider.getExpressionToPlanTranslatorFactory();
+ ILangExpressionToPlanTranslator t =
+
translatorFactory.createExpressionToPlanTranslator(metadataProvider,
rewrittenResult.second, null);
+ org.apache.asterix.translator.ResultMetadata resultMetadata =
+ new
org.apache.asterix.translator.ResultMetadata(sessionOutput.config().fmt());
+ ILogicalPlan plan = t.translate((Query) rewrittenResult.first, null,
null, resultMetadata);
+ if (plan.getRoots().size() != 1) {
+ throw new
CompilationException(ErrorCode.INVALID_TRANSFORM_FUNCTION, sourceLoc,
+ "Transform function cannot have more than one root");
+ }
+ ILogicalOperator op =
plan.getRoots().get(0).getValue().getInputs().get(0).getValue();
+ if (op.getOperatorTag() == LogicalOperatorTag.AGGREGATE) {
+ if
(!FunctionCardinalityInferenceVisitor.isCardinalityZeroOrOne(op.getInputs().get(0).getValue()))
{
+ throw new
CompilationException(ErrorCode.INVALID_TRANSFORM_FUNCTION, sourceLoc,
+ "Transform function cannot return more than one row");
+ }
+ } else {
+ throw new
CompilationException(ErrorCode.INVALID_TRANSFORM_FUNCTION, sourceLoc,
+ "Transform function should always contain a query");
+ }
+ }
+
private Triple<TypeSignature, TypeSignature, Datatype>
translateFunctionParameterType(
FunctionSignature functionSignature, int paramIdx, TypeExpression
paramTypeExpr, SourceLocation sourceLoc,
MetadataProvider metadataProvider, MetadataTransactionContext
mdTxnCtx) throws AlgebricksException {
diff --git
a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/function/transform/negative/transform.000.ddl.sqlpp
b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/function/transform/negative/transform.000.ddl.sqlpp
new file mode 100644
index 0000000000..39b0abdd52
--- /dev/null
+++
b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/function/transform/negative/transform.000.ddl.sqlpp
@@ -0,0 +1,31 @@
+/*
+ * 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 dataverse test if exists;
+create dataverse test;
+use test;
+
+CREATE TYPE T1 AS {
+ a: int32,
+ b: int32
+};
+
+CREATE COLLECTION test_collection(T1) PRIMARY KEY a;
+
+CREATE VIEW test_view AS
+ SELECT * FROM test_collection;
diff --git
a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/function/transform/negative/transform.001.ddl.sqlpp
b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/function/transform/negative/transform.001.ddl.sqlpp
new file mode 100644
index 0000000000..07f2fb1415
--- /dev/null
+++
b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/function/transform/negative/transform.001.ddl.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.
+ */
+use test;
+
+-- Fail: More than one argument
+
+CREATE TRANSFORM FUNCTION transformTest(a, b) {
+ SELECT * FROM [{"a": 1, "b": 2}] t
+};
diff --git
a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/function/transform/negative/transform.002.ddl.sqlpp
b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/function/transform/negative/transform.002.ddl.sqlpp
new file mode 100644
index 0000000000..61696adc09
--- /dev/null
+++
b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/function/transform/negative/transform.002.ddl.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.
+ */
+use test;
+
+-- Fail: Less than one argument
+
+CREATE TRANSFORM FUNCTION transformTest() {
+ SELECT * FROM [{"a": 1, "b": 2}] t
+};
\ No newline at end of file
diff --git
a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/function/transform/negative/transform.003.ddl.sqlpp
b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/function/transform/negative/transform.003.ddl.sqlpp
new file mode 100644
index 0000000000..dc1a2779bc
--- /dev/null
+++
b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/function/transform/negative/transform.003.ddl.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.
+ */
+use test;
+
+-- Fail: Using a collection in the definition
+
+CREATE TRANSFORM FUNCTION transformTest(doc) {
+ SELECT count(*) as count FROM test_collection
+};
diff --git
a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/function/transform/negative/transform.004.ddl.sqlpp
b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/function/transform/negative/transform.004.ddl.sqlpp
new file mode 100644
index 0000000000..7ad55bd405
--- /dev/null
+++
b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/function/transform/negative/transform.004.ddl.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.
+ */
+use test;
+
+-- Fail: Using a view in the definition
+
+CREATE TRANSFORM FUNCTION transformTest(doc) {
+ SELECT count(*) FROM test_view
+};
diff --git
a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/function/transform/negative/transform.005.ddl.sqlpp
b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/function/transform/negative/transform.005.ddl.sqlpp
new file mode 100644
index 0000000000..9e4e7b9589
--- /dev/null
+++
b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/function/transform/negative/transform.005.ddl.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.
+ */
+drop dataverse test if exists;
+create dataverse test;
+use test;
+
+CREATE TRANSFORM FUNCTION transformTest(doc) {
+ SELECT * FROM [doc] d UNNEST d.a
+};
diff --git
a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/function/transform/positive/transform.000.ddl.sqlpp
b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/function/transform/positive/transform.000.ddl.sqlpp
new file mode 100644
index 0000000000..39b0abdd52
--- /dev/null
+++
b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/function/transform/positive/transform.000.ddl.sqlpp
@@ -0,0 +1,31 @@
+/*
+ * 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 dataverse test if exists;
+create dataverse test;
+use test;
+
+CREATE TYPE T1 AS {
+ a: int32,
+ b: int32
+};
+
+CREATE COLLECTION test_collection(T1) PRIMARY KEY a;
+
+CREATE VIEW test_view AS
+ SELECT * FROM test_collection;
diff --git
a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/function/transform/positive/transform.001.ddl.sqlpp
b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/function/transform/positive/transform.001.ddl.sqlpp
new file mode 100644
index 0000000000..0ab3ace2b2
--- /dev/null
+++
b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/function/transform/positive/transform.001.ddl.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.
+ */
+use test;
+
+-- Fail: More than one argument
+
+CREATE TRANSFORM FUNCTION transformTest1(doc) {
+ SELECT count(*) as count FROM [doc] t UNNEST t.a
+};
diff --git
a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/function/transform/positive/transform.002.ddl.sqlpp
b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/function/transform/positive/transform.002.ddl.sqlpp
new file mode 100644
index 0000000000..6d5599106b
--- /dev/null
+++
b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/function/transform/positive/transform.002.ddl.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.
+ */
+use test;
+
+-- Fail: Less than one argument
+
+CREATE TRANSFORM FUNCTION transformTest2(doc) {
+ SELECT (SELECT * FROM [doc] t UNNEST t.a) as tt
+};
\ No newline at end of file
diff --git
a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/function/transform/positive/transform.003.ddl.sqlpp
b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/function/transform/positive/transform.003.ddl.sqlpp
new file mode 100644
index 0000000000..c38bd72455
--- /dev/null
+++
b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/function/transform/positive/transform.003.ddl.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.
+ */
+use test;
+
+-- Fail: Using a collection in the definition
+
+CREATE TRANSFORM FUNCTION transformTest3(doc) {
+ SELECT * FROM [doc] t UNNEST t.a LIMIT 1
+};
diff --git
a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/function/transform/positive/transform.004.ddl.sqlpp
b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/function/transform/positive/transform.004.ddl.sqlpp
new file mode 100644
index 0000000000..10cfb4ea88
--- /dev/null
+++
b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/function/transform/positive/transform.004.ddl.sqlpp
@@ -0,0 +1,28 @@
+/*
+ * 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.
+ */
+use test;
+
+-- Fail: Using a view in the definition
+
+CREATE TRANSFORM FUNCTION transformTest4(cust) {
+ SELECT VALUE c
+ FROM [cust] AS c
+ WHERE c.address.zipcode IS NOT MISSING
+ LIMIT 1
+};
diff --git
a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/function/transform/positive/transform.005.ddl.sqlpp
b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/function/transform/positive/transform.005.ddl.sqlpp
new file mode 100644
index 0000000000..77153ceafb
--- /dev/null
+++
b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/function/transform/positive/transform.005.ddl.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.
+ */
+use test;
+
+CREATE TRANSFORM FUNCTION transformTest5(ord) {
+ SELECT o.orderno, i.itemno, i.qty AS item_qty, i.price AS item_price
+ FROM [ord] AS o UNNEST o.items AS i
+ LIMIT 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 a472904b8b..a26853b9ef 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/sqlpp_queries.xml
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/sqlpp_queries.xml
@@ -14229,6 +14229,21 @@
<output-dir compare="Text">drop_if_exists</output-dir>
</compilation-unit>
</test-case>
+ <test-case FilePath="function/transform">
+ <compilation-unit name="negative">
+ <output-dir compare="Text">negative</output-dir>
+ <expected-error>ASX1223: Failed to create transform function.
Encountered error: 'Transform function should have exactly one
parameter'</expected-error>
+ <expected-error>ASX1223: Failed to create transform function.
Encountered error: 'Transform function should have exactly one
parameter'</expected-error>
+ <expected-error>ASX1223: Failed to create transform function.
Encountered error: 'Transform function definition can not use
collections/views'</expected-error>
+ <expected-error>ASX1223: Failed to create transform function.
Encountered error: 'Transform function definition can not use
collections/views'</expected-error>
+ <expected-error>ASX1223: Failed to create transform function.
Encountered error: 'Transform function cannot return more than one
row'</expected-error>
+ </compilation-unit>
+ </test-case>
+ <test-case FilePath="function/transform">
+ <compilation-unit name="positive">
+ <output-dir compare="Text">positive</output-dir>
+ </compilation-unit>
+ </test-case>
</test-group>
<test-group name="feeds">
<test-case FilePath="feeds">
diff --git
a/asterixdb/asterix-common/src/main/java/org/apache/asterix/common/exceptions/ErrorCode.java
b/asterixdb/asterix-common/src/main/java/org/apache/asterix/common/exceptions/ErrorCode.java
index 463695e040..260617a157 100644
---
a/asterixdb/asterix-common/src/main/java/org/apache/asterix/common/exceptions/ErrorCode.java
+++
b/asterixdb/asterix-common/src/main/java/org/apache/asterix/common/exceptions/ErrorCode.java
@@ -328,6 +328,7 @@ public enum ErrorCode implements IError {
CANNOT_TRUNCATE_DATASET_TYPE(1220),
NO_VALID_AUTHENTICATION_PARAMS_PROVIDED(1221),
NO_VALID_AUTHENTICATION_PARAMS_PROVIDED_TO_IMPERSONATE_SERVICE_ACCOUNT(1222),
+ INVALID_TRANSFORM_FUNCTION(1223),
// Feed errors
DATAFLOW_ILLEGAL_STATE(3001),
diff --git
a/asterixdb/asterix-common/src/main/resources/asx_errormsg/en.properties
b/asterixdb/asterix-common/src/main/resources/asx_errormsg/en.properties
index df08016e31..68c23c7868 100644
--- a/asterixdb/asterix-common/src/main/resources/asx_errormsg/en.properties
+++ b/asterixdb/asterix-common/src/main/resources/asx_errormsg/en.properties
@@ -330,6 +330,7 @@
1220 = Cannot truncate %1$s '%2$s'
1221 = No valid authentication parameters were provided
1222 = No valid authentication parameters were provided to impersonate service
account
+1223 = Failed to create transform function. Encountered error: '%1$s'
# Feed Errors
3001 = Illegal state.
diff --git
a/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/operators/logical/visitors/CardinalityInferenceVisitor.java
b/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/operators/logical/visitors/CardinalityInferenceVisitor.java
index 6b25b1f6c3..21d9ea2a3a 100644
---
a/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/operators/logical/visitors/CardinalityInferenceVisitor.java
+++
b/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/operators/logical/visitors/CardinalityInferenceVisitor.java
@@ -84,15 +84,15 @@ import
org.apache.hyracks.algebricks.core.algebra.visitors.ILogicalOperatorVisit
* 5. the cardinality is some unknown value.
*/
public class CardinalityInferenceVisitor implements
ILogicalOperatorVisitor<Long, Void> {
- private static final long ZERO_OR_ONE = 0L;
- private static final long ONE = 1L;
- private static final long ZERO_OR_ONE_GROUP = 2L;
- private static final long ONE_GROUP = 3L;
- private static final long UNKNOWN = 100L; // so it fits into the
auto-boxing cache
+ protected static final long ZERO_OR_ONE = 0L;
+ protected static final long ONE = 1L;
+ protected static final long ZERO_OR_ONE_GROUP = 2L;
+ protected static final long ONE_GROUP = 3L;
+ protected static final long UNKNOWN = 100L; // so it fits into the
auto-boxing cache
private final Set<LogicalVariable> keyVariables = new HashSet<>();
- private CardinalityInferenceVisitor() {
+ public CardinalityInferenceVisitor() {
}
@Override
@@ -379,7 +379,7 @@ public class CardinalityInferenceVisitor implements
ILogicalOperatorVisitor<Long
}
// For operators including SELECT and LIMIT.
- private long adjustCardinalityForTupleReductionOperator(long
inputCardinality) {
+ protected long adjustCardinalityForTupleReductionOperator(long
inputCardinality) {
if (inputCardinality == ONE) {
return ZERO_OR_ONE;
}