Steven Jacobs has uploaded a new change for review. https://asterix-gerrit.ics.uci.edu/2253
Change subject: [ASTERIXDB-2180][FUN] Provent dropping of entities used by functions ...................................................................... [ASTERIXDB-2180][FUN] Provent dropping of entities used by functions Add dependencies to Functional Metadata Check dependencies before dropping datasets or functions Add tests Change-Id: I2f08ff150dfd57432b88381c507814ddb57bd67b --- M asterixdb/asterix-app/src/main/java/org/apache/asterix/api/common/APIFramework.java M asterixdb/asterix-app/src/main/java/org/apache/asterix/app/external/ExternalLibraryUtils.java M asterixdb/asterix-app/src/main/java/org/apache/asterix/app/translator/QueryTranslator.java A asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/user-defined-functions/check-dependencies-1/check-dependencies-1.1.ddl.sqlpp A asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/user-defined-functions/check-dependencies-1/check-dependencies-1.2.query.sqlpp A asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/user-defined-functions/drop-dependency-1/drop-dependency.1.ddl.sqlpp A asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/user-defined-functions/drop-dependency-2/drop-dependency.2.ddl.sqlpp A asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/user-defined-functions/drop-dependency-3/drop-dependency.3.ddl.sqlpp A asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/user-defined-functions/drop-dependency-4/drop-dependency.4.ddl.sqlpp A asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/user-defined-functions/drop-dependency-5/drop-dependency.5.ddl.sqlpp A asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/user-defined-functions/drop-dependency-6/drop-dependency.6.ddl.sqlpp A asterixdb/asterix-app/src/test/resources/runtimets/results/user-defined-functions/check-dependencies-1/check-dependencies-1.1.adm M asterixdb/asterix-app/src/test/resources/runtimets/testsuite_sqlpp.xml M asterixdb/asterix-lang-aql/src/main/java/org/apache/asterix/lang/aql/rewrites/AqlQueryRewriter.java M asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/base/IQueryRewriter.java M asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/util/FunctionUtil.java M asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/visitor/AbstractInlineUdfsVisitor.java M asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/visitor/GatherFunctionCallsVisitor.java M asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/SqlppFunctionBodyRewriter.java M asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/SqlppQueryRewriter.java M asterixdb/asterix-metadata/src/main/java/org/apache/asterix/metadata/MetadataNode.java M asterixdb/asterix-metadata/src/main/java/org/apache/asterix/metadata/MetadataTransactionContext.java M asterixdb/asterix-metadata/src/main/java/org/apache/asterix/metadata/bootstrap/MetadataRecordTypes.java M asterixdb/asterix-metadata/src/main/java/org/apache/asterix/metadata/entities/Function.java M asterixdb/asterix-metadata/src/main/java/org/apache/asterix/metadata/entitytupletranslators/FunctionTupleTranslator.java 25 files changed, 690 insertions(+), 48 deletions(-) git pull ssh://asterix-gerrit.ics.uci.edu:29418/asterixdb refs/changes/53/2253/1 diff --git a/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/common/APIFramework.java b/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/common/APIFramework.java index 1257a61..4428e05 100644 --- a/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/common/APIFramework.java +++ b/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/common/APIFramework.java @@ -180,7 +180,7 @@ } public Pair<IReturningStatement, Integer> reWriteQuery(List<FunctionDecl> declaredFunctions, - MetadataProvider metadataProvider, IReturningStatement q, SessionOutput output) + MetadataProvider metadataProvider, IReturningStatement q, SessionOutput output, boolean inlineUdfs) throws CompilationException { if (q == null) { return null; @@ -193,7 +193,7 @@ printPlanPostfix(output); } IQueryRewriter rw = rewriterFactory.createQueryRewriter(); - rw.rewrite(declaredFunctions, q, metadataProvider, new LangRewritingContext(q.getVarCounter())); + rw.rewrite(declaredFunctions, q, metadataProvider, new LangRewritingContext(q.getVarCounter()), inlineUdfs); return new Pair<>(q, q.getVarCounter()); } diff --git a/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/external/ExternalLibraryUtils.java b/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/external/ExternalLibraryUtils.java index b013c60..9f05263 100755 --- a/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/external/ExternalLibraryUtils.java +++ b/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/external/ExternalLibraryUtils.java @@ -248,7 +248,7 @@ } Function f = new Function(dataverse, libraryName + "#" + function.getName().trim(), args.size(), args, function.getReturnType().trim(), function.getDefinition().trim(), - library.getLanguage().trim(), function.getFunctionType().trim()); + library.getLanguage().trim(), function.getFunctionType().trim(), new ArrayList<>()); MetadataManager.INSTANCE.addFunction(mdTxnCtx, f); if (LOGGER.isInfoEnabled()) { LOGGER.info("Installed function: " + libraryName + "#" + function.getName().trim()); 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 de0c88f..5b68dc9 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 @@ -26,6 +26,7 @@ import java.io.InputStreamReader; import java.rmi.RemoteException; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Date; @@ -83,6 +84,7 @@ import org.apache.asterix.lang.common.base.IRewriterFactory; import org.apache.asterix.lang.common.base.IStatementRewriter; import org.apache.asterix.lang.common.base.Statement; +import org.apache.asterix.lang.common.expression.CallExpr; import org.apache.asterix.lang.common.expression.FieldBinding; import org.apache.asterix.lang.common.expression.IndexedTypeExpression; import org.apache.asterix.lang.common.expression.LiteralExpr; @@ -124,6 +126,7 @@ import org.apache.asterix.lang.common.statement.WriteStatement; import org.apache.asterix.lang.common.struct.Identifier; import org.apache.asterix.lang.common.struct.VarIdentifier; +import org.apache.asterix.lang.common.util.CommonFunctionMapUtil; import org.apache.asterix.lang.common.util.MergePolicyUtils; import org.apache.asterix.lang.sqlpp.rewrites.SqlppRewriterFactory; import org.apache.asterix.metadata.IDatasetDetails; @@ -155,6 +158,7 @@ import org.apache.asterix.metadata.utils.MetadataConstants; import org.apache.asterix.metadata.utils.MetadataLockUtil; import org.apache.asterix.metadata.utils.MetadataUtil; +import org.apache.asterix.om.functions.BuiltinFunctions; import org.apache.asterix.om.types.ARecordType; import org.apache.asterix.om.types.ATypeTag; import org.apache.asterix.om.types.IAType; @@ -185,6 +189,7 @@ import org.apache.hyracks.algebricks.common.exceptions.AlgebricksException; import org.apache.hyracks.algebricks.common.utils.Pair; import org.apache.hyracks.algebricks.core.algebra.expressions.AbstractFunctionCallExpression.FunctionKind; +import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier; import org.apache.hyracks.algebricks.data.IAWriterFactory; import org.apache.hyracks.algebricks.data.IResultSerializerFactoryProvider; import org.apache.hyracks.algebricks.runtime.serializer.ResultSerializerFactoryProvider; @@ -1701,12 +1706,40 @@ varIds.add(new VarIdentifier(v)); } wrappedQuery.setExternalVars(varIds); - apiFramework.reWriteQuery(declaredFunctions, metadataProvider, wrappedQuery, sessionOutput); + apiFramework.reWriteQuery(declaredFunctions, metadataProvider, wrappedQuery, sessionOutput, false); + + Set<CallExpr> functionCalls = + rewriterFactory.createQueryRewriter().getFunctionCalls(cfs.getFunctionBodyExpression()); + + //Get the List of used functions and used datasets + List<List<List<String>>> dependencies = new ArrayList<>(); + //dataset dependencies + dependencies.add(new ArrayList<>()); + //functional dependencies + dependencies.add(new ArrayList<>()); + for (CallExpr functionCall : functionCalls) { + FunctionSignature signature = functionCall.getFunctionSignature(); + FunctionIdentifier fid = + new FunctionIdentifier(signature.getNamespace(), signature.getName(), signature.getArity()); + if (fid.equals(BuiltinFunctions.DATASET)) { + Pair<String, String> path = DatasetUtil.getDatasetInfo(metadataProvider, + ((LiteralExpr) functionCall.getExprList().get(0)).getValue().getStringValue()); + dependencies.get(0).add(new ArrayList<>(Arrays.asList(path.first, path.second))); + } + + else if (BuiltinFunctions.isBuiltinCompilerFunction( + CommonFunctionMapUtil.normalizeBuiltinFunctionSignature(signature), false)) { + continue; + } else { + dependencies.get(1).add(new ArrayList<>(Arrays.asList(signature.getNamespace(), signature.getName(), + Integer.toString(signature.getArity())))); + } + } Function function = new Function(dataverse, functionName, cfs.getFunctionSignature().getArity(), cfs.getParamList(), Function.RETURNTYPE_VOID, cfs.getFunctionBody(), rewriterFactory instanceof SqlppRewriterFactory ? Function.LANGUAGE_SQLPP : Function.LANGUAGE_AQL, - FunctionKind.SCALAR.toString()); + FunctionKind.SCALAR.toString(), dependencies); MetadataManager.INSTANCE.addFunction(mdTxnCtx, function); MetadataManager.INSTANCE.commitTransaction(mdTxnCtx); @@ -1898,7 +1931,7 @@ // Query Rewriting (happens under the same ongoing metadata transaction) Pair<IReturningStatement, Integer> rewrittenResult = apiFramework.reWriteQuery(declaredFunctions, - metadataProvider, query, sessionOutput); + metadataProvider, query, sessionOutput, true); // Query Compilation (happens under the same ongoing metadata transaction) return apiFramework.compileQuery(clusterInfoCollector, metadataProvider, (Query) rewrittenResult.first, @@ -1912,7 +1945,7 @@ // Insert/upsert statement rewriting (happens under the same ongoing metadata // transaction) Pair<IReturningStatement, Integer> rewrittenResult = apiFramework.reWriteQuery(declaredFunctions, - metadataProvider, insertUpsert, sessionOutput); + metadataProvider, insertUpsert, sessionOutput, true); InsertStatement rewrittenInsertUpsert = (InsertStatement) rewrittenResult.first; String dataverseName = getActiveDataverse(rewrittenInsertUpsert.getDataverseName()); diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/user-defined-functions/check-dependencies-1/check-dependencies-1.1.ddl.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/user-defined-functions/check-dependencies-1/check-dependencies-1.1.ddl.sqlpp new file mode 100644 index 0000000..c734c34 --- /dev/null +++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/user-defined-functions/check-dependencies-1/check-dependencies-1.1.ddl.sqlpp @@ -0,0 +1,81 @@ +/* + * 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. + */ +/* + * Description : Verify Function Dependency Metadata + * Expected Res : Success + * Date : Dec 15th 2017 + */ + +drop dataverse two if exists; +drop dataverse experiments if exists; +create dataverse experiments; +use experiments; + +create type TweetMessageType as closed { + tweetid: uuid, + sender_location: point, + send_time: datetime, + referred_topics: {{ string }}, + message_text: string, + countA: int32, + countB: int32 +}; + +create dataset TweetMessages(TweetMessageType) +primary key tweetid autogenerated; + +create function f1(message, text){ + contains(message,text) +}; + +create function f4(){ +(select * from TweetMessages) +}; + +create dataverse two; +use two; + +create dataset TweetMessages2(experiments.TweetMessageType) +primary key tweetid autogenerated; + +create function f0(message, text){ + contains(message,text) +}; + +create function experiments.f2(place, text) { + (select m.message_text + from TweetMessages m + where contains(m.message_text,text) + and spatial_intersect(m.sender_location, place) + and f1(m.message_text,text) + and two.f0(m.message_text,text)) +}; + +create function experiments.f3(place, text) { + f2(place, text) +}; + +create function f5(place, text){ + (select m.message_text + from TweetMessages2 m, experiments.TweetMessages m2 + where contains(m.message_text,text) + and spatial_intersect(m.sender_location, place) + and experiments.f1(m.message_text,text) + and f0(m2.message_text,text)) +}; \ No newline at end of file diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/user-defined-functions/check-dependencies-1/check-dependencies-1.2.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/user-defined-functions/check-dependencies-1/check-dependencies-1.2.query.sqlpp new file mode 100644 index 0000000..8082b80 --- /dev/null +++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/user-defined-functions/check-dependencies-1/check-dependencies-1.2.query.sqlpp @@ -0,0 +1,29 @@ +/* + * 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. + */ +/* + * Description : Verify Function Dependency Metadata + * Expected Res : Success + * Date : Dec 15th 2017 + */ + +use experiments; + + +select f.DataverseName,f.Name,f.Dependencies from Metadata.`Function` f +order by f.DataverseName, f.Name; diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/user-defined-functions/drop-dependency-1/drop-dependency.1.ddl.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/user-defined-functions/drop-dependency-1/drop-dependency.1.ddl.sqlpp new file mode 100644 index 0000000..10f94ac --- /dev/null +++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/user-defined-functions/drop-dependency-1/drop-dependency.1.ddl.sqlpp @@ -0,0 +1,41 @@ +/* + * 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. + */ + +/* + * Description : Try to drop a functional dependency + * Expected Res : Error + */ + +drop dataverse two if exists; +drop dataverse experiments if exists; +create dataverse experiments; +use experiments; + +create function f1(message, text){ + contains(message,text) +}; + +create dataverse two; +use two; + +create function f0(message, text){ + experiments.f1(message,text) +}; + +drop dataverse experiments; \ No newline at end of file diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/user-defined-functions/drop-dependency-2/drop-dependency.2.ddl.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/user-defined-functions/drop-dependency-2/drop-dependency.2.ddl.sqlpp new file mode 100644 index 0000000..bbd1276 --- /dev/null +++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/user-defined-functions/drop-dependency-2/drop-dependency.2.ddl.sqlpp @@ -0,0 +1,55 @@ +/* + * 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. + */ + +/* + * Description : Try to drop a functional dependency + * Expected Res : Error + */ + +drop dataverse two if exists; +drop dataverse experiments if exists; +create dataverse experiments; +use experiments; + +create type TweetMessageType as closed { + tweetid: uuid, + sender_location: point, + send_time: datetime, + referred_topics: {{ string }}, + message_text: string, + countA: int32, + countB: int32 +}; + +create dataset TweetMessages(TweetMessageType) +primary key tweetid autogenerated; + +create function f1(message, text){ + contains(message,text) +}; + +create dataverse two; +use two; + +create function f2(place, text){ + (select m.message_text + from experiments.TweetMessages m) +}; + +drop dataverse experiments; \ No newline at end of file diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/user-defined-functions/drop-dependency-3/drop-dependency.3.ddl.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/user-defined-functions/drop-dependency-3/drop-dependency.3.ddl.sqlpp new file mode 100644 index 0000000..f42fd2a --- /dev/null +++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/user-defined-functions/drop-dependency-3/drop-dependency.3.ddl.sqlpp @@ -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. + */ + +/* + * Description : Try to drop a functional dependency + * Expected Res : Error + */ + +drop dataverse two if exists; +drop dataverse experiments if exists; +create dataverse experiments; +use experiments; + +create function f1(message, text){ + contains(message,text) +}; + +create dataverse two; +use two; + +create function f0(message, text){ + experiments.f1(message,text) +}; + +use experiments; +drop function f1@2; \ No newline at end of file diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/user-defined-functions/drop-dependency-4/drop-dependency.4.ddl.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/user-defined-functions/drop-dependency-4/drop-dependency.4.ddl.sqlpp new file mode 100644 index 0000000..8ac9cff --- /dev/null +++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/user-defined-functions/drop-dependency-4/drop-dependency.4.ddl.sqlpp @@ -0,0 +1,56 @@ +/* + * 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. + */ + +/* + * Description : Try to drop a functional dependency + * Expected Res : Error + */ + +drop dataverse two if exists; +drop dataverse experiments if exists; +create dataverse experiments; +use experiments; + +create type TweetMessageType as closed { + tweetid: uuid, + sender_location: point, + send_time: datetime, + referred_topics: {{ string }}, + message_text: string, + countA: int32, + countB: int32 +}; + +create dataset TweetMessages(TweetMessageType) +primary key tweetid autogenerated; + +create function f1(message, text){ + contains(message,text) +}; + +create dataverse two; +use two; + +create function f2(place, text){ + (select m.message_text + from experiments.TweetMessages m) +}; + +use experiments; +drop dataset TweetMessages; \ No newline at end of file diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/user-defined-functions/drop-dependency-5/drop-dependency.5.ddl.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/user-defined-functions/drop-dependency-5/drop-dependency.5.ddl.sqlpp new file mode 100644 index 0000000..235514c --- /dev/null +++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/user-defined-functions/drop-dependency-5/drop-dependency.5.ddl.sqlpp @@ -0,0 +1,38 @@ +/* + * 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. + */ + +/* + * Description : Try to drop a functional dependency + * Expected Res : Error + */ + +drop dataverse two if exists; +drop dataverse experiments if exists; +create dataverse experiments; +use experiments; + +create function f1(message, text){ + contains(message,text) +}; + +create function f0(message, text){ + experiments.f1(message,text) +}; + +drop function f1@2; \ No newline at end of file diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/user-defined-functions/drop-dependency-6/drop-dependency.6.ddl.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/user-defined-functions/drop-dependency-6/drop-dependency.6.ddl.sqlpp new file mode 100644 index 0000000..d3ca1da --- /dev/null +++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/user-defined-functions/drop-dependency-6/drop-dependency.6.ddl.sqlpp @@ -0,0 +1,52 @@ +/* + * 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. + */ + +/* + * Description : Try to drop a functional dependency + * Expected Res : Error + */ + +drop dataverse two if exists; +drop dataverse experiments if exists; +create dataverse experiments; +use experiments; + +create type TweetMessageType as closed { + tweetid: uuid, + sender_location: point, + send_time: datetime, + referred_topics: {{ string }}, + message_text: string, + countA: int32, + countB: int32 +}; + +create dataset TweetMessages(TweetMessageType) +primary key tweetid autogenerated; + +create function f1(message, text){ + contains(message,text) +}; + +create function f2(place, text){ + (select m.message_text + from experiments.TweetMessages m) +}; + +drop dataset TweetMessages; \ No newline at end of file diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/user-defined-functions/check-dependencies-1/check-dependencies-1.1.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/user-defined-functions/check-dependencies-1/check-dependencies-1.1.adm new file mode 100644 index 0000000..a5dcb7e --- /dev/null +++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/user-defined-functions/check-dependencies-1/check-dependencies-1.1.adm @@ -0,0 +1,6 @@ +{ "DataverseName": "experiments", "Name": "f1", "Dependencies": [ [ ], [ ] ] } +{ "DataverseName": "experiments", "Name": "f2", "Dependencies": [ [ [ "experiments", "TweetMessages" ] ], [ [ "experiments", "f1" ], [ "two", "f0" ] ] ] } +{ "DataverseName": "experiments", "Name": "f3", "Dependencies": [ [ ], [ [ "experiments", "f2" ] ] ] } +{ "DataverseName": "experiments", "Name": "f4", "Dependencies": [ [ [ "experiments", "TweetMessages" ] ], [ ] ] } +{ "DataverseName": "two", "Name": "f0", "Dependencies": [ [ ], [ ] ] } +{ "DataverseName": "two", "Name": "f5", "Dependencies": [ [ [ "experiments", "TweetMessages" ], [ "two", "TweetMessages2" ] ], [ [ "two", "f0" ], [ "experiments", "f1" ] ] ] } \ No newline at end of file diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/testsuite_sqlpp.xml b/asterixdb/asterix-app/src/test/resources/runtimets/testsuite_sqlpp.xml index 2c3d855..84f8473 100644 --- a/asterixdb/asterix-app/src/test/resources/runtimets/testsuite_sqlpp.xml +++ b/asterixdb/asterix-app/src/test/resources/runtimets/testsuite_sqlpp.xml @@ -7927,6 +7927,47 @@ </compilation-unit> </test-case> <test-case FilePath="user-defined-functions"> + <compilation-unit name="check-dependencies-1"> + <output-dir compare="Text">check-dependencies-1</output-dir> + </compilation-unit> + </test-case> + <test-case FilePath="user-defined-functions"> + <compilation-unit name="drop-dependency-1"> + <output-dir compare="Text">drop-dependency-1</output-dir> + <expected-error>Cannot drop dataverse. Function two.f0@2 depends on function experiments.f1@2</expected-error> + </compilation-unit> + </test-case> + <test-case FilePath="user-defined-functions"> + <compilation-unit name="drop-dependency-2"> + <output-dir compare="Text">drop-dependency-2</output-dir> + <expected-error>Cannot drop dataverse. Function two.f2@2 depends on dataset experiments.TweetMessages</expected-error> + </compilation-unit> + </test-case> + <test-case FilePath="user-defined-functions"> + <compilation-unit name="drop-dependency-3"> + <output-dir compare="Text">drop-dependency-3</output-dir> + <expected-error>Cannot drop function experiments.f1@2 being used by function two.f0@2</expected-error> + </compilation-unit> + </test-case> + <test-case FilePath="user-defined-functions"> + <compilation-unit name="drop-dependency-4"> + <output-dir compare="Text">drop-dependency-4</output-dir> + <expected-error>Cannot drop dataset experiments.TweetMessages being used by function two.f2@2</expected-error> + </compilation-unit> + </test-case> + <test-case FilePath="user-defined-functions"> + <compilation-unit name="drop-dependency-5"> + <output-dir compare="Text">drop-dependency-5</output-dir> + <expected-error>Cannot drop function experiments.f1@2 being used by function experiments.f0@2</expected-error> + </compilation-unit> + </test-case> + <test-case FilePath="user-defined-functions"> + <compilation-unit name="drop-dependency-6"> + <output-dir compare="Text">drop-dependency-6</output-dir> + <expected-error>Cannot drop dataset experiments.TweetMessages being used by function experiments.f2@2</expected-error> + </compilation-unit> + </test-case> + <test-case FilePath="user-defined-functions"> <compilation-unit name="single-line-definition"> <output-dir compare="Text">single-line-definition</output-dir> </compilation-unit> diff --git a/asterixdb/asterix-lang-aql/src/main/java/org/apache/asterix/lang/aql/rewrites/AqlQueryRewriter.java b/asterixdb/asterix-lang-aql/src/main/java/org/apache/asterix/lang/aql/rewrites/AqlQueryRewriter.java index 977f6bb..6ab69ad 100644 --- a/asterixdb/asterix-lang-aql/src/main/java/org/apache/asterix/lang/aql/rewrites/AqlQueryRewriter.java +++ b/asterixdb/asterix-lang-aql/src/main/java/org/apache/asterix/lang/aql/rewrites/AqlQueryRewriter.java @@ -40,6 +40,7 @@ import org.apache.asterix.lang.common.base.IReturningStatement; import org.apache.asterix.lang.common.clause.GroupbyClause; import org.apache.asterix.lang.common.clause.LetClause; +import org.apache.asterix.lang.common.expression.CallExpr; import org.apache.asterix.lang.common.expression.GbyVariableExpressionPair; import org.apache.asterix.lang.common.expression.VariableExpr; import org.apache.asterix.lang.common.rewrites.LangRewritingContext; @@ -68,7 +69,8 @@ @Override public void rewrite(List<FunctionDecl> declaredFunctions, IReturningStatement topStatement, - MetadataProvider metadataProvider, LangRewritingContext context) throws CompilationException { + MetadataProvider metadataProvider, LangRewritingContext context, boolean inlineUdfs) + throws CompilationException { setup(declaredFunctions, topStatement, metadataProvider, context); if (topStatement.isTopLevel()) { wrapInLets(); @@ -130,7 +132,8 @@ declaredFunctions.removeAll(storedFunctionDecls); } - private Set<FunctionSignature> getFunctionCalls(Expression expression) throws CompilationException { + @Override + public Set<CallExpr> getFunctionCalls(Expression expression) throws CompilationException { GatherFunctionCalls gfc = new GatherFunctionCalls(); expression.accept(gfc, null); return gfc.getCalls(); diff --git a/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/base/IQueryRewriter.java b/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/base/IQueryRewriter.java index 0c6c04c..f072917 100644 --- a/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/base/IQueryRewriter.java +++ b/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/base/IQueryRewriter.java @@ -19,8 +19,10 @@ package org.apache.asterix.lang.common.base; import java.util.List; +import java.util.Set; import org.apache.asterix.common.exceptions.CompilationException; +import org.apache.asterix.lang.common.expression.CallExpr; import org.apache.asterix.lang.common.rewrites.LangRewritingContext; import org.apache.asterix.lang.common.statement.FunctionDecl; import org.apache.asterix.metadata.declared.MetadataProvider; @@ -39,6 +41,13 @@ * @param context, * manages ids of variables and guarantees uniqueness of variables. */ - public void rewrite(List<FunctionDecl> declaredFunctions, IReturningStatement topExpr, - MetadataProvider metadataProvider, LangRewritingContext context) throws CompilationException; + void rewrite(List<FunctionDecl> declaredFunctions, IReturningStatement topExpr, + MetadataProvider metadataProvider, LangRewritingContext context, boolean inlineUdfs) + throws CompilationException; + + /** + * Find the function calls used by a given expression + */ + Set<CallExpr> getFunctionCalls(Expression expression) throws CompilationException; + } diff --git a/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/util/FunctionUtil.java b/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/util/FunctionUtil.java index 1ca9316..cad84ee 100644 --- a/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/util/FunctionUtil.java +++ b/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/util/FunctionUtil.java @@ -27,6 +27,7 @@ import org.apache.asterix.common.functions.FunctionConstants; import org.apache.asterix.common.functions.FunctionSignature; import org.apache.asterix.lang.common.base.Expression; +import org.apache.asterix.lang.common.expression.CallExpr; import org.apache.asterix.lang.common.statement.FunctionDecl; import org.apache.asterix.metadata.MetadataManager; import org.apache.asterix.metadata.MetadataTransactionContext; @@ -52,7 +53,7 @@ @FunctionalInterface public interface IFunctionCollector { - Set<FunctionSignature> getFunctionCalls(Expression expression) throws CompilationException; + Set<CallExpr> getFunctionCalls(Expression expression) throws CompilationException; } @FunctionalInterface @@ -95,8 +96,9 @@ } String value = metadataProvider.getConfig().get(FunctionUtil.IMPORT_PRIVATE_FUNCTIONS); boolean includePrivateFunctions = (value != null) ? Boolean.valueOf(value.toLowerCase()) : false; - Set<FunctionSignature> functionCalls = functionCollector.getFunctionCalls(expression); - for (FunctionSignature signature : functionCalls) { + Set<CallExpr> functionCalls = functionCollector.getFunctionCalls(expression); + for (CallExpr functionCall : functionCalls) { + FunctionSignature signature = functionCall.getFunctionSignature(); if (declaredFunctions != null && declaredFunctions.contains(signature)) { continue; } diff --git a/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/visitor/AbstractInlineUdfsVisitor.java b/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/visitor/AbstractInlineUdfsVisitor.java index 7d8c11e..8ae67d6 100644 --- a/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/visitor/AbstractInlineUdfsVisitor.java +++ b/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/visitor/AbstractInlineUdfsVisitor.java @@ -347,7 +347,7 @@ metadataProvider.setDefaultDataverse(fnDataverse); try { IQueryRewriter queryRewriter = rewriterFactory.createQueryRewriter(); - queryRewriter.rewrite(declaredFunctions, wrappedQuery, metadataProvider, context); + queryRewriter.rewrite(declaredFunctions, wrappedQuery, metadataProvider, context, true); return wrappedQuery.getBody(); } finally { metadataProvider.setDefaultDataverse(defaultDataverse); diff --git a/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/visitor/GatherFunctionCallsVisitor.java b/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/visitor/GatherFunctionCallsVisitor.java index 8842d86..3d149fb 100644 --- a/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/visitor/GatherFunctionCallsVisitor.java +++ b/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/visitor/GatherFunctionCallsVisitor.java @@ -23,7 +23,6 @@ import java.util.Set; import org.apache.asterix.common.exceptions.CompilationException; -import org.apache.asterix.common.functions.FunctionSignature; import org.apache.asterix.lang.common.base.Expression; import org.apache.asterix.lang.common.clause.GroupbyClause; import org.apache.asterix.lang.common.clause.LetClause; @@ -53,11 +52,11 @@ public class GatherFunctionCallsVisitor extends AbstractQueryExpressionVisitor<Void, Void> { - protected final Set<FunctionSignature> calls = new HashSet<FunctionSignature>(); + protected final Set<CallExpr> calls = new HashSet<>(); @Override public Void visit(CallExpr pf, Void arg) throws CompilationException { - calls.add(pf.getFunctionSignature()); + calls.add(pf); for (Expression e : pf.getExprList()) { e.accept(this, arg); } @@ -202,7 +201,7 @@ return null; } - public Set<FunctionSignature> getCalls() { + public Set<CallExpr> getCalls() { return calls; } diff --git a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/SqlppFunctionBodyRewriter.java b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/SqlppFunctionBodyRewriter.java index a8b3dc6..cd57396 100644 --- a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/SqlppFunctionBodyRewriter.java +++ b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/SqlppFunctionBodyRewriter.java @@ -31,7 +31,7 @@ @Override public void rewrite(List<FunctionDecl> declaredFunctions, IReturningStatement topStatement, MetadataProvider metadataProvider, - LangRewritingContext context) throws CompilationException { + LangRewritingContext context, boolean inlineUdfs) throws CompilationException { // Sets up parameters. setup(declaredFunctions, topStatement, metadataProvider, context); @@ -63,6 +63,6 @@ rewriteListInputFunctions(); // Inlines functions recursively. - inlineDeclaredUdfs(); + inlineDeclaredUdfs(inlineUdfs); } } diff --git a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/SqlppQueryRewriter.java b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/SqlppQueryRewriter.java index 3e9a873..befa5ab 100644 --- a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/SqlppQueryRewriter.java +++ b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/SqlppQueryRewriter.java @@ -28,6 +28,7 @@ import org.apache.asterix.lang.common.base.IQueryRewriter; import org.apache.asterix.lang.common.base.IReturningStatement; import org.apache.asterix.lang.common.clause.LetClause; +import org.apache.asterix.lang.common.expression.CallExpr; import org.apache.asterix.lang.common.rewrites.LangRewritingContext; import org.apache.asterix.lang.common.statement.FunctionDecl; import org.apache.asterix.lang.common.util.FunctionUtil; @@ -86,7 +87,8 @@ @Override public void rewrite(List<FunctionDecl> declaredFunctions, IReturningStatement topStatement, - MetadataProvider metadataProvider, LangRewritingContext context) throws CompilationException { + MetadataProvider metadataProvider, LangRewritingContext context, boolean inlineUdfs) + throws CompilationException { if (topStatement == null) { return; } @@ -126,7 +128,7 @@ rewriteListInputFunctions(); // Inlines functions. - inlineDeclaredUdfs(); + inlineDeclaredUdfs(inlineUdfs); // Rewrites function names. // This should be done after inlineDeclaredUdfs() because user-defined function @@ -213,7 +215,7 @@ topExpr.accept(groupByVisitor, null); } - protected void inlineDeclaredUdfs() throws CompilationException { + protected void inlineDeclaredUdfs(boolean inlineUdfs) throws CompilationException { List<FunctionSignature> funIds = new ArrayList<FunctionSignature>(); for (FunctionDecl fdecl : declaredFunctions) { funIds.add(fdecl.getSignature()); @@ -226,7 +228,7 @@ signature -> FunctionMapUtil.normalizeBuiltinFunctionSignature(signature, false))); } declaredFunctions.addAll(usedStoredFunctionDecls); - if (!declaredFunctions.isEmpty()) { + if (inlineUdfs && !declaredFunctions.isEmpty()) { SqlppInlineUdfsVisitor visitor = new SqlppInlineUdfsVisitor(context, new SqlppFunctionBodyRewriterFactory() /* the rewriter for function bodies expressions*/, declaredFunctions, metadataProvider); @@ -237,7 +239,8 @@ declaredFunctions.removeAll(usedStoredFunctionDecls); } - private Set<FunctionSignature> getFunctionCalls(Expression expression) throws CompilationException { + @Override + public Set<CallExpr> getFunctionCalls(Expression expression) throws CompilationException { GatherFunctionCalls gfc = new GatherFunctionCalls(); expression.accept(gfc, null); return gfc.getCalls(); diff --git a/asterixdb/asterix-metadata/src/main/java/org/apache/asterix/metadata/MetadataNode.java b/asterixdb/asterix-metadata/src/main/java/org/apache/asterix/metadata/MetadataNode.java index 368fc2a..cee5edb 100644 --- a/asterixdb/asterix-metadata/src/main/java/org/apache/asterix/metadata/MetadataNode.java +++ b/asterixdb/asterix-metadata/src/main/java/org/apache/asterix/metadata/MetadataNode.java @@ -27,7 +27,6 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.logging.Logger; import org.apache.asterix.common.api.IDatasetLifecycleManager; import org.apache.asterix.common.api.INcApplicationContext; @@ -271,7 +270,6 @@ } } - @SuppressWarnings("unchecked") @Override public <T extends IExtensionMetadataEntity> void addEntity(TxnId txnId, T entity) throws AlgebricksException, RemoteException { @@ -283,7 +281,6 @@ addEntity(txnId, entity, tupleTranslator, index); } - @SuppressWarnings("unchecked") @Override public <T extends IExtensionMetadataEntity> void upsertEntity(TxnId txnId, T entity) throws AlgebricksException, RemoteException { @@ -295,7 +292,6 @@ upsertEntity(txnId, entity, tupleTranslator, index); } - @SuppressWarnings("unchecked") @Override public <T extends IExtensionMetadataEntity> void deleteEntity(TxnId txnId, T entity) throws AlgebricksException, RemoteException { @@ -307,7 +303,6 @@ deleteEntity(txnId, entity, tupleTranslator, index); } - @SuppressWarnings("unchecked") @Override public <T extends IExtensionMetadataEntity> List<T> getEntities(TxnId txnId, IExtensionMetadataSearchKey searchKey) throws AlgebricksException, RemoteException { @@ -530,13 +525,22 @@ confirmDataverseCanBeDeleted(txnId, dataverseName); + // As a side effect, acquires an S lock on the 'Function' dataset + // on behalf of txnId. + List<Function> dataverseFunctions = getDataverseFunctions(txnId, dataverseName); + // Drop all functions in this dataverse. + for (Function function : dataverseFunctions) { + dropFunction(txnId, new FunctionSignature(dataverseName, function.getName(), function.getArity()), + true); + } + List<Dataset> dataverseDatasets; Dataset ds; dataverseDatasets = getDataverseDatasets(txnId, dataverseName); // Drop all datasets in this dataverse. for (int i = 0; i < dataverseDatasets.size(); i++) { ds = dataverseDatasets.get(i); - dropDataset(txnId, dataverseName, ds.getDatasetName()); + dropDataset(txnId, dataverseName, ds.getDatasetName(), true); } // After dropping datasets, drop datatypes @@ -547,14 +551,6 @@ // Drop all types in this dataverse. for (int i = 0; i < dataverseDatatypes.size(); i++) { forceDropDatatype(txnId, dataverseName, dataverseDatatypes.get(i).getDatatypeName()); - } - - // As a side effect, acquires an S lock on the 'Function' dataset - // on behalf of txnId. - List<Function> dataverseFunctions = getDataverseFunctions(txnId, dataverseName); - // Drop all functions in this dataverse. - for (Function function : dataverseFunctions) { - dropFunction(txnId, new FunctionSignature(dataverseName, function.getName(), function.getArity())); } // As a side effect, acquires an S lock on the 'Adapter' dataset @@ -612,6 +608,16 @@ @Override public void dropDataset(TxnId txnId, String dataverseName, String datasetName) throws AlgebricksException, RemoteException { + dropDataset(txnId, dataverseName, datasetName, false); + } + + public void dropDataset(TxnId txnId, String dataverseName, String datasetName, boolean force) + throws AlgebricksException, RemoteException { + + if (!force) { + confirmDatasetCanBeDeleted(txnId, dataverseName, datasetName); + } + Dataset dataset = getDataset(txnId, dataverseName, datasetName); if (dataset == null) { throw new AlgebricksException("Cannot drop dataset '" + datasetName + "' because it doesn't exist."); @@ -905,6 +911,19 @@ } } + public List<Function> getAllFunctions(TxnId txnId) throws AlgebricksException, RemoteException { + try { + ITupleReference searchKey = null; + FunctionTupleTranslator tupleReaderWriter = tupleTranslatorProvider.getFunctionTupleTranslator(false); + IValueExtractor<Function> valueExtractor = new MetadataEntityValueExtractor<>(tupleReaderWriter); + List<Function> results = new ArrayList<>(); + searchIndex(txnId, MetadataPrimaryIndexes.FUNCTION_DATASET, searchKey, valueExtractor, results); + return results; + } catch (HyracksDataException e) { + throw new AlgebricksException(e); + } + } + public List<Datatype> getAllDatatypes(TxnId txnId) throws AlgebricksException, RemoteException { try { ITupleReference searchKey = null; @@ -933,6 +952,62 @@ throw new AlgebricksException( "Cannot drop dataverse. Type " + dataverseName + "." + set.getItemTypeName() + " used by dataset " + set.getDataverseName() + "." + set.getDatasetName()); + } + } + + // If a function from a DIFFERENT dataverse + // uses functions or datatypes from this dataverse + // throw an error + List<Function> functions = getAllFunctions(txnId); + for (Function function : functions) { + if (function.getDataverseName().equals(dataverseName)) { + continue; + } + for (List<String> datasetDependency : function.getDependencies().get(0)) { + if (datasetDependency.get(0).equals(dataverseName)) { + throw new AlgebricksException("Cannot drop dataverse. Function " + function.getDataverseName() + "." + + function.getName() + "@" + function.getArity() + " depends on dataset " + + datasetDependency.get(0) + "." + datasetDependency.get(1)); + } + } + for (List<String> functionDependency : function.getDependencies().get(1)) { + if (functionDependency.get(0).equals(dataverseName)) { + throw new AlgebricksException( + "Cannot drop dataverse. Function " + function.getDataverseName() + "." + function.getName() + + "@" + function.getArity() + " depends on function " + functionDependency.get(0) + + "." + functionDependency.get(1) + "@" + functionDependency.get(2)); + } + } + } + } + + private void confirmFunctionCanBeDeleted(TxnId txnId, FunctionSignature signature) + throws AlgebricksException, RemoteException { + // If any other function uses this function, throw an error + List<Function> functions = getAllFunctions(txnId); + for (Function function : functions) { + for (List<String> functionalDependency : function.getDependencies().get(1)) { + if (functionalDependency.get(0).equals(signature.getNamespace()) + && functionalDependency.get(1).equals(signature.getName()) + && functionalDependency.get(2).equals(Integer.toString(signature.getArity()))) { + throw new AlgebricksException("Cannot drop function " + signature + " being used by function " + + function.getDataverseName() + "." + function.getName() + "@" + function.getArity()); + } + } + } + } + + private void confirmDatasetCanBeDeleted(TxnId txnId, String dataverseName, String datasetName) + throws AlgebricksException, RemoteException { + // If any function uses this type, throw an error + List<Function> functions = getAllFunctions(txnId); + for (Function function : functions) { + for (List<String> datasetDependency : function.getDependencies().get(0)) { + if (datasetDependency.get(0).equals(dataverseName) && datasetDependency.get(1).equals(datasetName)) { + throw new AlgebricksException("Cannot drop dataset " + dataverseName + "." + datasetName + + " being used by function " + function.getDataverseName() + "." + function.getName() + "@" + + function.getArity()); + } } } } @@ -1126,6 +1201,15 @@ @Override public void dropFunction(TxnId txnId, FunctionSignature functionSignature) throws AlgebricksException, RemoteException { + dropFunction(txnId, functionSignature, false); + } + + private void dropFunction(TxnId txnId, FunctionSignature functionSignature, boolean force) + throws AlgebricksException, RemoteException { + + if (!force) { + confirmFunctionCanBeDeleted(txnId, functionSignature); + } Function function = getFunction(txnId, functionSignature); @@ -1332,7 +1416,6 @@ // TODO: Can use Hyrack's TupleUtils for this, once we switch to a newer // Hyracks version. public static ITupleReference createTuple(String... fields) { - @SuppressWarnings("unchecked") ISerializerDeserializer<AString> stringSerde = SerializerDeserializerProvider.INSTANCE.getSerializerDeserializer(BuiltinType.ASTRING); AMutableString aString = new AMutableString(""); @@ -1839,7 +1922,6 @@ // This method is used to create a search tuple for external data file since the // search tuple has an int value - @SuppressWarnings("unchecked") public ITupleReference createExternalFileSearchTuple(String dataverseName, String datasetName, int fileNumber) throws HyracksDataException { ISerializerDeserializer<AString> stringSerde = diff --git a/asterixdb/asterix-metadata/src/main/java/org/apache/asterix/metadata/MetadataTransactionContext.java b/asterixdb/asterix-metadata/src/main/java/org/apache/asterix/metadata/MetadataTransactionContext.java index 406b3d6..99bec66 100644 --- a/asterixdb/asterix-metadata/src/main/java/org/apache/asterix/metadata/MetadataTransactionContext.java +++ b/asterixdb/asterix-metadata/src/main/java/org/apache/asterix/metadata/MetadataTransactionContext.java @@ -158,7 +158,7 @@ public void dropFunction(FunctionSignature signature) { Function function = new Function(signature.getNamespace(), signature.getName(), signature.getArity(), null, - null, null, null, null); + null, null, null, null, null); droppedCache.addFunctionIfNotExists(function); logAndApply(new MetadataLogicalOperation(function, false)); } diff --git a/asterixdb/asterix-metadata/src/main/java/org/apache/asterix/metadata/bootstrap/MetadataRecordTypes.java b/asterixdb/asterix-metadata/src/main/java/org/apache/asterix/metadata/bootstrap/MetadataRecordTypes.java index 2a04b58..df8b7f1 100644 --- a/asterixdb/asterix-metadata/src/main/java/org/apache/asterix/metadata/bootstrap/MetadataRecordTypes.java +++ b/asterixdb/asterix-metadata/src/main/java/org/apache/asterix/metadata/bootstrap/MetadataRecordTypes.java @@ -48,6 +48,7 @@ public static final String FIELD_NAME_DATAVERSE_NAME = "DataverseName"; public static final String FIELD_NAME_DATA_FORMAT = "DataFormat"; public static final String FIELD_NAME_DEFINITION = "Definition"; + public static final String FIELD_NAME_DEPENDENCIES = "Dependencies"; public static final String FIELD_NAME_DERIVED = "Derived"; public static final String FIELD_NAME_DESCRIPTION = "Description"; public static final String FIELD_NAME_EXTERNAL_DETAILS = "ExternalDetails"; @@ -321,16 +322,20 @@ public static final int FUNCTION_ARECORD_FUNCTION_DEFINITION_FIELD_INDEX = 5; public static final int FUNCTION_ARECORD_FUNCTION_LANGUAGE_FIELD_INDEX = 6; public static final int FUNCTION_ARECORD_FUNCTION_KIND_FIELD_INDEX = 7; + public static final int FUNCTION_ARECORD_FUNCTION_DEPENDENCIES_FIELD_INDEX = 8; public static final ARecordType FUNCTION_RECORDTYPE = createRecordType( // RecordTypeName RECORD_NAME_FUNCTION, // FieldNames new String[] { FIELD_NAME_DATAVERSE_NAME, FIELD_NAME_NAME, FIELD_NAME_ARITY, FIELD_NAME_PARAMS, - FIELD_NAME_RETURN_TYPE, FIELD_NAME_DEFINITION, FIELD_NAME_LANGUAGE, FIELD_NAME_KIND }, + FIELD_NAME_RETURN_TYPE, FIELD_NAME_DEFINITION, FIELD_NAME_LANGUAGE, FIELD_NAME_KIND, + FIELD_NAME_DEPENDENCIES }, // FieldTypes new IAType[] { BuiltinType.ASTRING, BuiltinType.ASTRING, BuiltinType.ASTRING, new AOrderedListType(BuiltinType.ASTRING, null), BuiltinType.ASTRING, BuiltinType.ASTRING, - BuiltinType.ASTRING, BuiltinType.ASTRING }, + BuiltinType.ASTRING, BuiltinType.ASTRING, + new AOrderedListType(new AOrderedListType(new AOrderedListType(BuiltinType.ASTRING, null), null), + null) }, //IsOpen? true); //------------------------------------------ Adapter ----------------------------------------// diff --git a/asterixdb/asterix-metadata/src/main/java/org/apache/asterix/metadata/entities/Function.java b/asterixdb/asterix-metadata/src/main/java/org/apache/asterix/metadata/entities/Function.java index 1d1db37..b01f087 100644 --- a/asterixdb/asterix-metadata/src/main/java/org/apache/asterix/metadata/entities/Function.java +++ b/asterixdb/asterix-metadata/src/main/java/org/apache/asterix/metadata/entities/Function.java @@ -36,13 +36,14 @@ private final String name; private final int arity; private final List<String> params; + private final List<List<List<String>>> dependencies; private final String body; private final String returnType; private final String language; private final String kind; public Function(String dataverseName, String functionName, int arity, List<String> params, String returnType, - String functionBody, String language, String functionKind) { + String functionBody, String language, String functionKind, List<List<List<String>>> dependencies) { this.dataverse = dataverseName; this.name = functionName; this.params = params; @@ -51,6 +52,7 @@ this.language = language; this.kind = functionKind; this.arity = arity; + this.dependencies = dependencies; } public String getDataverseName() { @@ -65,6 +67,10 @@ return params; } + public List<List<List<String>>> getDependencies() { + return dependencies; + } + public String getFunctionBody() { return body; } diff --git a/asterixdb/asterix-metadata/src/main/java/org/apache/asterix/metadata/entitytupletranslators/FunctionTupleTranslator.java b/asterixdb/asterix-metadata/src/main/java/org/apache/asterix/metadata/entitytupletranslators/FunctionTupleTranslator.java index e15805e..86f221d 100644 --- a/asterixdb/asterix-metadata/src/main/java/org/apache/asterix/metadata/entitytupletranslators/FunctionTupleTranslator.java +++ b/asterixdb/asterix-metadata/src/main/java/org/apache/asterix/metadata/entitytupletranslators/FunctionTupleTranslator.java @@ -35,6 +35,7 @@ import org.apache.asterix.om.base.AString; import org.apache.asterix.om.base.IACursor; import org.apache.asterix.om.types.AOrderedListType; +import org.apache.asterix.om.types.BuiltinType; import org.apache.hyracks.algebricks.common.exceptions.AlgebricksException; import org.apache.hyracks.api.dataflow.value.ISerializerDeserializer; import org.apache.hyracks.api.exceptions.HyracksDataException; @@ -56,7 +57,13 @@ // Payload field containing serialized Function. public static final int FUNCTION_PAYLOAD_TUPLE_FIELD_INDEX = 3; - @SuppressWarnings("unchecked") + private transient OrderedListBuilder dependenciesListBuilder = new OrderedListBuilder(); + private transient OrderedListBuilder dependencyListBuilder = new OrderedListBuilder(); + private transient OrderedListBuilder dependencyNameListBuilder = new OrderedListBuilder(); + private transient AOrderedListType stringList = new AOrderedListType(BuiltinType.ASTRING, null); + private transient AOrderedListType ListofLists = + new AOrderedListType(new AOrderedListType(BuiltinType.ASTRING, null), null); + private ISerializerDeserializer<ARecord> recordSerDes = SerializerDeserializerProvider.INSTANCE.getSerializerDeserializer(MetadataRecordTypes.FUNCTION_RECORDTYPE); @@ -104,8 +111,33 @@ String functionKind = ((AString) functionRecord.getValueByPos(MetadataRecordTypes.FUNCTION_ARECORD_FUNCTION_KIND_FIELD_INDEX)) .getStringValue(); + + IACursor dependenciesCursor = ((AOrderedList) functionRecord + .getValueByPos(MetadataRecordTypes.FUNCTION_ARECORD_FUNCTION_DEPENDENCIES_FIELD_INDEX)).getCursor(); + List<List<List<String>>> dependencies = new ArrayList<>(); + AOrderedList dependencyList; + AOrderedList quilifiedList; + int i = 0; + while (dependenciesCursor.next()) { + dependencies.add(new ArrayList<>()); + dependencyList = (AOrderedList) dependenciesCursor.get(); + IACursor qualifiedDependencyCursor = (dependencyList.getCursor()); + int j = 0; + while (qualifiedDependencyCursor.next()) { + quilifiedList = (AOrderedList) qualifiedDependencyCursor.get(); + IACursor qualifiedNameCursor = (quilifiedList.getCursor()); + dependencies.get(i).add(new ArrayList<>()); + while (qualifiedNameCursor.next()) { + dependencies.get(i).get(j).add(((AString) qualifiedNameCursor.get()).getStringValue()); + } + j++; + } + i++; + + } + return new Function(dataverseName, functionName, Integer.parseInt(arity), params, returnType, definition, - language, functionKind); + language, functionKind, dependencies); } @@ -185,6 +217,33 @@ stringSerde.serialize(aString, fieldValue.getDataOutput()); recordBuilder.addField(MetadataRecordTypes.FUNCTION_ARECORD_FUNCTION_KIND_FIELD_INDEX, fieldValue); + // write field 8 + dependenciesListBuilder.reset((AOrderedListType) MetadataRecordTypes.FUNCTION_RECORDTYPE + .getFieldTypes()[MetadataRecordTypes.FUNCTION_ARECORD_FUNCTION_DEPENDENCIES_FIELD_INDEX]); + List<List<List<String>>> dependenciesList = function.getDependencies(); + for (List<List<String>> dependencies : dependenciesList) { + dependencyListBuilder.reset(ListofLists); + for (List<String> dependency : dependencies) { + dependencyNameListBuilder.reset(stringList); + for (String subName : dependency) { + itemValue.reset(); + aString.setValue(subName); + stringSerde.serialize(aString, itemValue.getDataOutput()); + dependencyNameListBuilder.addItem(itemValue); + } + itemValue.reset(); + dependencyNameListBuilder.write(itemValue.getDataOutput(), true); + dependencyListBuilder.addItem(itemValue); + + } + itemValue.reset(); + dependencyListBuilder.write(itemValue.getDataOutput(), true); + dependenciesListBuilder.addItem(itemValue); + } + fieldValue.reset(); + dependenciesListBuilder.write(fieldValue.getDataOutput(), true); + recordBuilder.addField(MetadataRecordTypes.FUNCTION_ARECORD_FUNCTION_DEPENDENCIES_FIELD_INDEX, fieldValue); + // write record recordBuilder.write(tupleBuilder.getDataOutput(), true); tupleBuilder.addFieldEndOffset(); -- To view, visit https://asterix-gerrit.ics.uci.edu/2253 To unsubscribe, visit https://asterix-gerrit.ics.uci.edu/settings Gerrit-MessageType: newchange Gerrit-Change-Id: I2f08ff150dfd57432b88381c507814ddb57bd67b Gerrit-PatchSet: 1 Gerrit-Project: asterixdb Gerrit-Branch: master Gerrit-Owner: Steven Jacobs <sjaco...@ucr.edu>