[NO-ISSUE][GRAPHIX] Large Graphix update. Large commit for the following: - Using AbstractClauseExtension. - LEFT-MATCH now defaults to a non-foldable-action. - Refactor of some docstrings to use HTML lists. - Starting work towards adding using SWITCH and CYCLE at Graphix. - Adding support for implicit correlated vertex JOINs. - Adding support for graphs with duplicate schema edge labels. - Adding support for unconditional schema decoration. - Adding support for negated edge labels. - Total revamp for schema resolution: we now take an exhaustive approach. - Adding support for specifying Graphix compiler options in the config file. Change-Id: I120362128a5557f7de8904b86bacde3b606760db Reviewed-on: https://asterix-gerrit.ics.uci.edu/c/asterixdb-graph/+/17235 Tested-by: Jenkins <jenkins@fulliautomatix.ics.uci.edu> Reviewed-by: Glenn Galvizo <ggalvizo@uci.edu>
diff --git a/asterix-graphix/pom.xml b/asterix-graphix/pom.xml index e1d517c..d20bcfe 100644 --- a/asterix-graphix/pom.xml +++ b/asterix-graphix/pom.xml
@@ -190,6 +190,11 @@ <dependencies> <dependency> + <groupId>com.github.dpaukov</groupId> + <artifactId>combinatoricslib3</artifactId> + <version>3.3.0</version> + </dependency> + <dependency> <groupId>org.apache.asterix</groupId> <artifactId>asterix-om</artifactId> <version>${asterix.version}</version>
diff --git a/asterix-graphix/src/main/java/org/apache/asterix/graphix/algebra/compiler/option/ElementEvaluationOption.java b/asterix-graphix/src/main/java/org/apache/asterix/graphix/algebra/compiler/option/ElementEvaluationOption.java new file mode 100644 index 0000000..a44fc74 --- /dev/null +++ b/asterix-graphix/src/main/java/org/apache/asterix/graphix/algebra/compiler/option/ElementEvaluationOption.java
@@ -0,0 +1,39 @@ +/* + * 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.graphix.algebra.compiler.option; + +import java.util.Locale; + +public enum ElementEvaluationOption implements IGraphixCompilerOption { + EXPAND_AND_UNION, + SWITCH_AND_CYCLE; + + public static final String OPTION_KEY_NAME = "graphix.evaluation.default"; + public static final ElementEvaluationOption OPTION_DEFAULT = EXPAND_AND_UNION; + + @Override + public String getOptionValue() { + return name().toLowerCase(Locale.ROOT).replace("_", "-"); + } + + @Override + public String getDisplayName() { + return String.format("%s ( %s )", OPTION_KEY_NAME, getOptionValue()); + } +}
diff --git a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/lower/transform/ISequenceTransformer.java b/asterix-graphix/src/main/java/org/apache/asterix/graphix/algebra/compiler/option/IGraphixCompilerOption.java similarity index 73% copy from asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/lower/transform/ISequenceTransformer.java copy to asterix-graphix/src/main/java/org/apache/asterix/graphix/algebra/compiler/option/IGraphixCompilerOption.java index 4509792..5cb0027 100644 --- a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/lower/transform/ISequenceTransformer.java +++ b/asterix-graphix/src/main/java/org/apache/asterix/graphix/algebra/compiler/option/IGraphixCompilerOption.java
@@ -16,11 +16,10 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.asterix.graphix.lang.rewrites.lower.transform; +package org.apache.asterix.graphix.algebra.compiler.option; -import org.apache.asterix.common.exceptions.CompilationException; +public interface IGraphixCompilerOption { + String getOptionValue(); -@FunctionalInterface -public interface ISequenceTransformer { - void accept(CorrelatedClauseSequence clauseSequence) throws CompilationException; + String getDisplayName(); }
diff --git a/asterix-graphix/src/main/java/org/apache/asterix/graphix/algebra/compiler/option/SchemaDecorateEdgeOption.java b/asterix-graphix/src/main/java/org/apache/asterix/graphix/algebra/compiler/option/SchemaDecorateEdgeOption.java new file mode 100644 index 0000000..e67c6cb --- /dev/null +++ b/asterix-graphix/src/main/java/org/apache/asterix/graphix/algebra/compiler/option/SchemaDecorateEdgeOption.java
@@ -0,0 +1,40 @@ +/* + * 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.graphix.algebra.compiler.option; + +import java.util.Locale; + +public enum SchemaDecorateEdgeOption implements IGraphixCompilerOption { + NEVER, + AS_NEEDED, + ALWAYS; + + public static final String OPTION_KEY_NAME = "graphix.schema-decorate.edge"; + public static final SchemaDecorateEdgeOption OPTION_DEFAULT = AS_NEEDED; + + @Override + public String getOptionValue() { + return name().toLowerCase(Locale.ROOT).replace("_", "-"); + } + + @Override + public String getDisplayName() { + return String.format("%s ( %s )", OPTION_KEY_NAME, getOptionValue()); + } +}
diff --git a/asterix-graphix/src/main/java/org/apache/asterix/graphix/algebra/compiler/option/SchemaDecorateVertexOption.java b/asterix-graphix/src/main/java/org/apache/asterix/graphix/algebra/compiler/option/SchemaDecorateVertexOption.java new file mode 100644 index 0000000..7a0cb73 --- /dev/null +++ b/asterix-graphix/src/main/java/org/apache/asterix/graphix/algebra/compiler/option/SchemaDecorateVertexOption.java
@@ -0,0 +1,40 @@ +/* + * 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.graphix.algebra.compiler.option; + +import java.util.Locale; + +public enum SchemaDecorateVertexOption implements IGraphixCompilerOption { + NEVER, + AS_NEEDED, + ALWAYS; + + public static final String OPTION_KEY_NAME = "graphix.schema-decorate.vertex"; + public static final SchemaDecorateVertexOption OPTION_DEFAULT = AS_NEEDED; + + @Override + public String getOptionValue() { + return name().toLowerCase(Locale.ROOT).replace("_", "-"); + } + + @Override + public String getDisplayName() { + return String.format("%s ( %s )", OPTION_KEY_NAME, getOptionValue()); + } +}
diff --git a/asterix-graphix/src/main/java/org/apache/asterix/graphix/algebra/compiler/option/SemanticsNavigationOption.java b/asterix-graphix/src/main/java/org/apache/asterix/graphix/algebra/compiler/option/SemanticsNavigationOption.java new file mode 100644 index 0000000..7715387 --- /dev/null +++ b/asterix-graphix/src/main/java/org/apache/asterix/graphix/algebra/compiler/option/SemanticsNavigationOption.java
@@ -0,0 +1,40 @@ +/* + * 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.graphix.algebra.compiler.option; + +import java.util.Locale; + +public enum SemanticsNavigationOption implements IGraphixCompilerOption { + NO_REPEAT_VERTICES, + NO_REPEAT_EDGES, + NO_REPEAT_ANYTHING; + + public static final String OPTION_KEY_NAME = "graphix.semantics.navigation"; + public static final SemanticsNavigationOption OPTION_DEFAULT = NO_REPEAT_ANYTHING; + + @Override + public String getOptionValue() { + return name().toLowerCase(Locale.ROOT).replace("_", "-"); + } + + @Override + public String getDisplayName() { + return String.format("%s ( %s )", OPTION_KEY_NAME, getOptionValue()); + } +}
diff --git a/asterix-graphix/src/main/java/org/apache/asterix/graphix/algebra/compiler/option/SemanticsPatternOption.java b/asterix-graphix/src/main/java/org/apache/asterix/graphix/algebra/compiler/option/SemanticsPatternOption.java new file mode 100644 index 0000000..559686f --- /dev/null +++ b/asterix-graphix/src/main/java/org/apache/asterix/graphix/algebra/compiler/option/SemanticsPatternOption.java
@@ -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. + */ +package org.apache.asterix.graphix.algebra.compiler.option; + +import java.util.Locale; + +public enum SemanticsPatternOption implements IGraphixCompilerOption { + ISOMORPHISM, + VERTEX_ISOMORPHISM, + EDGE_ISOMORPHISM, + HOMOMORPHISM; + + public static final String OPTION_KEY_NAME = "graphix.semantics.pattern"; + public static final SemanticsPatternOption OPTION_DEFAULT = ISOMORPHISM; + + @Override + public String getOptionValue() { + return name().toLowerCase(Locale.ROOT).replace("_", "-"); + } + + @Override + public String getDisplayName() { + return String.format("%s ( %s )", OPTION_KEY_NAME, getOptionValue()); + } +}
diff --git a/asterix-graphix/src/main/java/org/apache/asterix/graphix/algebra/compiler/provider/GraphixCompilationProvider.java b/asterix-graphix/src/main/java/org/apache/asterix/graphix/algebra/compiler/provider/GraphixCompilationProvider.java index 79a2b68..06f28c5 100644 --- a/asterix-graphix/src/main/java/org/apache/asterix/graphix/algebra/compiler/provider/GraphixCompilationProvider.java +++ b/asterix-graphix/src/main/java/org/apache/asterix/graphix/algebra/compiler/provider/GraphixCompilationProvider.java
@@ -23,21 +23,20 @@ import org.apache.asterix.algebra.base.ILangExpressionToPlanTranslatorFactory; import org.apache.asterix.compiler.provider.IRuleSetFactory; import org.apache.asterix.compiler.provider.SqlppCompilationProvider; +import org.apache.asterix.graphix.algebra.compiler.option.ElementEvaluationOption; +import org.apache.asterix.graphix.algebra.compiler.option.SchemaDecorateEdgeOption; +import org.apache.asterix.graphix.algebra.compiler.option.SchemaDecorateVertexOption; +import org.apache.asterix.graphix.algebra.compiler.option.SemanticsNavigationOption; +import org.apache.asterix.graphix.algebra.compiler.option.SemanticsPatternOption; import org.apache.asterix.graphix.algebra.translator.GraphixExpressionToPlanTranslatorFactory; import org.apache.asterix.graphix.lang.parser.GraphixParserFactory; -import org.apache.asterix.graphix.lang.rewrites.GraphixRewriterFactory; -import org.apache.asterix.graphix.lang.rewrites.print.GraphixASTPrintVisitorFactory; +import org.apache.asterix.graphix.lang.rewrite.GraphixRewriterFactory; +import org.apache.asterix.graphix.lang.rewrite.print.GraphixASTPrintVisitorFactory; import org.apache.asterix.lang.common.base.IAstPrintVisitorFactory; import org.apache.asterix.lang.common.base.IParserFactory; import org.apache.asterix.lang.common.base.IRewriterFactory; public class GraphixCompilationProvider extends SqlppCompilationProvider { - public static final String PRINT_REWRITE_METADATA_CONFIG = "graphix.print-rewrite"; - public static final String RESOLVER_METADATA_CONFIG = "graphix.resolver"; - public static final String RESOLVER_ITERATION_MAX_METADATA_CONFIG = "graphix.max-resolution-iterations"; - public static final String MATCH_EVALUATION_METADATA_CONFIG = "graphix.match-evaluation"; - public static final String EDGE_STRATEGY_METADATA_CONFIG = "graphix.edge-strategy"; - @Override public IParserFactory getParserFactory() { return new GraphixParserFactory(); @@ -66,8 +65,11 @@ @Override public Set<String> getCompilerOptions() { Set<String> parentConfigurableParameters = super.getCompilerOptions(); - parentConfigurableParameters.addAll(Set.of(PRINT_REWRITE_METADATA_CONFIG, MATCH_EVALUATION_METADATA_CONFIG, - RESOLVER_METADATA_CONFIG, RESOLVER_ITERATION_MAX_METADATA_CONFIG, EDGE_STRATEGY_METADATA_CONFIG)); + parentConfigurableParameters.add(ElementEvaluationOption.OPTION_KEY_NAME); + parentConfigurableParameters.add(SchemaDecorateEdgeOption.OPTION_KEY_NAME); + parentConfigurableParameters.add(SchemaDecorateVertexOption.OPTION_KEY_NAME); + parentConfigurableParameters.add(SemanticsNavigationOption.OPTION_KEY_NAME); + parentConfigurableParameters.add(SemanticsPatternOption.OPTION_KEY_NAME); return parentConfigurableParameters; } }
diff --git a/asterix-graphix/src/main/java/org/apache/asterix/graphix/algebra/translator/GraphixExpressionToPlanTranslator.java b/asterix-graphix/src/main/java/org/apache/asterix/graphix/algebra/translator/GraphixExpressionToPlanTranslator.java index 12a7f39..c720a17 100644 --- a/asterix-graphix/src/main/java/org/apache/asterix/graphix/algebra/translator/GraphixExpressionToPlanTranslator.java +++ b/asterix-graphix/src/main/java/org/apache/asterix/graphix/algebra/translator/GraphixExpressionToPlanTranslator.java
@@ -16,17 +16,96 @@ */ package org.apache.asterix.graphix.algebra.translator; +import static org.apache.asterix.graphix.runtime.function.GraphixFunctionInfoCollection.*; + +import java.util.Iterator; import java.util.Map; +import org.apache.asterix.common.exceptions.CompilationException; +import org.apache.asterix.common.exceptions.ErrorCode; +import org.apache.asterix.graphix.lang.clause.extension.IGraphixVisitorExtension; +import org.apache.asterix.graphix.lang.clause.extension.LowerListClauseExtension; +import org.apache.asterix.graphix.lang.rewrite.lower.struct.ClauseCollection; +import org.apache.asterix.lang.common.base.AbstractClause; +import org.apache.asterix.lang.common.base.Expression; +import org.apache.asterix.lang.common.base.IVisitorExtension; import org.apache.asterix.lang.common.struct.VarIdentifier; +import org.apache.asterix.lang.sqlpp.clause.AbstractBinaryCorrelateClause; import org.apache.asterix.metadata.declared.MetadataProvider; import org.apache.asterix.om.base.IAObject; import org.apache.asterix.translator.SqlppExpressionToPlanTranslator; +import org.apache.commons.lang3.mutable.Mutable; +import org.apache.commons.lang3.mutable.MutableObject; import org.apache.hyracks.algebricks.common.exceptions.AlgebricksException; +import org.apache.hyracks.algebricks.common.utils.Pair; +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.LogicalVariable; +import org.apache.hyracks.algebricks.core.algebra.operators.logical.UnnestOperator; public class GraphixExpressionToPlanTranslator extends SqlppExpressionToPlanTranslator { public GraphixExpressionToPlanTranslator(MetadataProvider metadataProvider, int currentVarCounter, Map<VarIdentifier, IAObject> externalVars) throws AlgebricksException { super(metadataProvider, currentVarCounter, externalVars); } + + @Override + public Pair<ILogicalOperator, LogicalVariable> visit(IVisitorExtension ve, Mutable<ILogicalOperator> tupSource) + throws CompilationException { + if (ve instanceof IGraphixVisitorExtension) { + IGraphixVisitorExtension gve = (IGraphixVisitorExtension) ve; + if (gve.getKind() != IGraphixVisitorExtension.Kind.LOWER_LIST) { + throw new CompilationException(ErrorCode.COMPILATION_ILLEGAL_STATE, + "Encountered illegal type of Graphix visitor extension!"); + } + return translateLowerListClause((LowerListClauseExtension) gve, tupSource); + + } else { + return super.visit(ve, tupSource); + } + } + + public Pair<ILogicalOperator, LogicalVariable> translateLowerListClause(LowerListClauseExtension llce, + Mutable<ILogicalOperator> tupSource) throws CompilationException { + ClauseCollection clauseCollection = llce.getLowerListClause().getClauseCollection(); + Iterator<AbstractClause> clauseIterator = clauseCollection.iterator(); + if (!clauseIterator.hasNext()) { + throw new CompilationException(ErrorCode.COMPILATION_ILLEGAL_STATE, + "Encountered empty lower-clause collection!"); + } + + // Translate our leading clause in our collection. + AbstractBinaryCorrelateClause leadingLowerClause = (AbstractBinaryCorrelateClause) clauseIterator.next(); + LogicalVariable leftVar = context.newVarFromExpression(leadingLowerClause.getRightVariable()); + Mutable<ILogicalOperator> topOpRef = new MutableObject<>(); + // if (tupSource.getValue() instanceof GraphTupleSourceOperator) { + // // Do not ignore our condition. + // topOpRef = new MutableObject<>(leadingLowerClause.accept(this, tupSource).first); + // + // } else { + // Our first clause is functionally equivalent to the left expression of a FROM-TERM. Ignore our condition. + Expression leftLangExpr = leadingLowerClause.getRightExpression(); + Pair<ILogicalExpression, Mutable<ILogicalOperator>> eo = langExprToAlgExpression(leftLangExpr, tupSource); + Pair<ILogicalExpression, Mutable<ILogicalOperator>> unnestExpr = makeUnnestExpression(eo.first, eo.second); + UnnestOperator unnestOp = new UnnestOperator(leftVar, new MutableObject<>(unnestExpr.first)); + unnestOp.getInputs().add(unnestExpr.second); + unnestOp.setSourceLocation(clauseCollection.getSourceLocation()); + topOpRef.setValue(unnestOp); + // } + + // The remainder of our clauses are either JOINs, UNNESTs, LETs, WHEREs, or GRAPH-CLAUSEs. + while (clauseIterator.hasNext()) { + AbstractClause workingLowerClause = clauseIterator.next(); + // if (workingLowerClause instanceof LowerSwitchClause) { + // LowerSwitchClause lowerBFSClause = (LowerSwitchClause) workingLowerClause; + // LowerSwitchClauseExtension visitorExtension = + // (LowerSwitchClauseExtension) lowerBFSClause.getVisitorExtension(); + // topOpRef = new MutableObject<>(translateLowerGraphClause(visitorExtension, topOpRef).first); + // + // } else { + topOpRef = new MutableObject<>(workingLowerClause.accept(this, topOpRef).first); + // } + } + return new Pair<>(topOpRef.getValue(), leftVar); + } }
diff --git a/asterix-graphix/src/main/java/org/apache/asterix/graphix/app/translator/GraphixQueryTranslator.java b/asterix-graphix/src/main/java/org/apache/asterix/graphix/app/translator/GraphixQueryTranslator.java index c342b10..e24d930 100644 --- a/asterix-graphix/src/main/java/org/apache/asterix/graphix/app/translator/GraphixQueryTranslator.java +++ b/asterix-graphix/src/main/java/org/apache/asterix/graphix/app/translator/GraphixQueryTranslator.java
@@ -20,9 +20,11 @@ import java.util.HashSet; import java.util.List; +import java.util.Map; import java.util.Optional; import java.util.Set; import java.util.concurrent.ExecutorService; +import java.util.stream.Collectors; import org.apache.asterix.app.translator.QueryTranslator; import org.apache.asterix.common.api.IResponsePrinter; @@ -33,8 +35,8 @@ import org.apache.asterix.common.metadata.DataverseName; import org.apache.asterix.compiler.provider.ILangCompilationProvider; import org.apache.asterix.graphix.extension.GraphixMetadataExtension; -import org.apache.asterix.graphix.lang.rewrites.GraphixQueryRewriter; -import org.apache.asterix.graphix.lang.rewrites.GraphixRewritingContext; +import org.apache.asterix.graphix.lang.rewrite.GraphixQueryRewriter; +import org.apache.asterix.graphix.lang.rewrite.GraphixRewritingContext; import org.apache.asterix.graphix.lang.statement.DeclareGraphStatement; import org.apache.asterix.graphix.lang.statement.GraphDropStatement; import org.apache.asterix.graphix.lang.statement.GraphElementDeclaration; @@ -61,16 +63,21 @@ import org.apache.asterix.metadata.declared.MetadataProvider; import org.apache.asterix.translator.IRequestParameters; import org.apache.asterix.translator.SessionOutput; +import org.apache.hyracks.algebricks.common.utils.Pair; import org.apache.hyracks.api.client.IHyracksClientConnection; import org.apache.hyracks.api.exceptions.IWarningCollector; public class GraphixQueryTranslator extends QueryTranslator { - Set<DeclareGraphStatement> declareGraphStatements = new HashSet<>(); + private final Set<DeclareGraphStatement> declareGraphStatements = new HashSet<>(); + private final Map<String, String> configFileOptions; public GraphixQueryTranslator(ICcApplicationContext appCtx, List<Statement> statements, SessionOutput output, ILangCompilationProvider compilationProvider, ExecutorService executorService, - IResponsePrinter responsePrinter) { + IResponsePrinter responsePrinter, List<Pair<String, String>> configFileOptions) { super(appCtx, statements, output, compilationProvider, executorService, responsePrinter); + + // We are given the following information from our cluster-controller config file. + this.configFileOptions = configFileOptions.stream().collect(Collectors.toMap(Pair::getFirst, Pair::getSecond)); } public GraphixQueryRewriter getQueryRewriter() { @@ -93,15 +100,17 @@ List<FunctionDecl> declaredFunctions, List<ViewDecl> declaredViews, IWarningCollector warningCollector, int varCounter) { return new GraphixRewritingContext(metadataProvider, declaredFunctions, declaredViews, declareGraphStatements, - warningCollector, varCounter); + warningCollector, varCounter, configFileOptions); } /** * To create a view, we must perform the following: - * a) Check the view body for any named graphs. - * b) Rewrite graph expressions into pure SQL++ expressions. The dependencies associated with the rewritten - * expressions will be recorded in the "Dataset" dataset. - * c) Record any graph-related dependencies for the view in our metadata. + * <ol> + * <li>Check the view body for any named graphs.</li> + * <li>Rewrite graph expressions into pure SQL++ expressions. The dependencies associated with the rewritten + * expressions will be recorded in the "Dataset" dataset.</li> + * <li>Record any graph-related dependencies for the view in our metadata.</li> + * </ol> */ @Override protected CreateResult doCreateView(MetadataProvider metadataProvider, CreateViewStatement cvs, @@ -153,10 +162,12 @@ /** * To create a function, we must perform the following: - * a) Check the function body for any named graphs. - * b) Rewrite graph expressions into pure SQL++ expressions. The dependencies associated with the rewritten - * expressions will be recorded in the "Function" dataset. - * c) Record any graph-related dependencies for the function in our metadata. + * <ol> + * <li>Check the function body for any named graphs.</li> + * <li>Rewrite graph expressions into pure SQL++ expressions. The dependencies associated with the rewritten + * expressions will be recorded in the "Function" dataset.</li> + * <li>Record any graph-related dependencies for the function in our metadata.</li> + * </ol> */ @Override protected CreateResult doCreateFunction(MetadataProvider metadataProvider, CreateFunctionStatement cfs, @@ -229,8 +240,10 @@ /** * Before dropping a function, we perform the following: - * 1. Check if any of our existing graphs depend on the function to-be-dropped. - * 2. Remove the GraphDependency record for the function to-be-dropped if it exists. + * <ol> + * <li>Check if any of our existing graphs depend on the function to-be-dropped.</li> + * <li>Remove the GraphDependency record for the function to-be-dropped if it exists.</li> + * </ol> */ @Override protected void handleFunctionDropStatement(MetadataProvider metadataProvider, Statement stmt, @@ -264,8 +277,10 @@ /** * Before dropping a view, we perform the following: - * 1. Check if any of our existing graphs depend on the view to-be-dropped. - * 2. Remove the GraphDependency record for the view to-be-dropped if it exists. + * <ol> + * <li>Check if any of our existing graphs depend on the view to-be-dropped.</li> + * <li>Remove the GraphDependency record for the view to-be-dropped if it exists.</li> + * </ol> */ @Override public void handleViewDropStatement(MetadataProvider metadataProvider, Statement stmt) throws Exception { @@ -313,9 +328,12 @@ /** * Before dropping a dataverse, we perform the following: - * 1. Check if any other entities outside the dataverse to-be-dropped depend on any entities inside the dataverse. - * 2. Remove all GraphDependency records associated with the dataverse to-be-dropped. - * 3. Remove all Graph records associated with the dataverse to-be-dropped. + * <ol> + * <li>Check if any other entities outside the dataverse to-be-dropped depend on any entities inside the + * dataverse.</li> + * <li>Remove all GraphDependency records associated with the dataverse to-be-dropped.</li> + * <li>Remove all Graph records associated with the dataverse to-be-dropped.</li> + * </ol> */ @Override protected void handleDataverseDropStatement(MetadataProvider metadataProvider, Statement stmt,
diff --git a/asterix-graphix/src/main/java/org/apache/asterix/graphix/app/translator/GraphixQueryTranslatorFactory.java b/asterix-graphix/src/main/java/org/apache/asterix/graphix/app/translator/GraphixQueryTranslatorFactory.java index a0c4d2f..7fad326 100644 --- a/asterix-graphix/src/main/java/org/apache/asterix/graphix/app/translator/GraphixQueryTranslatorFactory.java +++ b/asterix-graphix/src/main/java/org/apache/asterix/graphix/app/translator/GraphixQueryTranslatorFactory.java
@@ -28,12 +28,20 @@ import org.apache.asterix.compiler.provider.ILangCompilationProvider; import org.apache.asterix.lang.common.base.Statement; import org.apache.asterix.translator.SessionOutput; +import org.apache.hyracks.algebricks.common.utils.Pair; public class GraphixQueryTranslatorFactory extends DefaultStatementExecutorFactory { + private List<Pair<String, String>> configFileProvidedOptions; + @Override public QueryTranslator create(ICcApplicationContext appCtx, List<Statement> statements, SessionOutput output, ILangCompilationProvider compilationProvider, IStorageComponentProvider storageComponentProvider, IResponsePrinter printer) { - return new GraphixQueryTranslator(appCtx, statements, output, compilationProvider, executorService, printer); + return new GraphixQueryTranslator(appCtx, statements, output, compilationProvider, executorService, printer, + configFileProvidedOptions); + } + + public void setConfigFileProvidedOptions(List<Pair<String, String>> configFileProvidedOptions) { + this.configFileProvidedOptions = configFileProvidedOptions; } }
diff --git a/asterix-graphix/src/main/java/org/apache/asterix/graphix/common/metadata/EdgeIdentifier.java b/asterix-graphix/src/main/java/org/apache/asterix/graphix/common/metadata/EdgeIdentifier.java new file mode 100644 index 0000000..925515a --- /dev/null +++ b/asterix-graphix/src/main/java/org/apache/asterix/graphix/common/metadata/EdgeIdentifier.java
@@ -0,0 +1,90 @@ +/* + * 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.graphix.common.metadata; + +import java.util.Objects; + +import org.apache.asterix.graphix.lang.struct.ElementLabel; + +/** + * A unique identifier for an edge. An edge is uniquely identified by: + * <ul> + * <li>The graph identifier associated with the edge itself.</li> + * <li>The label associated with the source vertex.</li> + * <li>The label associated with the destination vertex.</li> + * <li>The label associated with the edge itself.</li> + * </ul> + */ +public class EdgeIdentifier implements IElementIdentifier { + private static final long serialVersionUID = 1L; + private final GraphIdentifier graphIdentifier; + private final ElementLabel sourceLabel; + private final ElementLabel edgeLabel; + private final ElementLabel destinationLabel; + + public EdgeIdentifier(GraphIdentifier graphIdentifier, ElementLabel sourceLabel, ElementLabel edgeLabel, + ElementLabel destinationLabel) { + this.graphIdentifier = Objects.requireNonNull(graphIdentifier); + this.sourceLabel = Objects.requireNonNull(sourceLabel); + this.edgeLabel = Objects.requireNonNull(edgeLabel); + this.destinationLabel = Objects.requireNonNull(destinationLabel); + } + + @Override + public GraphIdentifier getGraphIdentifier() { + return graphIdentifier; + } + + public ElementLabel getSourceLabel() { + return sourceLabel; + } + + public ElementLabel getEdgeLabel() { + return edgeLabel; + } + + public ElementLabel getDestinationLabel() { + return destinationLabel; + } + + @Override + public String toString() { + return String.format("%s (:%s)-[:%s]->(:%s)", graphIdentifier, sourceLabel, edgeLabel, destinationLabel); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o instanceof EdgeIdentifier) { + EdgeIdentifier that = (EdgeIdentifier) o; + return Objects.equals(this.graphIdentifier, that.graphIdentifier) + && Objects.equals(this.sourceLabel, that.sourceLabel) + && Objects.equals(this.edgeLabel, that.edgeLabel) + && Objects.equals(this.destinationLabel, that.destinationLabel); + } + return false; + } + + @Override + public int hashCode() { + return Objects.hash(graphIdentifier, sourceLabel, edgeLabel, destinationLabel); + } +}
diff --git a/asterix-graphix/src/main/java/org/apache/asterix/graphix/common/metadata/GraphElementIdentifier.java b/asterix-graphix/src/main/java/org/apache/asterix/graphix/common/metadata/GraphElementIdentifier.java deleted file mode 100644 index f440c2f..0000000 --- a/asterix-graphix/src/main/java/org/apache/asterix/graphix/common/metadata/GraphElementIdentifier.java +++ /dev/null
@@ -1,83 +0,0 @@ -/* - * 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.graphix.common.metadata; - -import java.io.Serializable; -import java.util.Objects; - -import org.apache.asterix.graphix.lang.struct.ElementLabel; - -/** - * A unique identifier for a graph element (vertex or edge). A graph element is uniquely identified by: - * 1. The graph identifier associated with the graph element itself. - * 2. The kind of the element (vertex or edge). - * 3. The label associated with the element itself- a graph element has only one label in our user model. - */ -public class GraphElementIdentifier implements Serializable { - private static final long serialVersionUID = 1L; - private final GraphIdentifier graphIdentifier; - private final Kind elementKind; - private final ElementLabel elementLabel; - - public GraphElementIdentifier(GraphIdentifier graphIdentifier, Kind elementKind, ElementLabel elementLabel) { - this.graphIdentifier = graphIdentifier; - this.elementKind = elementKind; - this.elementLabel = elementLabel; - } - - public GraphIdentifier getGraphIdentifier() { - return graphIdentifier; - } - - public Kind getElementKind() { - return elementKind; - } - - public ElementLabel getElementLabel() { - return elementLabel; - } - - @Override - public String toString() { - return graphIdentifier + "#" + elementLabel + " ( " + elementKind + " )"; - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o instanceof GraphElementIdentifier) { - GraphElementIdentifier that = (GraphElementIdentifier) o; - return graphIdentifier.equals(that.graphIdentifier) && elementKind.equals(that.elementKind) - && elementLabel.equals(that.elementLabel); - } - return false; - } - - @Override - public int hashCode() { - return Objects.hash(graphIdentifier, elementKind, elementLabel); - } - - public enum Kind { - VERTEX, - EDGE - } -}
diff --git a/asterix-graphix/src/main/java/org/apache/asterix/graphix/common/metadata/GraphIdentifier.java b/asterix-graphix/src/main/java/org/apache/asterix/graphix/common/metadata/GraphIdentifier.java index c3cc5bd..2172425 100644 --- a/asterix-graphix/src/main/java/org/apache/asterix/graphix/common/metadata/GraphIdentifier.java +++ b/asterix-graphix/src/main/java/org/apache/asterix/graphix/common/metadata/GraphIdentifier.java
@@ -25,8 +25,10 @@ /** * A unique identifier for a graph. A graph is uniquely identified by: - * 1. The dataverse associated with the graph. A graph identifier must always belong to some dataverse. - * 2. The name of the graph. Anonymous graphs should have a name generated from its respective GRAPH-CONSTRUCTOR. + * <ul> + * <li>The dataverse associated with the graph. A graph identifier must always belong to some dataverse.</li> + * <li>The name of the graph. Anonymous graphs should have a name generated from its respective GRAPH-CONSTRUCTOR.</li> + * </ul> */ public class GraphIdentifier implements Serializable { private static final long serialVersionUID = 1L; @@ -58,7 +60,7 @@ } if (o instanceof GraphIdentifier) { GraphIdentifier that = (GraphIdentifier) o; - return dataverseName.equals(that.dataverseName) && Objects.equals(graphName, that.graphName); + return Objects.equals(this.dataverseName, that.dataverseName) && Objects.equals(graphName, that.graphName); } return false; }
diff --git a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/lower/transform/ISequenceTransformer.java b/asterix-graphix/src/main/java/org/apache/asterix/graphix/common/metadata/IElementIdentifier.java similarity index 75% copy from asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/lower/transform/ISequenceTransformer.java copy to asterix-graphix/src/main/java/org/apache/asterix/graphix/common/metadata/IElementIdentifier.java index 4509792..2daff9f 100644 --- a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/lower/transform/ISequenceTransformer.java +++ b/asterix-graphix/src/main/java/org/apache/asterix/graphix/common/metadata/IElementIdentifier.java
@@ -16,11 +16,11 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.asterix.graphix.lang.rewrites.lower.transform; +package org.apache.asterix.graphix.common.metadata; -import org.apache.asterix.common.exceptions.CompilationException; +import java.io.Serializable; @FunctionalInterface -public interface ISequenceTransformer { - void accept(CorrelatedClauseSequence clauseSequence) throws CompilationException; +public interface IElementIdentifier extends Serializable { + GraphIdentifier getGraphIdentifier(); }
diff --git a/asterix-graphix/src/main/java/org/apache/asterix/graphix/common/metadata/VertexIdentifier.java b/asterix-graphix/src/main/java/org/apache/asterix/graphix/common/metadata/VertexIdentifier.java new file mode 100644 index 0000000..12fdb9d --- /dev/null +++ b/asterix-graphix/src/main/java/org/apache/asterix/graphix/common/metadata/VertexIdentifier.java
@@ -0,0 +1,73 @@ +/* + * 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.graphix.common.metadata; + +import java.util.Objects; + +import org.apache.asterix.graphix.lang.struct.ElementLabel; + +/** + * A unique identifier for a vertex. A vertex is uniquely identified by: + * <ul> + * <li>The graph identifier associated with the vertex itself.</li> + * <li>The label associated with the vertex itself.</li> + * </ul> + */ +public class VertexIdentifier implements IElementIdentifier { + private static final long serialVersionUID = 1L; + private final GraphIdentifier graphIdentifier; + private final ElementLabel vertexLabel; + + public VertexIdentifier(GraphIdentifier graphIdentifier, ElementLabel vertexLabel) { + this.graphIdentifier = graphIdentifier; + this.vertexLabel = vertexLabel; + } + + @Override + public GraphIdentifier getGraphIdentifier() { + return graphIdentifier; + } + + public ElementLabel getVertexLabel() { + return vertexLabel; + } + + @Override + public String toString() { + return String.format("%s (:%s)", graphIdentifier, vertexLabel); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o instanceof VertexIdentifier) { + VertexIdentifier that = (VertexIdentifier) o; + return Objects.equals(this.graphIdentifier, that.graphIdentifier) + && Objects.equals(this.vertexLabel, that.vertexLabel); + } + return false; + } + + @Override + public int hashCode() { + return Objects.hash(graphIdentifier, vertexLabel); + } +}
diff --git a/asterix-graphix/src/main/java/org/apache/asterix/graphix/extension/GraphixQueryTranslatorExtension.java b/asterix-graphix/src/main/java/org/apache/asterix/graphix/extension/GraphixQueryTranslatorExtension.java index b6f7753..139c8a8 100644 --- a/asterix-graphix/src/main/java/org/apache/asterix/graphix/extension/GraphixQueryTranslatorExtension.java +++ b/asterix-graphix/src/main/java/org/apache/asterix/graphix/extension/GraphixQueryTranslatorExtension.java
@@ -30,7 +30,7 @@ public static final ExtensionId GRAPHIX_QUERY_TRANSLATOR_EXTENSION_ID = new ExtensionId(GraphixQueryTranslatorExtension.class.getSimpleName(), 0); - private static final IStatementExecutorFactory INSTANCE = new GraphixQueryTranslatorFactory(); + private static final GraphixQueryTranslatorFactory INSTANCE = new GraphixQueryTranslatorFactory(); @Override public ExtensionId getId() { @@ -39,6 +39,7 @@ @Override public void configure(List<Pair<String, String>> args) { + INSTANCE.setConfigFileProvidedOptions(args); } @Override
diff --git a/asterix-graphix/src/main/java/org/apache/asterix/graphix/function/GraphixFunctionIdentifiers.java b/asterix-graphix/src/main/java/org/apache/asterix/graphix/function/GraphixFunctionIdentifiers.java index 50e58b9..a5dc34b 100644 --- a/asterix-graphix/src/main/java/org/apache/asterix/graphix/function/GraphixFunctionIdentifiers.java +++ b/asterix-graphix/src/main/java/org/apache/asterix/graphix/function/GraphixFunctionIdentifiers.java
@@ -69,8 +69,24 @@ public static final FunctionIdentifier PATH_EDGES = new FunctionIdentifier(GRAPHIX_DV.getCanonicalForm(), "path-edges", 1); + // Private functions used internally to enforce navigation semantics. + public static final FunctionIdentifier IS_DISTINCT_EDGE = + new FunctionIdentifier(GRAPHIX_DV.getCanonicalForm(), "is-distinct-edge", 2); + public static final FunctionIdentifier IS_DISTINCT_VERTEX = + new FunctionIdentifier(GRAPHIX_DV.getCanonicalForm(), "is-distinct-vertex", 2); + public static final FunctionIdentifier IS_DISTINCT_EVERYTHING = + new FunctionIdentifier(GRAPHIX_DV.getCanonicalForm(), "is-distinct-everything", 3); + + // Private functions used internally to manage a path during navigation. + public static final FunctionIdentifier CREATE_INTERNAL_PATH = + new FunctionIdentifier(GRAPHIX_DV.getCanonicalForm(), "create-internal-path", 1); + public static final FunctionIdentifier APPEND_INTERNAL_PATH = + new FunctionIdentifier(GRAPHIX_DV.getCanonicalForm(), "append-internal-path", 3); + public static final FunctionIdentifier MATERIALIZE_PATH = + new FunctionIdentifier(GRAPHIX_DV.getCanonicalForm(), "materialize-path", 1); + static { - // Register all the functions above. + // Register the non-internal functions above. functionIdentifierMap = new HashMap<>(); Consumer<FunctionIdentifier> functionRegister = f -> functionIdentifierMap.put(f.getName(), f); functionRegister.accept(ELEMENT_LABEL);
diff --git a/asterix-graphix/src/main/java/org/apache/asterix/graphix/function/GraphixFunctionMap.java b/asterix-graphix/src/main/java/org/apache/asterix/graphix/function/GraphixFunctionMap.java index d1f3c89..a17ebac 100644 --- a/asterix-graphix/src/main/java/org/apache/asterix/graphix/function/GraphixFunctionMap.java +++ b/asterix-graphix/src/main/java/org/apache/asterix/graphix/function/GraphixFunctionMap.java
@@ -34,12 +34,14 @@ import org.apache.asterix.graphix.function.rewrite.PathHopCountRewrite; import org.apache.asterix.graphix.function.rewrite.PathVerticesRewrite; import org.apache.asterix.graphix.function.rewrite.SchemaAccessRewrite; +import org.apache.asterix.graphix.lang.rewrite.visitor.GraphixFunctionCallVisitor; +import org.apache.asterix.graphix.lang.rewrite.visitor.SchemaEnrichmentVisitor; import org.apache.asterix.lang.common.struct.Identifier; import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier; /** - * @see org.apache.asterix.graphix.lang.rewrites.visitor.SchemaEnrichmentVisitor - * @see org.apache.asterix.graphix.lang.rewrites.visitor.GraphixFunctionCallVisitor + * @see SchemaEnrichmentVisitor + * @see GraphixFunctionCallVisitor */ public class GraphixFunctionMap { private final static Map<FunctionIdentifier, IFunctionPrepare> graphixFunctionPrepareMap;
diff --git a/asterix-graphix/src/main/java/org/apache/asterix/graphix/function/prepare/AbstractElementPrepare.java b/asterix-graphix/src/main/java/org/apache/asterix/graphix/function/prepare/AbstractElementPrepare.java index d93f670..82ad445 100644 --- a/asterix-graphix/src/main/java/org/apache/asterix/graphix/function/prepare/AbstractElementPrepare.java +++ b/asterix-graphix/src/main/java/org/apache/asterix/graphix/function/prepare/AbstractElementPrepare.java
@@ -26,7 +26,7 @@ import org.apache.asterix.common.functions.FunctionSignature; import org.apache.asterix.graphix.common.metadata.GraphIdentifier; import org.apache.asterix.graphix.lang.annotation.GraphixSchemaAnnotation; -import org.apache.asterix.graphix.lang.rewrites.common.ElementLookupTable; +import org.apache.asterix.graphix.lang.rewrite.common.ElementLookupTable; import org.apache.asterix.lang.common.base.Expression; import org.apache.asterix.lang.common.expression.CallExpr; import org.apache.asterix.lang.common.expression.FieldBinding;
diff --git a/asterix-graphix/src/main/java/org/apache/asterix/graphix/function/prepare/EdgeDestVertexPrepare.java b/asterix-graphix/src/main/java/org/apache/asterix/graphix/function/prepare/EdgeDestVertexPrepare.java index 7f95f28..affe5dd 100644 --- a/asterix-graphix/src/main/java/org/apache/asterix/graphix/function/prepare/EdgeDestVertexPrepare.java +++ b/asterix-graphix/src/main/java/org/apache/asterix/graphix/function/prepare/EdgeDestVertexPrepare.java
@@ -25,7 +25,6 @@ import org.apache.asterix.lang.common.expression.FieldBinding; import org.apache.asterix.lang.common.expression.LiteralExpr; import org.apache.asterix.lang.common.expression.RecordConstructor; -import org.apache.asterix.lang.common.expression.VariableExpr; import org.apache.asterix.lang.common.literal.StringLiteral; import org.apache.asterix.lang.common.struct.Identifier; @@ -42,9 +41,8 @@ EdgeDescriptor.EdgeDirection edgeDirection = edgeDescriptor.getEdgeDirection(); VertexPatternExpr destVertexExpr = (edgeDirection == EdgeDescriptor.EdgeDirection.LEFT_TO_RIGHT) ? edgePatternExpr.getRightVertex() : edgePatternExpr.getLeftVertex(); - VariableExpr destVariableExpr = new VariableExpr(destVertexExpr.getVariableExpr().getVar()); LiteralExpr fieldNameExpr = new LiteralExpr(new StringLiteral(IDENTIFIER.getValue())); - FieldBinding fieldBinding = new FieldBinding(fieldNameExpr, destVariableExpr); + FieldBinding fieldBinding = new FieldBinding(fieldNameExpr, destVertexExpr.getVariableExpr()); schemaRecord.getFbList().add(fieldBinding); } }
diff --git a/asterix-graphix/src/main/java/org/apache/asterix/graphix/function/prepare/EdgeDetailPrepare.java b/asterix-graphix/src/main/java/org/apache/asterix/graphix/function/prepare/EdgeDetailPrepare.java index 6efd635..a507bd4 100644 --- a/asterix-graphix/src/main/java/org/apache/asterix/graphix/function/prepare/EdgeDetailPrepare.java +++ b/asterix-graphix/src/main/java/org/apache/asterix/graphix/function/prepare/EdgeDetailPrepare.java
@@ -25,10 +25,9 @@ import java.util.stream.Collectors; import org.apache.asterix.common.exceptions.CompilationException; -import org.apache.asterix.graphix.common.metadata.GraphElementIdentifier; +import org.apache.asterix.graphix.common.metadata.EdgeIdentifier; import org.apache.asterix.graphix.lang.expression.EdgePatternExpr; -import org.apache.asterix.graphix.lang.rewrites.util.LowerRewritingUtil; -import org.apache.asterix.graphix.lang.struct.EdgeDescriptor; +import org.apache.asterix.graphix.lang.rewrite.util.LowerRewritingUtil; import org.apache.asterix.lang.common.base.Expression; import org.apache.asterix.lang.common.expression.FieldBinding; import org.apache.asterix.lang.common.expression.ListConstructor; @@ -49,7 +48,6 @@ return; } EdgePatternExpr edgePatternExpr = (EdgePatternExpr) inputExpr; - EdgeDescriptor edgeDescriptor = edgePatternExpr.getEdgeDescriptor(); // Insert our detail record into our schema. RecordConstructor detailRecord = new RecordConstructor(new ArrayList<>()); @@ -66,7 +64,7 @@ edgeDirectionPrepare.transformRecord(detailRecord, inputExpr, sourceExpr); // Insert our source-key into our detail record. - GraphElementIdentifier edgeIdentifier = edgeDescriptor.generateIdentifiers(graphIdentifier).get(0); + EdgeIdentifier edgeIdentifier = edgePatternExpr.generateIdentifiers(graphIdentifier).get(0); List<List<String>> edgeSourceKey = elementLookupTable.getEdgeSourceKey(edgeIdentifier); List<Expression> sourceKeyExprList = LowerRewritingUtil.buildAccessorList(sourceExpr, edgeSourceKey).stream() .map(e -> (Expression) e).collect(Collectors.toList());
diff --git a/asterix-graphix/src/main/java/org/apache/asterix/graphix/function/prepare/EdgeDirectionPrepare.java b/asterix-graphix/src/main/java/org/apache/asterix/graphix/function/prepare/EdgeDirectionPrepare.java index 4a9a216..df2d898 100644 --- a/asterix-graphix/src/main/java/org/apache/asterix/graphix/function/prepare/EdgeDirectionPrepare.java +++ b/asterix-graphix/src/main/java/org/apache/asterix/graphix/function/prepare/EdgeDirectionPrepare.java
@@ -38,7 +38,7 @@ EdgePatternExpr edgePatternExpr = (EdgePatternExpr) inputExpr; EdgeDescriptor edgeDescriptor = edgePatternExpr.getEdgeDescriptor(); EdgeDescriptor.EdgeDirection edgeDirection = edgeDescriptor.getEdgeDirection(); - LiteralExpr fieldValueExpr = new LiteralExpr(new StringLiteral(edgeDirection.toString())); + LiteralExpr fieldValueExpr = new LiteralExpr(new StringLiteral(edgeDirection.name())); LiteralExpr fieldNameExpr = new LiteralExpr(new StringLiteral(IDENTIFIER.getValue())); FieldBinding fieldBinding = new FieldBinding(fieldNameExpr, fieldValueExpr); schemaRecord.getFbList().add(fieldBinding);
diff --git a/asterix-graphix/src/main/java/org/apache/asterix/graphix/function/prepare/EdgeSourceVertexPrepare.java b/asterix-graphix/src/main/java/org/apache/asterix/graphix/function/prepare/EdgeSourceVertexPrepare.java index 5cc7bba..043f55a 100644 --- a/asterix-graphix/src/main/java/org/apache/asterix/graphix/function/prepare/EdgeSourceVertexPrepare.java +++ b/asterix-graphix/src/main/java/org/apache/asterix/graphix/function/prepare/EdgeSourceVertexPrepare.java
@@ -42,9 +42,9 @@ EdgeDescriptor.EdgeDirection edgeDirection = edgeDescriptor.getEdgeDirection(); VertexPatternExpr sourceVertexExpr = (edgeDirection == EdgeDescriptor.EdgeDirection.LEFT_TO_RIGHT) ? edgePatternExpr.getLeftVertex() : edgePatternExpr.getRightVertex(); - VariableExpr sourceVariableExpr = new VariableExpr(sourceVertexExpr.getVariableExpr().getVar()); + VariableExpr sourceVariableExprCopy = new VariableExpr(sourceVertexExpr.getVariableExpr().getVar()); LiteralExpr fieldNameExpr = new LiteralExpr(new StringLiteral(IDENTIFIER.getValue())); - FieldBinding fieldBinding = new FieldBinding(fieldNameExpr, sourceVariableExpr); + FieldBinding fieldBinding = new FieldBinding(fieldNameExpr, sourceVariableExprCopy); schemaRecord.getFbList().add(fieldBinding); } }
diff --git a/asterix-graphix/src/main/java/org/apache/asterix/graphix/function/prepare/IFunctionPrepare.java b/asterix-graphix/src/main/java/org/apache/asterix/graphix/function/prepare/IFunctionPrepare.java index 6c4c2b1..8f67fa5 100644 --- a/asterix-graphix/src/main/java/org/apache/asterix/graphix/function/prepare/IFunctionPrepare.java +++ b/asterix-graphix/src/main/java/org/apache/asterix/graphix/function/prepare/IFunctionPrepare.java
@@ -20,7 +20,7 @@ import org.apache.asterix.common.exceptions.CompilationException; import org.apache.asterix.graphix.common.metadata.GraphIdentifier; -import org.apache.asterix.graphix.lang.rewrites.common.ElementLookupTable; +import org.apache.asterix.graphix.lang.rewrite.common.ElementLookupTable; import org.apache.asterix.lang.common.base.Expression; @FunctionalInterface
diff --git a/asterix-graphix/src/main/java/org/apache/asterix/graphix/function/prepare/VertexDetailPrepare.java b/asterix-graphix/src/main/java/org/apache/asterix/graphix/function/prepare/VertexDetailPrepare.java index c6cde11..d9c583f 100644 --- a/asterix-graphix/src/main/java/org/apache/asterix/graphix/function/prepare/VertexDetailPrepare.java +++ b/asterix-graphix/src/main/java/org/apache/asterix/graphix/function/prepare/VertexDetailPrepare.java
@@ -25,9 +25,9 @@ import java.util.stream.Collectors; import org.apache.asterix.common.exceptions.CompilationException; -import org.apache.asterix.graphix.common.metadata.GraphElementIdentifier; +import org.apache.asterix.graphix.common.metadata.VertexIdentifier; import org.apache.asterix.graphix.lang.expression.VertexPatternExpr; -import org.apache.asterix.graphix.lang.rewrites.util.LowerRewritingUtil; +import org.apache.asterix.graphix.lang.rewrite.util.LowerRewritingUtil; import org.apache.asterix.lang.common.base.Expression; import org.apache.asterix.lang.common.expression.FieldBinding; import org.apache.asterix.lang.common.expression.ListConstructor; @@ -59,7 +59,7 @@ elementLabelPrepare.transformRecord(detailRecord, inputExpr, sourceExpr); // Insert our vertex-key into our detail record. - GraphElementIdentifier vertexIdentifier = vertexPatternExpr.generateIdentifiers(graphIdentifier).get(0); + VertexIdentifier vertexIdentifier = vertexPatternExpr.generateIdentifiers(graphIdentifier).get(0); List<List<String>> vertexKey = elementLookupTable.getVertexKey(vertexIdentifier); List<Expression> vertexKeyExprList = LowerRewritingUtil.buildAccessorList(sourceExpr, vertexKey).stream() .map(e -> (Expression) e).collect(Collectors.toList());
diff --git a/asterix-graphix/src/main/java/org/apache/asterix/graphix/function/rewrite/IFunctionRewrite.java b/asterix-graphix/src/main/java/org/apache/asterix/graphix/function/rewrite/IFunctionRewrite.java index 54b1963..cce1ec0 100644 --- a/asterix-graphix/src/main/java/org/apache/asterix/graphix/function/rewrite/IFunctionRewrite.java +++ b/asterix-graphix/src/main/java/org/apache/asterix/graphix/function/rewrite/IFunctionRewrite.java
@@ -20,7 +20,7 @@ import org.apache.asterix.common.exceptions.CompilationException; import org.apache.asterix.graphix.function.GraphixFunctionMap; -import org.apache.asterix.graphix.lang.rewrites.GraphixRewritingContext; +import org.apache.asterix.graphix.lang.rewrite.GraphixRewritingContext; import org.apache.asterix.lang.common.base.Expression; import org.apache.asterix.lang.common.expression.CallExpr;
diff --git a/asterix-graphix/src/main/java/org/apache/asterix/graphix/function/rewrite/PathEdgesRewrite.java b/asterix-graphix/src/main/java/org/apache/asterix/graphix/function/rewrite/PathEdgesRewrite.java index 3a1cca0..ad4a39c 100644 --- a/asterix-graphix/src/main/java/org/apache/asterix/graphix/function/rewrite/PathEdgesRewrite.java +++ b/asterix-graphix/src/main/java/org/apache/asterix/graphix/function/rewrite/PathEdgesRewrite.java
@@ -21,8 +21,8 @@ import org.apache.asterix.common.exceptions.CompilationException; import org.apache.asterix.common.exceptions.ErrorCode; import org.apache.asterix.graphix.function.GraphixFunctionIdentifiers; -import org.apache.asterix.graphix.lang.rewrites.GraphixRewritingContext; -import org.apache.asterix.graphix.lang.rewrites.lower.action.PathPatternAction; +import org.apache.asterix.graphix.lang.rewrite.GraphixRewritingContext; +import org.apache.asterix.graphix.type.MaterializePathTypeComputer; import org.apache.asterix.lang.common.base.Expression; import org.apache.asterix.lang.common.expression.CallExpr; import org.apache.asterix.lang.common.expression.FieldAccessor; @@ -36,7 +36,7 @@ throw new CompilationException(ErrorCode.ILLEGAL_FUNCTION_USE, callExpr.getSourceLocation(), GraphixFunctionIdentifiers.PATH_EDGES.toString()); } - Identifier pathEdgeIdentifier = new Identifier(PathPatternAction.PATH_EDGES_FIELD_NAME); + Identifier pathEdgeIdentifier = new Identifier(MaterializePathTypeComputer.EDGES_FIELD_NAME); FieldAccessor pathEdgeAccess = new FieldAccessor(callExpr.getExprList().get(0), pathEdgeIdentifier); pathEdgeAccess.setSourceLocation(callExpr.getSourceLocation()); return pathEdgeAccess;
diff --git a/asterix-graphix/src/main/java/org/apache/asterix/graphix/function/rewrite/PathHopCountRewrite.java b/asterix-graphix/src/main/java/org/apache/asterix/graphix/function/rewrite/PathHopCountRewrite.java index 32ebd92..e6f3f50 100644 --- a/asterix-graphix/src/main/java/org/apache/asterix/graphix/function/rewrite/PathHopCountRewrite.java +++ b/asterix-graphix/src/main/java/org/apache/asterix/graphix/function/rewrite/PathHopCountRewrite.java
@@ -25,8 +25,8 @@ import org.apache.asterix.common.exceptions.ErrorCode; import org.apache.asterix.common.functions.FunctionSignature; import org.apache.asterix.graphix.function.GraphixFunctionIdentifiers; -import org.apache.asterix.graphix.lang.rewrites.GraphixRewritingContext; -import org.apache.asterix.graphix.lang.rewrites.lower.action.PathPatternAction; +import org.apache.asterix.graphix.lang.rewrite.GraphixRewritingContext; +import org.apache.asterix.graphix.type.MaterializePathTypeComputer; import org.apache.asterix.lang.common.base.Expression; import org.apache.asterix.lang.common.expression.CallExpr; import org.apache.asterix.lang.common.expression.FieldAccessor; @@ -44,7 +44,7 @@ // Access the edges in our path. List<Expression> countFunctionArguments = new ArrayList<>(); - Identifier pathEdgeIdentifier = new Identifier(PathPatternAction.PATH_EDGES_FIELD_NAME); + Identifier pathEdgeIdentifier = new Identifier(MaterializePathTypeComputer.EDGES_FIELD_NAME); FieldAccessor pathEdgeAccess = new FieldAccessor(callExpr.getExprList().get(0), pathEdgeIdentifier); pathEdgeAccess.setSourceLocation(callExpr.getSourceLocation()); countFunctionArguments.add(pathEdgeAccess);
diff --git a/asterix-graphix/src/main/java/org/apache/asterix/graphix/function/rewrite/PathVerticesRewrite.java b/asterix-graphix/src/main/java/org/apache/asterix/graphix/function/rewrite/PathVerticesRewrite.java index 1dbc027..c9f5d1e 100644 --- a/asterix-graphix/src/main/java/org/apache/asterix/graphix/function/rewrite/PathVerticesRewrite.java +++ b/asterix-graphix/src/main/java/org/apache/asterix/graphix/function/rewrite/PathVerticesRewrite.java
@@ -21,8 +21,8 @@ import org.apache.asterix.common.exceptions.CompilationException; import org.apache.asterix.common.exceptions.ErrorCode; import org.apache.asterix.graphix.function.GraphixFunctionIdentifiers; -import org.apache.asterix.graphix.lang.rewrites.GraphixRewritingContext; -import org.apache.asterix.graphix.lang.rewrites.lower.action.PathPatternAction; +import org.apache.asterix.graphix.lang.rewrite.GraphixRewritingContext; +import org.apache.asterix.graphix.type.MaterializePathTypeComputer; import org.apache.asterix.lang.common.base.Expression; import org.apache.asterix.lang.common.expression.CallExpr; import org.apache.asterix.lang.common.expression.FieldAccessor; @@ -36,7 +36,7 @@ throw new CompilationException(ErrorCode.ILLEGAL_FUNCTION_USE, callExpr.getSourceLocation(), GraphixFunctionIdentifiers.PATH_VERTICES.toString()); } - Identifier pathVertexIdentifier = new Identifier(PathPatternAction.PATH_VERTICES_FIELD_NAME); + Identifier pathVertexIdentifier = new Identifier(MaterializePathTypeComputer.VERTICES_FIELD_NAME); FieldAccessor pathVertexAccess = new FieldAccessor(callExpr.getExprList().get(0), pathVertexIdentifier); pathVertexAccess.setSourceLocation(callExpr.getSourceLocation()); return pathVertexAccess;
diff --git a/asterix-graphix/src/main/java/org/apache/asterix/graphix/function/rewrite/SchemaAccessRewrite.java b/asterix-graphix/src/main/java/org/apache/asterix/graphix/function/rewrite/SchemaAccessRewrite.java index 73fb2a6..639278b 100644 --- a/asterix-graphix/src/main/java/org/apache/asterix/graphix/function/rewrite/SchemaAccessRewrite.java +++ b/asterix-graphix/src/main/java/org/apache/asterix/graphix/function/rewrite/SchemaAccessRewrite.java
@@ -21,7 +21,7 @@ import org.apache.asterix.common.exceptions.CompilationException; import org.apache.asterix.common.exceptions.ErrorCode; import org.apache.asterix.graphix.function.prepare.AbstractElementPrepare; -import org.apache.asterix.graphix.lang.rewrites.GraphixRewritingContext; +import org.apache.asterix.graphix.lang.rewrite.GraphixRewritingContext; import org.apache.asterix.lang.common.base.Expression; import org.apache.asterix.lang.common.expression.CallExpr; import org.apache.asterix.lang.common.expression.FieldAccessor;
diff --git a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/print/GraphixASTPrintVisitorFactory.java b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/annotation/ElementEvaluationAnnotation.java similarity index 60% copy from asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/print/GraphixASTPrintVisitorFactory.java copy to asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/annotation/ElementEvaluationAnnotation.java index ff00077..9dfa97f 100644 --- a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/print/GraphixASTPrintVisitorFactory.java +++ b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/annotation/ElementEvaluationAnnotation.java
@@ -16,16 +16,26 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.asterix.graphix.lang.rewrites.print; +package org.apache.asterix.graphix.lang.annotation; -import java.io.PrintWriter; +import org.apache.hyracks.algebricks.core.algebra.expressions.IExpressionAnnotation; -import org.apache.asterix.lang.common.base.IAstPrintVisitorFactory; -import org.apache.asterix.lang.common.visitor.QueryPrintVisitor; +/** + * Annotation used to attach an evaluation approach to a graph element. + */ +public class ElementEvaluationAnnotation implements IExpressionAnnotation { + private final Kind kind; -public class GraphixASTPrintVisitorFactory implements IAstPrintVisitorFactory { - @Override - public QueryPrintVisitor createLangVisitor(PrintWriter writer) { - return new GraphixASTPrintVisitor(writer); + public ElementEvaluationAnnotation(Kind kind) { + this.kind = kind; + } + + public Kind getKind() { + return kind; + } + + public enum Kind { + EXPAND_AND_UNION, + SWITCH_AND_CYCLE } }
diff --git a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/lower/transform/ISequenceTransformer.java b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/annotation/LoweringExemptAnnotation.java similarity index 63% copy from asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/lower/transform/ISequenceTransformer.java copy to asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/annotation/LoweringExemptAnnotation.java index 4509792..3f59a72 100644 --- a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/lower/transform/ISequenceTransformer.java +++ b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/annotation/LoweringExemptAnnotation.java
@@ -16,11 +16,16 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.asterix.graphix.lang.rewrites.lower.transform; +package org.apache.asterix.graphix.lang.annotation; -import org.apache.asterix.common.exceptions.CompilationException; +import org.apache.hyracks.algebricks.core.algebra.expressions.IExpressionAnnotation; -@FunctionalInterface -public interface ISequenceTransformer { - void accept(CorrelatedClauseSequence clauseSequence) throws CompilationException; +/** + * Annotation used to indicate that a VERTEX-PATTERN-EXPR or an EDGE-PATTERN-EXPR should not be lowered. + */ +public class LoweringExemptAnnotation implements IExpressionAnnotation { + public static final LoweringExemptAnnotation INSTANCE = new LoweringExemptAnnotation(); + + private LoweringExemptAnnotation() { + } }
diff --git a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/annotation/SubqueryVertexJoinAnnotation.java b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/annotation/SubqueryVertexJoinAnnotation.java new file mode 100644 index 0000000..8425b14 --- /dev/null +++ b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/annotation/SubqueryVertexJoinAnnotation.java
@@ -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. + */ +package org.apache.asterix.graphix.lang.annotation; + +import org.apache.asterix.lang.common.expression.VariableExpr; +import org.apache.hyracks.algebricks.core.algebra.expressions.IExpressionAnnotation; + +/** + * Annotation used to indicate that a VERTEX-PATTERN-EXPR has been rewritten to JOIN with another VERTEX-PATTERN-EXPR + * in a non-local scope. + */ +public class SubqueryVertexJoinAnnotation implements IExpressionAnnotation { + private final VariableExpr sourceVertexVariable; + + public SubqueryVertexJoinAnnotation(VariableExpr sourceVertexVariable) { + this.sourceVertexVariable = sourceVertexVariable; + } + + public VariableExpr getSourceVertexVariable() { + return sourceVertexVariable; + } +}
diff --git a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/clause/CorrLetClause.java b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/clause/CorrLetClause.java deleted file mode 100644 index 1a80aff..0000000 --- a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/clause/CorrLetClause.java +++ /dev/null
@@ -1,64 +0,0 @@ -/* - * 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.graphix.lang.clause; - -import org.apache.asterix.common.exceptions.CompilationException; -import org.apache.asterix.graphix.lang.rewrites.visitor.ILetCorrelateClauseVisitor; -import org.apache.asterix.lang.common.base.Expression; -import org.apache.asterix.lang.common.clause.LetClause; -import org.apache.asterix.lang.common.expression.VariableExpr; -import org.apache.asterix.lang.common.visitor.base.ILangVisitor; -import org.apache.asterix.lang.sqlpp.clause.AbstractBinaryCorrelateClause; - -/** - * Clause for introducing a {@link LetClause} into the scope of any correlated clauses. This clause allows us to avoid - * nesting the Graphix lowering result to handle any correlated clauses on graph elements. - */ -public class CorrLetClause extends AbstractBinaryCorrelateClause { - private final LetClause letClause; - - public CorrLetClause(Expression rightExpr, VariableExpr rightVar, VariableExpr rightPosVar) { - super(rightExpr, rightVar, rightPosVar); - letClause = new LetClause((rightVar == null) ? rightPosVar : rightVar, rightExpr); - } - - @Override - public void setRightExpression(Expression rightExpr) { - VariableExpr variableExpr = (getRightVariable() == null) ? getPositionalVariable() : getRightVariable(); - letClause.setVarExpr(variableExpr); - letClause.setBindingExpr(rightExpr); - super.setRightExpression(rightExpr); - } - - @Override - public ClauseType getClauseType() { - return ClauseType.LET_CLAUSE; - } - - @Override - public <R, T> R accept(ILangVisitor<R, T> visitor, T arg) throws CompilationException { - if (visitor instanceof ILetCorrelateClauseVisitor) { - return ((ILetCorrelateClauseVisitor<R, T>) visitor).visit(this, arg); - - } else { - // This node will survive our Graphix lowering, so by default we call the dispatch on our LET-CLAUSE node. - return letClause.accept(visitor, arg); - } - } -}
diff --git a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/clause/CorrWhereClause.java b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/clause/CorrWhereClause.java deleted file mode 100644 index 98ad42a..0000000 --- a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/clause/CorrWhereClause.java +++ /dev/null
@@ -1,57 +0,0 @@ -/* - * 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.graphix.lang.clause; - -import org.apache.asterix.common.exceptions.CompilationException; -import org.apache.asterix.lang.common.base.Expression; -import org.apache.asterix.lang.common.clause.WhereClause; -import org.apache.asterix.lang.common.visitor.base.ILangVisitor; -import org.apache.asterix.lang.sqlpp.clause.AbstractBinaryCorrelateClause; - -/** - * Clause for introducing a {@link WhereClause} in an intermediate list of correlated clauses. This clause allows us to - * perform predicate push-down at the AST level (rather than at the Algebricks level). - */ -public class CorrWhereClause extends AbstractBinaryCorrelateClause { - private final WhereClause whereClause; - - public CorrWhereClause(Expression conditionExpr) { - super(conditionExpr, null, null); - whereClause = new WhereClause(conditionExpr); - } - - public Expression getExpression() { - return whereClause.getWhereExpr(); - } - - public void setExpression(Expression expression) { - whereClause.setWhereExpr(expression); - } - - @Override - public ClauseType getClauseType() { - return ClauseType.WHERE_CLAUSE; - } - - @Override - public <R, T> R accept(ILangVisitor<R, T> visitor, T arg) throws CompilationException { - // This node will survive our Graphix lowering, so by default we call the dispatch on our WHERE-CLAUSE node. - return whereClause.accept(visitor, arg); - } -}
diff --git a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/clause/FromGraphClause.java b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/clause/FromGraphClause.java index d8d4cad..c4455d5 100644 --- a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/clause/FromGraphClause.java +++ b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/clause/FromGraphClause.java
@@ -18,45 +18,58 @@ */ package org.apache.asterix.graphix.lang.clause; +import java.util.Collections; import java.util.List; import java.util.Objects; import org.apache.asterix.common.exceptions.CompilationException; import org.apache.asterix.common.metadata.DataverseName; +import org.apache.asterix.graphix.common.metadata.GraphIdentifier; import org.apache.asterix.graphix.lang.expression.GraphConstructor; -import org.apache.asterix.graphix.lang.rewrites.visitor.IGraphixLangVisitor; -import org.apache.asterix.lang.common.base.AbstractClause; +import org.apache.asterix.graphix.lang.visitor.base.IGraphixLangVisitor; +import org.apache.asterix.lang.common.base.AbstractExtensionClause; import org.apache.asterix.lang.common.struct.Identifier; import org.apache.asterix.lang.common.visitor.base.ILangVisitor; import org.apache.asterix.lang.sqlpp.clause.AbstractBinaryCorrelateClause; +import org.apache.asterix.lang.sqlpp.clause.FromClause; +import org.apache.asterix.lang.sqlpp.visitor.base.ISqlppVisitor; +import org.apache.asterix.metadata.declared.MetadataProvider; /** - * The logical starting AST node for Graphix queries. A FROM-GRAPH node includes the following: - * - Either a {@link GraphConstructor} OR a [dataverse, graph name] pair. The former indicates that we are dealing with - * an anonymous graph, while the latter indicates that we must search our metadata for the graph. - * - A list of {@link MatchClause} nodes, with a minimum size of one. The first MATCH node type must always be LEADING. - * - A list of {@link AbstractBinaryCorrelateClause} nodes, which may be empty. These include UNNEST and explicit JOINs. + * The logical starting AST node for Graphix queries. Lowering a Graphix AST involves setting the + * {@link AbstractExtensionClause}, initially set to null. A FROM-GRAPH node includes the following: + * <ul> + * <li>Either a {@link GraphConstructor} OR a [dataverse, graph name] pair. The former indicates that we are dealing + * with an anonymous graph, while the latter indicates that we must search our metadata for the graph.</li> + * <li>A list of {@link MatchClause} nodes, with a minimum size of one. The first MATCH node type must always be + * LEADING.</li> + * <li>A list of {@link AbstractBinaryCorrelateClause} nodes, which may be empty. These include UNNEST and explicit + * JOINs.</li> + * </ul> */ -public class FromGraphClause extends AbstractClause { - // A FROM-MATCH must either have a graph constructor... +public class FromGraphClause extends FromClause { + // A non-lowered FROM-GRAPH-CLAUSE must either have a graph constructor... private final GraphConstructor graphConstructor; // Or a reference to a named graph (both cannot be specified). private final DataverseName dataverse; private final Identifier name; - // Every FROM-MATCH **MUST** include at-least a single MATCH clause. Correlated clauses are optional. + // Every non-lowered FROM-GRAPH-CLAUSE **MUST** include at-least a single MATCH clause. private final List<MatchClause> matchClauses; private final List<AbstractBinaryCorrelateClause> correlateClauses; + // After lowering, we should have built an extension clause of some sort. + private AbstractExtensionClause lowerClause = null; + public FromGraphClause(DataverseName dataverse, Identifier name, List<MatchClause> matchClauses, List<AbstractBinaryCorrelateClause> correlateClauses) { + super(Collections.emptyList()); this.graphConstructor = null; this.dataverse = dataverse; this.name = Objects.requireNonNull(name); this.matchClauses = Objects.requireNonNull(matchClauses); this.correlateClauses = Objects.requireNonNull(correlateClauses); - if (matchClauses.isEmpty()) { throw new IllegalArgumentException("FROM-MATCH requires at least one MATCH clause."); } @@ -64,17 +77,36 @@ public FromGraphClause(GraphConstructor graphConstructor, List<MatchClause> matchClauses, List<AbstractBinaryCorrelateClause> correlateClauses) { + super(Collections.emptyList()); this.graphConstructor = Objects.requireNonNull(graphConstructor); this.dataverse = null; this.name = null; this.matchClauses = Objects.requireNonNull(matchClauses); this.correlateClauses = Objects.requireNonNull(correlateClauses); - if (matchClauses.isEmpty()) { throw new IllegalArgumentException("FROM-MATCH requires at least one MATCH clause."); } } + public FromGraphClause(AbstractExtensionClause lowerClause) { + super(Collections.emptyList()); + this.lowerClause = Objects.requireNonNull(lowerClause); + this.graphConstructor = null; + this.dataverse = null; + this.name = null; + this.matchClauses = Collections.emptyList(); + this.correlateClauses = Collections.emptyList(); + } + + public GraphIdentifier getGraphIdentifier(MetadataProvider metadataProvider) { + DataverseName dataverseName = metadataProvider.getDefaultDataverseName(); + if (this.dataverse != null) { + dataverseName = this.dataverse; + } + return (graphConstructor == null) ? new GraphIdentifier(dataverseName, name.getValue()) + : new GraphIdentifier(dataverseName, graphConstructor.getInstanceID()); + } + public GraphConstructor getGraphConstructor() { return graphConstructor; } @@ -95,25 +127,47 @@ return correlateClauses; } - @Override - public ClauseType getClauseType() { - return null; + public AbstractExtensionClause getLowerClause() { + return lowerClause; + } + + public void setLowerClause(AbstractExtensionClause lowerClause) { + this.lowerClause = lowerClause; } @Override public <R, T> R accept(ILangVisitor<R, T> visitor, T arg) throws CompilationException { - return ((IGraphixLangVisitor<R, T>) visitor).visit(this, arg); + if (visitor instanceof IGraphixLangVisitor) { + return ((IGraphixLangVisitor<R, T>) visitor).visit(this, arg); + + } else if (lowerClause != null) { + return visitor.visit(lowerClause.getVisitorExtension(), arg); + + } else { + return ((ISqlppVisitor<R, T>) visitor).visit(this, arg); + } } @Override public String toString() { - return (graphConstructor != null) ? graphConstructor.toString() - : ((dataverse == null) ? name.getValue() : (dataverse + "." + name)); + if (lowerClause != null) { + return lowerClause.toString(); + + } else if (graphConstructor != null) { + return graphConstructor.toString(); + + } else if (dataverse != null && name != null) { + return dataverse + "." + name.getValue(); + + } else if (dataverse == null && name != null) { + return name.getValue(); + } + throw new IllegalStateException(); } @Override public int hashCode() { - return Objects.hash(graphConstructor, dataverse, name, matchClauses, correlateClauses); + return Objects.hash(graphConstructor, dataverse, name, matchClauses, correlateClauses, lowerClause); } @Override @@ -127,6 +181,7 @@ FromGraphClause that = (FromGraphClause) object; return Objects.equals(graphConstructor, that.graphConstructor) && Objects.equals(dataverse, that.dataverse) && Objects.equals(name, that.name) && matchClauses.equals(that.matchClauses) - && Objects.equals(correlateClauses, that.correlateClauses); + && Objects.equals(correlateClauses, that.correlateClauses) + && Objects.equals(lowerClause, that.lowerClause); } }
diff --git a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/clause/GraphSelectBlock.java b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/clause/GraphSelectBlock.java deleted file mode 100644 index 8871aaf..0000000 --- a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/clause/GraphSelectBlock.java +++ /dev/null
@@ -1,108 +0,0 @@ -/* - * 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.graphix.lang.clause; - -import java.util.List; -import java.util.Objects; - -import org.apache.asterix.common.exceptions.CompilationException; -import org.apache.asterix.graphix.lang.rewrites.visitor.IGraphixLangVisitor; -import org.apache.asterix.lang.common.base.AbstractClause; -import org.apache.asterix.lang.common.clause.GroupbyClause; -import org.apache.asterix.lang.common.visitor.base.ILangVisitor; -import org.apache.asterix.lang.sqlpp.clause.SelectBlock; -import org.apache.asterix.lang.sqlpp.clause.SelectClause; -import org.apache.asterix.lang.sqlpp.visitor.base.ISqlppVisitor; - -/** - * Starting AST node for a Graphix query, which will replace the FROM-CLAUSE with a {@link FromGraphClause} on - * parse. The goal of our Graphix rewriter is to replace these {@link FromGraphClause} nodes with applicable - * {@link org.apache.asterix.lang.sqlpp.clause.FromClause} nodes. - */ -public class GraphSelectBlock extends SelectBlock { - private FromGraphClause fromGraphClause; - - public GraphSelectBlock(SelectClause selectClause, FromGraphClause fromGraphClause, - List<AbstractClause> letWhereClauses, GroupbyClause groupbyClause, - List<AbstractClause> letHavingClausesAfterGby) { - super(selectClause, null, letWhereClauses, groupbyClause, letHavingClausesAfterGby); - this.fromGraphClause = fromGraphClause; - } - - public FromGraphClause getFromGraphClause() { - return fromGraphClause; - } - - public void setFromGraphClause(FromGraphClause fromGraphClause) { - this.fromGraphClause = fromGraphClause; - } - - public boolean hasFromGraphClause() { - return fromGraphClause != null; - } - - @Override - public <R, T> R accept(ILangVisitor<R, T> visitor, T arg) throws CompilationException { - if (hasFromClause()) { - return ((ISqlppVisitor<R, T>) visitor).visit(this, arg); - - } else { - return ((IGraphixLangVisitor<R, T>) visitor).visit(this, arg); - } - } - - @Override - public int hashCode() { - return Objects.hash(getFromClause(), getFromGraphClause(), getGroupbyClause(), getLetWhereList(), - getLetHavingListAfterGroupby(), getSelectClause()); - } - - @Override - public boolean equals(Object object) { - if (this == object) { - return true; - } - if (!(object instanceof GraphSelectBlock)) { - return false; - } - GraphSelectBlock target = (GraphSelectBlock) object; - return super.equals(target) && Objects.equals(getFromGraphClause(), target.getFromGraphClause()); - } - - @Override - public String toString() { - StringBuilder sb = new StringBuilder(); - sb.append(getSelectClause()); - if (hasFromClause()) { - sb.append(' ').append(getFromClause()); - } else if (hasFromGraphClause()) { - sb.append(' ').append(getFromGraphClause()); - } - if (hasLetWhereClauses()) { - sb.append(' ').append(getLetWhereList()); - } - if (hasGroupbyClause()) { - sb.append(' ').append(getGroupbyClause()); - } - if (hasLetHavingClausesAfterGroupby()) { - sb.append(' ').append(getLetHavingListAfterGroupby()); - } - return sb.toString(); - } -}
diff --git a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/clause/LowerListClause.java b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/clause/LowerListClause.java new file mode 100644 index 0000000..18e85c9 --- /dev/null +++ b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/clause/LowerListClause.java
@@ -0,0 +1,74 @@ +/* + * 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.graphix.lang.clause; + +import java.util.Objects; + +import org.apache.asterix.graphix.lang.clause.extension.LowerListClauseExtension; +import org.apache.asterix.graphix.lang.rewrite.lower.struct.ClauseCollection; +import org.apache.asterix.lang.common.base.AbstractExtensionClause; +import org.apache.asterix.lang.common.base.IVisitorExtension; +import org.apache.asterix.lang.common.clause.LetClause; +import org.apache.asterix.lang.sqlpp.clause.FromTerm; + +/** + * A functional equivalent to the {@link FromTerm}, used as a container for lowering a non-recursive portion of a + * {@link FromGraphClause}. We also maintain a list of {@link LetClause} nodes that bind vertex, edge, and path + * variables to expressions after the main linked list. + */ +public class LowerListClause extends AbstractExtensionClause { + private final LowerListClauseExtension lowerClauseExtension; + private final ClauseCollection clauseCollection; + + public LowerListClause(ClauseCollection clauseCollection) { + this.clauseCollection = clauseCollection; + this.lowerClauseExtension = new LowerListClauseExtension(this); + } + + public ClauseCollection getClauseCollection() { + return clauseCollection; + } + + @Override + public IVisitorExtension getVisitorExtension() { + return lowerClauseExtension; + } + + @Override + public int hashCode() { + return Objects.hash(clauseCollection.hashCode()); + } + + @Override + public boolean equals(Object object) { + if (this == object) { + return true; + } + if (!(object instanceof LowerListClause)) { + return false; + } + LowerListClause that = (LowerListClause) object; + return Objects.equals(this.clauseCollection, that.clauseCollection); + } + + @Override + public String toString() { + return clauseCollection.toString(); + } +}
diff --git a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/clause/LowerSwitchClause.java b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/clause/LowerSwitchClause.java new file mode 100644 index 0000000..99d8610 --- /dev/null +++ b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/clause/LowerSwitchClause.java
@@ -0,0 +1,238 @@ +/* + * 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.graphix.lang.clause; + +import java.util.Objects; + +import org.apache.asterix.graphix.algebra.compiler.option.SemanticsNavigationOption; +import org.apache.asterix.graphix.lang.clause.extension.LowerSwitchClauseExtension; +import org.apache.asterix.graphix.lang.rewrite.lower.action.MatchSemanticAction; +import org.apache.asterix.graphix.lang.rewrite.lower.struct.CollectionTable; +import org.apache.asterix.graphix.lang.struct.ElementLabel; +import org.apache.asterix.lang.common.base.AbstractExtensionClause; +import org.apache.asterix.lang.common.base.Expression; +import org.apache.asterix.lang.common.base.IVisitorExtension; +import org.apache.asterix.lang.common.expression.IndexAccessor; +import org.apache.asterix.lang.common.expression.LiteralExpr; +import org.apache.asterix.lang.common.expression.VariableExpr; +import org.apache.asterix.lang.common.literal.IntegerLiteral; + +/** + * A functional equivalent to the {@link org.apache.asterix.lang.sqlpp.clause.JoinClause}, used as a container for + * lowering a recursive / ambiguous portion of a {@link FromGraphClause}. + */ +public class LowerSwitchClause extends AbstractExtensionClause { + private final LowerSwitchClauseExtension lowerClauseExtension; + private final ClauseOutputEnvironment clauseOutputEnvironment; + private final ClauseInputEnvironment clauseInputEnvironment; + private final CollectionTable collectionLookupTable; + + /** + * The following is set by {@link MatchSemanticAction}. + */ + private SemanticsNavigationOption navigationSemantics; + + /** + * The output to a BFS clause will be of type list, containing three items. + */ + public static class ClauseOutputEnvironment { + public static final int OUTPUT_VERTEX_ITERATION_VARIABLE_INDEX = 0; + public static final int OUTPUT_VERTEX_JOIN_VARIABLE_INDEX = 1; + public static final int OUTPUT_PATH_VARIABLE_INDEX = 2; + private final VariableExpr outputVariable; + private final ElementLabel endingLabel; + + // We provide the following as output, through our output variable. + private final VariableExpr outputVertexIterationVariable; + private final VariableExpr outputVertexJoinVariable; + private final VariableExpr pathVariable; + + public ClauseOutputEnvironment(VariableExpr outputVariable, VariableExpr outputVertexIterationVariable, + VariableExpr outputVertexJoinVariable, VariableExpr pathVariable, ElementLabel endingLabel) { + this.outputVariable = Objects.requireNonNull(outputVariable); + this.outputVertexIterationVariable = Objects.requireNonNull(outputVertexIterationVariable); + this.outputVertexJoinVariable = Objects.requireNonNull(outputVertexJoinVariable); + this.pathVariable = Objects.requireNonNull(pathVariable); + this.endingLabel = endingLabel; + } + + public VariableExpr getOutputVariable() { + return outputVariable; + } + + public VariableExpr getOutputVertexIterationVariable() { + return outputVertexIterationVariable; + } + + public VariableExpr getOutputVertexJoinVariable() { + return outputVertexJoinVariable; + } + + public VariableExpr getPathVariable() { + return pathVariable; + } + + public ElementLabel getEndingLabel() { + return endingLabel; + } + + public Expression buildIterationVariableAccess() { + LiteralExpr indexExpr = new LiteralExpr(new IntegerLiteral(OUTPUT_VERTEX_ITERATION_VARIABLE_INDEX)); + return new IndexAccessor(outputVariable, IndexAccessor.IndexKind.ELEMENT, indexExpr); + } + + public Expression buildJoinVariableAccess() { + LiteralExpr indexExpr = new LiteralExpr(new IntegerLiteral(OUTPUT_VERTEX_JOIN_VARIABLE_INDEX)); + return new IndexAccessor(outputVariable, IndexAccessor.IndexKind.ELEMENT, indexExpr); + } + + public Expression buildPathVariableAccess() { + LiteralExpr indexExpr = new LiteralExpr(new IntegerLiteral(OUTPUT_PATH_VARIABLE_INDEX)); + return new IndexAccessor(outputVariable, IndexAccessor.IndexKind.ELEMENT, indexExpr); + } + + @Override + public int hashCode() { + return Objects.hash(outputVariable, outputVertexIterationVariable, outputVertexJoinVariable, pathVariable); + } + + @Override + public boolean equals(Object object) { + if (this == object) { + return true; + } + if (!(object instanceof ClauseOutputEnvironment)) { + return false; + } + ClauseOutputEnvironment that = (ClauseOutputEnvironment) object; + return Objects.equals(this.outputVariable, that.outputVariable) + && Objects.equals(this.outputVertexIterationVariable, that.outputVertexIterationVariable) + && Objects.equals(this.outputVertexJoinVariable, that.outputVertexJoinVariable) + && Objects.equals(this.pathVariable, that.pathVariable) + && Objects.equals(this.endingLabel, that.endingLabel); + } + + @Override + public String toString() { + String exposeToOutputString = "clause-output-env (" + outputVariable.toString() + ")"; + String iterationString = "iteration: " + outputVertexIterationVariable.toString(); + String joinString = "join: " + outputVertexJoinVariable.toString(); + String pathString = "path: " + pathVariable.toString(); + return String.format("%s: {%s, %s, %s}", exposeToOutputString, iterationString, joinString, pathString); + } + } + + /** + * The input to a BFS clause will be a single representative vertex. + */ + public static class ClauseInputEnvironment { + private final VariableExpr inputVariable; + private final ElementLabel startingLabel; + + public ClauseInputEnvironment(VariableExpr inputVariable, ElementLabel startingLabel) { + this.inputVariable = inputVariable; + this.startingLabel = startingLabel; + } + + public VariableExpr getInputVariable() { + return inputVariable; + } + + public ElementLabel getStartingLabel() { + return startingLabel; + } + + @Override + public int hashCode() { + return Objects.hash(inputVariable, startingLabel); + } + + @Override + public boolean equals(Object object) { + if (this == object) { + return true; + } + if (!(object instanceof ClauseInputEnvironment)) { + return false; + } + ClauseInputEnvironment that = (ClauseInputEnvironment) object; + return Objects.equals(this.inputVariable, that.inputVariable) + && Objects.equals(this.startingLabel, that.startingLabel); + } + } + + public LowerSwitchClause(CollectionTable pathClauseCollectionTable, ClauseInputEnvironment inputEnvironment, + ClauseOutputEnvironment outputEnvironment) { + this.collectionLookupTable = pathClauseCollectionTable; + this.clauseInputEnvironment = inputEnvironment; + this.clauseOutputEnvironment = outputEnvironment; + this.lowerClauseExtension = new LowerSwitchClauseExtension(this); + } + + public void setNavigationSemantics(SemanticsNavigationOption navigationSemantics) { + this.navigationSemantics = navigationSemantics; + } + + public SemanticsNavigationOption getNavigationSemantics() { + return navigationSemantics; + } + + public ClauseInputEnvironment getClauseInputEnvironment() { + return clauseInputEnvironment; + } + + public ClauseOutputEnvironment getClauseOutputEnvironment() { + return clauseOutputEnvironment; + } + + public CollectionTable getCollectionLookupTable() { + return collectionLookupTable; + } + + @Override + public IVisitorExtension getVisitorExtension() { + return lowerClauseExtension; + } + + @Override + public int hashCode() { + return Objects.hash(collectionLookupTable, clauseInputEnvironment, clauseOutputEnvironment, + navigationSemantics); + } + + @Override + public boolean equals(Object object) { + if (this == object) { + return true; + } + if (!(object instanceof LowerSwitchClause)) { + return false; + } + LowerSwitchClause that = (LowerSwitchClause) object; + return Objects.equals(this.collectionLookupTable, that.collectionLookupTable) + && Objects.equals(this.clauseOutputEnvironment, that.clauseOutputEnvironment) + && Objects.equals(this.clauseInputEnvironment, that.clauseInputEnvironment) + && Objects.equals(this.navigationSemantics, that.navigationSemantics); + } + + @Override + public String toString() { + return clauseOutputEnvironment.toString(); + } +}
diff --git a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/clause/MatchClause.java b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/clause/MatchClause.java index 11580e0..7823bcd 100644 --- a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/clause/MatchClause.java +++ b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/clause/MatchClause.java
@@ -25,17 +25,21 @@ import org.apache.asterix.common.exceptions.CompilationException; import org.apache.asterix.graphix.lang.expression.PathPatternExpr; import org.apache.asterix.graphix.lang.optype.MatchType; -import org.apache.asterix.graphix.lang.rewrites.visitor.IGraphixLangVisitor; +import org.apache.asterix.graphix.lang.rewrite.lower.action.MatchSemanticAction; +import org.apache.asterix.graphix.lang.visitor.base.IGraphixLangVisitor; import org.apache.asterix.lang.common.base.AbstractClause; import org.apache.asterix.lang.common.visitor.base.ILangVisitor; /** * Container for a collection of {@link PathPatternExpr} nodes. - * - A MATCH node has three types: LEADING (indicating that this node is first), INNER (indicating that this node is not - * first, but all patterns must be matched), and LEFTOUTER (indicating that this node is optionally matched). - * - Under isomorphism semantics, two patterns in different MATCH nodes (one pattern in a LEADING MATCH node and - * one pattern in an INNER MATCH node) are equivalent to two patterns in a single LEADING MATCH node. See - * {@link org.apache.asterix.graphix.lang.rewrites.lower.action.IsomorphismAction} for more detail. + * <ul> + * <li>A MATCH node has three types: LEADING (indicating that this node is first), INNER (indicating that this node + * is not first, but all patterns must be matched), and LEFTOUTER (indicating that this node is optionally + * matched).</li> + * <li>Under isomorphism semantics, two patterns in different MATCH nodes (one pattern in a LEADING MATCH node and + * one pattern in an INNER MATCH node) are equivalent to two patterns in a single LEADING MATCH node. See + * {@link MatchSemanticAction} for more detail</li> + * </ul> */ public class MatchClause extends AbstractClause { private final List<PathPatternExpr> pathExpressions; @@ -43,7 +47,7 @@ public MatchClause(List<PathPatternExpr> pathExpressions, MatchType matchType) { this.pathExpressions = Objects.requireNonNull(pathExpressions); - this.matchType = matchType; + this.matchType = Objects.requireNonNull(matchType); } public List<PathPatternExpr> getPathExpressions() { @@ -67,7 +71,6 @@ @Override public String toString() { String pathString = pathExpressions.stream().map(PathPatternExpr::toString).collect(Collectors.joining("\n")); - return matchType.toString() + " " + pathString; - + return matchType + " " + pathString; } }
diff --git a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/lower/transform/ISequenceTransformer.java b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/clause/extension/IGraphixVisitorExtension.java similarity index 73% copy from asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/lower/transform/ISequenceTransformer.java copy to asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/clause/extension/IGraphixVisitorExtension.java index 4509792..ddc6d5f 100644 --- a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/lower/transform/ISequenceTransformer.java +++ b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/clause/extension/IGraphixVisitorExtension.java
@@ -16,11 +16,15 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.asterix.graphix.lang.rewrites.lower.transform; +package org.apache.asterix.graphix.lang.clause.extension; -import org.apache.asterix.common.exceptions.CompilationException; +import org.apache.asterix.lang.common.base.IVisitorExtension; -@FunctionalInterface -public interface ISequenceTransformer { - void accept(CorrelatedClauseSequence clauseSequence) throws CompilationException; +public interface IGraphixVisitorExtension extends IVisitorExtension { + Kind getKind(); + + enum Kind { + LOWER_LIST, + LOWER_SWITCH + } }
diff --git a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/clause/extension/LowerListClauseExtension.java b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/clause/extension/LowerListClauseExtension.java new file mode 100644 index 0000000..caf8ff6 --- /dev/null +++ b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/clause/extension/LowerListClauseExtension.java
@@ -0,0 +1,339 @@ +/* + * 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.graphix.lang.clause.extension; + +import java.util.Collection; +import java.util.HashSet; +import java.util.Set; + +import org.apache.asterix.common.exceptions.CompilationException; +import org.apache.asterix.common.exceptions.ErrorCode; +import org.apache.asterix.graphix.lang.clause.FromGraphClause; +import org.apache.asterix.graphix.lang.clause.LowerListClause; +import org.apache.asterix.graphix.lang.clause.LowerSwitchClause; +import org.apache.asterix.graphix.lang.rewrite.lower.struct.ClauseCollection; +import org.apache.asterix.lang.common.base.AbstractClause; +import org.apache.asterix.lang.common.base.Clause; +import org.apache.asterix.lang.common.base.Expression; +import org.apache.asterix.lang.common.base.ILangExpression; +import org.apache.asterix.lang.common.base.IVisitorExtension; +import org.apache.asterix.lang.common.clause.LetClause; +import org.apache.asterix.lang.common.clause.WhereClause; +import org.apache.asterix.lang.common.expression.AbstractCallExpression; +import org.apache.asterix.lang.common.expression.VariableExpr; +import org.apache.asterix.lang.common.parser.ScopeChecker; +import org.apache.asterix.lang.common.rewrites.VariableSubstitutionEnvironment; +import org.apache.asterix.lang.common.visitor.base.ILangVisitor; +import org.apache.asterix.lang.sqlpp.clause.AbstractBinaryCorrelateClause; +import org.apache.asterix.lang.sqlpp.clause.JoinClause; +import org.apache.asterix.lang.sqlpp.clause.UnnestClause; +import org.apache.asterix.lang.sqlpp.util.SqlppVariableUtil; +import org.apache.asterix.lang.sqlpp.visitor.base.AbstractSqlppExpressionScopingVisitor; +import org.apache.hyracks.algebricks.common.utils.Pair; + +/** + * @see LowerListClause + */ +public class LowerListClauseExtension implements IGraphixVisitorExtension { + private final ClauseCollection clauseCollection; + private final LowerListClause lowerListClause; + + public LowerListClauseExtension(LowerListClause lowerListClause) { + this.clauseCollection = lowerListClause.getClauseCollection(); + this.lowerListClause = lowerListClause; + } + + public LowerListClause getLowerListClause() { + return lowerListClause; + } + + @Override + public Expression simpleExpressionDispatch(ILangVisitor<Expression, ILangExpression> simpleExpressionVisitor, + ILangExpression argument) throws CompilationException { + for (AbstractClause workingClause : clauseCollection) { + if (workingClause instanceof LowerSwitchClause) { + LowerSwitchClause lowerSwitchClause = (LowerSwitchClause) workingClause; + simpleExpressionVisitor.visit(lowerSwitchClause.getVisitorExtension(), argument); + + } else { + workingClause.accept(simpleExpressionVisitor, argument); + } + } + return null; + } + + @Override + public Void freeVariableDispatch(ILangVisitor<Void, Collection<VariableExpr>> freeVariableVisitor, + Collection<VariableExpr> freeVariables) throws CompilationException { + Collection<VariableExpr> bindingVariables = new HashSet<>(); + Collection<VariableExpr> clauseFreeVariables = new HashSet<>(); + for (AbstractClause workingClause : clauseCollection) { + clauseFreeVariables.clear(); + switch (workingClause.getClauseType()) { + case LET_CLAUSE: + LetClause letClause = (LetClause) workingClause; + letClause.getBindingExpr().accept(freeVariableVisitor, clauseFreeVariables); + clauseFreeVariables.removeAll(bindingVariables); + freeVariables.addAll(clauseFreeVariables); + bindingVariables.add(letClause.getVarExpr()); + break; + + case UNNEST_CLAUSE: + UnnestClause unnestClause = (UnnestClause) workingClause; + unnestClause.getRightExpression().accept(freeVariableVisitor, clauseFreeVariables); + clauseFreeVariables.removeAll(bindingVariables); + freeVariables.addAll(clauseFreeVariables); + bindingVariables.add(unnestClause.getRightVariable()); + if (unnestClause.hasPositionalVariable()) { + bindingVariables.add(unnestClause.getPositionalVariable()); + } + break; + + case JOIN_CLAUSE: + JoinClause joinClause = (JoinClause) workingClause; + joinClause.getRightExpression().accept(freeVariableVisitor, clauseFreeVariables); + clauseFreeVariables.removeAll(bindingVariables); + freeVariables.addAll(clauseFreeVariables); + + // Handle our condition expression, which can reference its right variable. + Collection<VariableExpr> conditionFreeVariables = new HashSet<>(); + joinClause.getConditionExpression().accept(freeVariableVisitor, conditionFreeVariables); + conditionFreeVariables.removeAll(bindingVariables); + conditionFreeVariables.remove(joinClause.getRightVariable()); + bindingVariables.add(joinClause.getRightVariable()); + if (joinClause.hasPositionalVariable()) { + conditionFreeVariables.remove(joinClause.getPositionalVariable()); + bindingVariables.add(joinClause.getPositionalVariable()); + } + freeVariables.addAll(conditionFreeVariables); + break; + + case WHERE_CLAUSE: + WhereClause whereClause = (WhereClause) workingClause; + whereClause.getWhereExpr().accept(freeVariableVisitor, clauseFreeVariables); + clauseFreeVariables.removeAll(bindingVariables); + freeVariables.addAll(clauseFreeVariables); + break; + + case EXTENSION: + if (workingClause instanceof LowerSwitchClause) { + LowerSwitchClause lowerSwitchClause = (LowerSwitchClause) workingClause; + IVisitorExtension visitorExtension = lowerSwitchClause.getVisitorExtension(); + visitorExtension.freeVariableDispatch(freeVariableVisitor, freeVariables); + break; + } + + default: + throw new CompilationException(ErrorCode.COMPILATION_ILLEGAL_STATE, + "Illegal clause found in the lower clause list!"); + } + } + return null; + } + + @Override + public Void bindingVariableDispatch(ILangVisitor<Void, Collection<VariableExpr>> bindingVariableVisitor, + Collection<VariableExpr> bindingVariables) { + for (AbstractClause workingClause : clauseCollection) { + switch (workingClause.getClauseType()) { + case LET_CLAUSE: + LetClause letClause = (LetClause) workingClause; + bindingVariables.add(letClause.getVarExpr()); + break; + + case UNNEST_CLAUSE: + case JOIN_CLAUSE: + AbstractBinaryCorrelateClause correlateClause = (AbstractBinaryCorrelateClause) workingClause; + bindingVariables.add(correlateClause.getRightVariable()); + break; + + case EXTENSION: + if (workingClause instanceof LowerSwitchClause) { + LowerSwitchClause lowerSwitchClause = (LowerSwitchClause) workingClause; + bindingVariables.add(lowerSwitchClause.getClauseOutputEnvironment().getOutputVariable()); + break; + } + } + } + return null; + } + + @Override + public Expression variableScopeDispatch(ILangVisitor<Expression, ILangExpression> scopingVisitor, + ILangExpression argument, ScopeChecker scopeChecker) throws CompilationException { + for (AbstractClause workingClause : clauseCollection) { + if (workingClause.getClauseType() == Clause.ClauseType.LET_CLAUSE) { + // We do not extend the scope for our LET-CLAUSE nodes. + LetClause letClause = (LetClause) workingClause; + letClause.setBindingExpr(letClause.getBindingExpr().accept(scopingVisitor, letClause)); + VariableExpr letClauseVariable = letClause.getVarExpr(); + if (scopeChecker.getCurrentScope().findLocalSymbol(letClauseVariable.getVar().getValue()) != null) { + String varName = SqlppVariableUtil.toUserDefinedName(letClauseVariable.getVar().getValue()); + throw new CompilationException(ErrorCode.COMPILATION_ERROR, letClauseVariable.getSourceLocation(), + "Duplicate alias definitions: " + varName); + } + scopeChecker.getCurrentScope().addNewVarSymbolToScope(letClauseVariable.getVar(), + Set.of(AbstractSqlppExpressionScopingVisitor.SqlppVariableAnnotation.CONTEXT_VARIABLE)); + + } else if (workingClause instanceof LowerSwitchClause) { + LowerSwitchClause lowerSwitchClause = (LowerSwitchClause) workingClause; + IVisitorExtension visitorExtension = lowerSwitchClause.getVisitorExtension(); + visitorExtension.variableScopeDispatch(scopingVisitor, argument, scopeChecker); + + } else { + workingClause.accept(scopingVisitor, argument); + } + } + return null; + } + + @Override + public ILangExpression deepCopyDispatch(ILangVisitor<ILangExpression, Void> deepCopyVisitor) + throws CompilationException { + ClauseCollection copyCollection = new ClauseCollection(clauseCollection.getSourceLocation()); + for (AbstractClause clause : clauseCollection.getNonRepresentativeClauses()) { + if (clause instanceof LowerSwitchClause) { + LowerSwitchClause lowerSwitchClause = (LowerSwitchClause) clause; + IVisitorExtension visitorExtension = lowerSwitchClause.getVisitorExtension(); + AbstractClause copiedClause = (AbstractClause) visitorExtension.deepCopyDispatch(deepCopyVisitor); + copyCollection.addNonRepresentativeClause(copiedClause); + + } else { + copyCollection.addNonRepresentativeClause((AbstractClause) clause.accept(deepCopyVisitor, null)); + } + } + for (LetClause clause : clauseCollection.getRepresentativeVertexBindings()) { + LetClause copiedClause = (LetClause) clause.accept(deepCopyVisitor, null); + copyCollection.addVertexBinding(copiedClause.getVarExpr(), copiedClause.getBindingExpr()); + } + for (LetClause clause : clauseCollection.getRepresentativeEdgeBindings()) { + LetClause copiedClause = (LetClause) clause.accept(deepCopyVisitor, null); + copyCollection.addEdgeBinding(copiedClause.getVarExpr(), copiedClause.getBindingExpr()); + } + for (LetClause clause : clauseCollection.getRepresentativePathBindings()) { + LetClause copiedClause = (LetClause) clause.accept(deepCopyVisitor, null); + copyCollection.addPathBinding(copiedClause.getVarExpr(), copiedClause.getBindingExpr()); + } + for (AbstractBinaryCorrelateClause clause : clauseCollection.getUserDefinedCorrelateClauses()) { + AbstractBinaryCorrelateClause copiedClause = + (AbstractBinaryCorrelateClause) clause.accept(deepCopyVisitor, null); + copyCollection.addUserDefinedCorrelateClause(copiedClause); + } + + // Note: a LOWER-LIST-CLAUSE is also the entry-point for a FROM-GRAPH-CLAUSE, so we return the latter. + LowerListClause copyListClause = new LowerListClause(copyCollection); + copyListClause.setSourceLocation(lowerListClause.getSourceLocation()); + FromGraphClause fromGraphClause = new FromGraphClause(copyListClause); + fromGraphClause.setSourceLocation(lowerListClause.getSourceLocation()); + return fromGraphClause; + } + + @Override + public Pair<ILangExpression, VariableSubstitutionEnvironment> remapCloneDispatch( + ILangVisitor<Pair<ILangExpression, VariableSubstitutionEnvironment>, VariableSubstitutionEnvironment> remapCloneVisitor, + VariableSubstitutionEnvironment substitutionEnvironment) { + // TODO (GLENN): Finish the remap-clone dispatch. + return null; + } + + @Override + public Boolean inlineUDFsDispatch(ILangVisitor<Boolean, Void> inlineUDFsVisitor) throws CompilationException { + boolean changed = false; + for (AbstractClause workingClause : clauseCollection) { + if (workingClause instanceof LowerSwitchClause) { + LowerSwitchClause lowerSwitchClause = (LowerSwitchClause) workingClause; + IVisitorExtension visitorExtension = lowerSwitchClause.getVisitorExtension(); + changed |= visitorExtension.inlineUDFsDispatch(inlineUDFsVisitor); + + } else { + changed |= workingClause.accept(inlineUDFsVisitor, null); + } + } + return changed; + } + + @Override + public Void gatherFunctionsDispatch(ILangVisitor<Void, Void> gatherFunctionsVisitor, + Collection<? super AbstractCallExpression> functionCalls) throws CompilationException { + for (AbstractClause workingClause : clauseCollection) { + if (workingClause instanceof LowerSwitchClause) { + LowerSwitchClause lowerSwitchClause = (LowerSwitchClause) workingClause; + IVisitorExtension visitorExtension = lowerSwitchClause.getVisitorExtension(); + visitorExtension.gatherFunctionsDispatch(gatherFunctionsVisitor, functionCalls); + + } else { + workingClause.accept(gatherFunctionsVisitor, null); + } + } + return null; + } + + @Override + public Boolean checkSubqueryDispatch(ILangVisitor<Boolean, ILangExpression> checkSubqueryVisitor, + ILangExpression argument) throws CompilationException { + for (AbstractClause workingClause : clauseCollection) { + if (workingClause instanceof LowerSwitchClause) { + LowerSwitchClause lowerSwitchClause = (LowerSwitchClause) workingClause; + IVisitorExtension visitorExtension = lowerSwitchClause.getVisitorExtension(); + if (visitorExtension.checkSubqueryDispatch(checkSubqueryVisitor, argument)) { + return true; + } + + } else if (workingClause.accept(checkSubqueryVisitor, null)) { + return true; + } + } + return false; + } + + @Override + public Boolean check92AggregateDispatch(ILangVisitor<Boolean, ILangExpression> check92AggregateVisitor, + ILangExpression argument) { + return false; + } + + @Override + public Boolean checkNonFunctionalDispatch(ILangVisitor<Boolean, Void> checkNonFunctionalVisitor) + throws CompilationException { + for (AbstractClause workingClause : clauseCollection) { + if (workingClause instanceof LowerSwitchClause) { + LowerSwitchClause lowerSwitchClause = (LowerSwitchClause) workingClause; + IVisitorExtension visitorExtension = lowerSwitchClause.getVisitorExtension(); + if (visitorExtension.checkNonFunctionalDispatch(checkNonFunctionalVisitor)) { + return true; + } + + } else if (workingClause.accept(checkNonFunctionalVisitor, null)) { + return true; + } + } + return false; + } + + @Override + public Boolean checkDatasetOnlyDispatch(ILangVisitor<Boolean, VariableExpr> checkDatasetOnlyVisitor, + VariableExpr datasetCandidate) { + return false; + } + + @Override + public Kind getKind() { + return Kind.LOWER_LIST; + } +}
diff --git a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/clause/extension/LowerSwitchClauseExtension.java b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/clause/extension/LowerSwitchClauseExtension.java new file mode 100644 index 0000000..436933d --- /dev/null +++ b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/clause/extension/LowerSwitchClauseExtension.java
@@ -0,0 +1,252 @@ +/* + * 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.graphix.lang.clause.extension; + +import java.util.Collection; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Set; + +import org.apache.asterix.common.exceptions.CompilationException; +import org.apache.asterix.common.exceptions.ErrorCode; +import org.apache.asterix.graphix.lang.clause.FromGraphClause; +import org.apache.asterix.graphix.lang.clause.LowerListClause; +import org.apache.asterix.graphix.lang.clause.LowerSwitchClause; +import org.apache.asterix.graphix.lang.rewrite.lower.struct.ClauseCollection; +import org.apache.asterix.graphix.lang.rewrite.lower.struct.CollectionTable; +import org.apache.asterix.graphix.lang.rewrite.lower.struct.StateContainer; +import org.apache.asterix.graphix.lang.struct.ElementLabel; +import org.apache.asterix.lang.common.base.AbstractClause; +import org.apache.asterix.lang.common.base.Expression; +import org.apache.asterix.lang.common.base.ILangExpression; +import org.apache.asterix.lang.common.context.Scope; +import org.apache.asterix.lang.common.expression.AbstractCallExpression; +import org.apache.asterix.lang.common.expression.VariableExpr; +import org.apache.asterix.lang.common.parser.ScopeChecker; +import org.apache.asterix.lang.common.rewrites.VariableSubstitutionEnvironment; +import org.apache.asterix.lang.common.struct.VarIdentifier; +import org.apache.asterix.lang.common.visitor.base.ILangVisitor; +import org.apache.asterix.lang.sqlpp.util.SqlppVariableUtil; +import org.apache.hyracks.algebricks.common.utils.Pair; + +/** + * @see LowerSwitchClause + */ +public class LowerSwitchClauseExtension implements IGraphixVisitorExtension { + private final CollectionTable collectionLookupTable; + private final LowerSwitchClause lowerSwitchClause; + + public LowerSwitchClauseExtension(LowerSwitchClause lowerSwitchClause) { + this.collectionLookupTable = lowerSwitchClause.getCollectionLookupTable(); + this.lowerSwitchClause = lowerSwitchClause; + } + + public LowerSwitchClause getLowerSwitchClause() { + return lowerSwitchClause; + } + + @Override + public Expression simpleExpressionDispatch(ILangVisitor<Expression, ILangExpression> simpleExpressionVisitor, + ILangExpression argument) throws CompilationException { + for (ClauseCollection clauseCollection : collectionLookupTable) { + for (AbstractClause workingClause : clauseCollection) { + workingClause.accept(simpleExpressionVisitor, argument); + } + } + return null; + } + + @Override + public Void freeVariableDispatch(ILangVisitor<Void, Collection<VariableExpr>> freeVariableVisitor, + Collection<VariableExpr> freeVariables) throws CompilationException { + Iterator<Pair<ElementLabel, List<CollectionTable.Entry>>> entryIterator = collectionLookupTable.entryIterator(); + while (entryIterator.hasNext()) { + Pair<ElementLabel, List<CollectionTable.Entry>> tableEntry = entryIterator.next(); + StateContainer inputState = collectionLookupTable.getInputMap().get(tableEntry.first); + for (CollectionTable.Entry entry : tableEntry.second) { + ClauseCollection clauseCollection = entry.getClauseCollection(); + LowerListClause lowerListClause = new LowerListClause(clauseCollection); + LowerListClauseExtension lowerListClauseExtension = new LowerListClauseExtension(lowerListClause); + + // The input variables to each branch are **not** free (in the context of this visitor). + Set<VariableExpr> clauseCollectionFreeVars = new HashSet<>(); + lowerListClauseExtension.freeVariableDispatch(freeVariableVisitor, clauseCollectionFreeVars); + clauseCollectionFreeVars.removeIf(v -> { + final VariableExpr inputJoinVariable = inputState.getJoinVariable(); + final VariableExpr inputIterationVariable = inputState.getIterationVariable(); + return v.equals(inputJoinVariable) || v.equals(inputIterationVariable); + }); + } + } + return null; + } + + @Override + public Void bindingVariableDispatch(ILangVisitor<Void, Collection<VariableExpr>> bindingVariableVisitor, + Collection<VariableExpr> bindingVariables) throws CompilationException { + throw new CompilationException(ErrorCode.COMPILATION_ILLEGAL_STATE, lowerSwitchClause.getSourceLocation(), + "Binding variable dispatch invoked for LOWER-SWITCH-CLAUSE!"); + } + + @Override + public Expression variableScopeDispatch(ILangVisitor<Expression, ILangExpression> scopingVisitor, + ILangExpression argument, ScopeChecker scopeChecker) throws CompilationException { + // Traverse our branches. + Iterator<Pair<ElementLabel, List<CollectionTable.Entry>>> entryIterator = collectionLookupTable.entryIterator(); + while (entryIterator.hasNext()) { + Pair<ElementLabel, List<CollectionTable.Entry>> tableEntry = entryIterator.next(); + StateContainer inputState = collectionLookupTable.getInputMap().get(tableEntry.first); + for (CollectionTable.Entry entry : tableEntry.second) { + ClauseCollection clauseCollection = entry.getClauseCollection(); + LowerListClause lowerListClause = new LowerListClause(clauseCollection); + LowerListClauseExtension lowerListClauseExtension = new LowerListClauseExtension(lowerListClause); + + // Our input variables should only be visible to each branch. + Scope newScope = scopeChecker.createNewScope(); + addVariableToScope(newScope, inputState.getJoinVariable().getVar()); + addVariableToScope(newScope, inputState.getIterationVariable().getVar()); + lowerListClauseExtension.variableScopeDispatch(scopingVisitor, argument, scopeChecker); + scopeChecker.removeCurrentScope(); + } + } + + // Introduce our output variable into scope. + LowerSwitchClause.ClauseOutputEnvironment outputEnv = lowerSwitchClause.getClauseOutputEnvironment(); + addVariableToScope(scopeChecker.getCurrentScope(), outputEnv.getOutputVariable().getVar()); + return null; + } + + @Override + public ILangExpression deepCopyDispatch(ILangVisitor<ILangExpression, Void> deepCopyVisitor) + throws CompilationException { + CollectionTable copyTable = new CollectionTable(); + Iterator<Pair<ElementLabel, List<CollectionTable.Entry>>> entryIterator = collectionLookupTable.entryIterator(); + while (entryIterator.hasNext()) { + Pair<ElementLabel, List<CollectionTable.Entry>> tableEntry = entryIterator.next(); + for (CollectionTable.Entry entry : tableEntry.second) { + ClauseCollection clauseCollection = entry.getClauseCollection(); + LowerListClauseExtension llce = new LowerListClauseExtension(new LowerListClause(clauseCollection)); + FromGraphClause fromGraphClause = (FromGraphClause) llce.deepCopyDispatch(deepCopyVisitor); + LowerListClause lowerClauseCopy = (LowerListClause) fromGraphClause.getLowerClause(); + copyTable.putCollection(tableEntry.first, entry.getEdgeLabel(), entry.getDestinationLabel(), + lowerClauseCopy.getClauseCollection(), entry.getEdgeDirection()); + } + } + copyTable.setInputMap(collectionLookupTable.getInputMap()); + copyTable.setOutputMap(collectionLookupTable.getOutputMap()); + LowerSwitchClause.ClauseOutputEnvironment outputEnv = lowerSwitchClause.getClauseOutputEnvironment(); + VariableExpr outputVariableExpr = outputEnv.getOutputVariable(); + VariableExpr copyOutputVariableExpr = (VariableExpr) outputVariableExpr.accept(deepCopyVisitor, null); + LowerSwitchClause.ClauseOutputEnvironment copyOutputEnv = new LowerSwitchClause.ClauseOutputEnvironment( + copyOutputVariableExpr, outputEnv.getOutputVertexIterationVariable(), + outputEnv.getOutputVertexJoinVariable(), outputEnv.getPathVariable(), outputEnv.getEndingLabel()); + LowerSwitchClause.ClauseInputEnvironment inputEnv = lowerSwitchClause.getClauseInputEnvironment(); + VariableExpr inputVariableExpr = inputEnv.getInputVariable(); + VariableExpr copyInputVariableExpr = (VariableExpr) inputVariableExpr.accept(deepCopyVisitor, null); + LowerSwitchClause.ClauseInputEnvironment copyInputEnv = + new LowerSwitchClause.ClauseInputEnvironment(copyInputVariableExpr, inputEnv.getStartingLabel()); + LowerSwitchClause copyLowerSwitchClause = new LowerSwitchClause(copyTable, copyInputEnv, copyOutputEnv); + copyLowerSwitchClause.setNavigationSemantics(lowerSwitchClause.getNavigationSemantics()); + copyLowerSwitchClause.setSourceLocation(lowerSwitchClause.getSourceLocation()); + return copyLowerSwitchClause; + } + + @Override + public Pair<ILangExpression, VariableSubstitutionEnvironment> remapCloneDispatch( + ILangVisitor<Pair<ILangExpression, VariableSubstitutionEnvironment>, VariableSubstitutionEnvironment> remapCloneVisitor, + VariableSubstitutionEnvironment substitutionEnvironment) { + // TODO (GLENN): Finish the remap-clone dispatch. + return null; + } + + @Override + public Boolean inlineUDFsDispatch(ILangVisitor<Boolean, Void> inlineUDFsVisitor) throws CompilationException { + boolean changed = false; + for (ClauseCollection clauseCollection : collectionLookupTable) { + for (AbstractClause workingClause : clauseCollection) { + changed |= workingClause.accept(inlineUDFsVisitor, null); + } + } + return changed; + } + + @Override + public Void gatherFunctionsDispatch(ILangVisitor<Void, Void> gatherFunctionsVisitor, + Collection<? super AbstractCallExpression> functionCalls) throws CompilationException { + for (ClauseCollection clauseCollection : collectionLookupTable) { + for (AbstractClause workingClause : clauseCollection) { + workingClause.accept(gatherFunctionsVisitor, null); + } + } + return null; + } + + @Override + public Boolean checkSubqueryDispatch(ILangVisitor<Boolean, ILangExpression> checkSubqueryVisitor, + ILangExpression argument) throws CompilationException { + for (ClauseCollection clauseCollection : collectionLookupTable) { + for (AbstractClause workingClause : clauseCollection) { + if (workingClause.accept(checkSubqueryVisitor, null)) { + return true; + } + } + } + return false; + } + + @Override + public Boolean check92AggregateDispatch(ILangVisitor<Boolean, ILangExpression> check92AggregateVisitor, + ILangExpression argument) { + return false; + } + + @Override + public Boolean checkNonFunctionalDispatch(ILangVisitor<Boolean, Void> checkNonFunctionalVisitor) + throws CompilationException { + for (ClauseCollection clauseCollection : collectionLookupTable) { + for (AbstractClause workingClause : clauseCollection) { + if (workingClause.accept(checkNonFunctionalVisitor, null)) { + return true; + } + } + } + return false; + } + + @Override + public Boolean checkDatasetOnlyDispatch(ILangVisitor<Boolean, VariableExpr> checkDatasetOnlyVisitor, + VariableExpr datasetCandidate) { + return false; + } + + @Override + public Kind getKind() { + return Kind.LOWER_SWITCH; + } + + private void addVariableToScope(Scope scope, VarIdentifier varIdentifier) throws CompilationException { + if (scope.findLocalSymbol(varIdentifier.getValue()) != null) { + String varName = SqlppVariableUtil.toUserDefinedName(varIdentifier.getValue()); + throw new CompilationException(ErrorCode.COMPILATION_ERROR, lowerSwitchClause.getSourceLocation(), + "Duplicate alias definitions: " + varName); + } + scope.addNewVarSymbolToScope(varIdentifier, Set.of()); + } +}
diff --git a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/expression/EdgePatternExpr.java b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/expression/EdgePatternExpr.java index 666141b..03d3bce 100644 --- a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/expression/EdgePatternExpr.java +++ b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/expression/EdgePatternExpr.java
@@ -24,33 +24,39 @@ import java.util.Objects; import org.apache.asterix.common.exceptions.CompilationException; -import org.apache.asterix.graphix.lang.rewrites.visitor.IGraphixLangVisitor; +import org.apache.asterix.graphix.common.metadata.EdgeIdentifier; +import org.apache.asterix.graphix.common.metadata.GraphIdentifier; import org.apache.asterix.graphix.lang.struct.EdgeDescriptor; +import org.apache.asterix.graphix.lang.struct.ElementLabel; +import org.apache.asterix.graphix.lang.visitor.base.IGraphixLangVisitor; import org.apache.asterix.lang.common.base.AbstractExpression; import org.apache.asterix.lang.common.visitor.base.ILangVisitor; /** - * A query edge (not to be confused with an edge constructor) is composed of a {@link EdgeDescriptor} (containing the - * edge labels, an optional edge variable, and the hop range), an list of optional internal {@link VertexPatternExpr} - * instances, a left {@link VertexPatternExpr}, and a right {@link VertexPatternExpr}. + * A query edge (not to be confused with an edge constructor) is composed of: + * <ul> + * <li>A {@link EdgeDescriptor} (containing the edge labels, an optional edge variable, and the hop range).</li> + * <li>An optional internal {@link VertexPatternExpr}.</li> + * <li>A left {@link VertexPatternExpr}.</li> + * <li>A right {@link VertexPatternExpr}.</li> + * </ul> */ public class EdgePatternExpr extends AbstractExpression { - private final List<VertexPatternExpr> internalVertices; private final EdgeDescriptor edgeDescriptor; private VertexPatternExpr leftVertex; private VertexPatternExpr rightVertex; + private VertexPatternExpr internalVertex; public EdgePatternExpr(VertexPatternExpr leftVertex, VertexPatternExpr rightVertex, EdgeDescriptor edgeDescriptor) { this.leftVertex = Objects.requireNonNull(leftVertex); this.rightVertex = Objects.requireNonNull(rightVertex); this.edgeDescriptor = Objects.requireNonNull(edgeDescriptor); - this.internalVertices = new ArrayList<>(); - if (edgeDescriptor.getPatternType() == EdgeDescriptor.PatternType.PATH) { // If we have a sub-path, we have an internal vertex that we need to manage. - for (int i = 0; i < edgeDescriptor.getMaximumHops() - 1; i++) { - this.internalVertices.add(new VertexPatternExpr(null, new HashSet<>())); - } + this.internalVertex = new VertexPatternExpr(null, null, new HashSet<>()); + + } else { + this.internalVertex = null; } } @@ -62,12 +68,12 @@ return rightVertex; } - public EdgeDescriptor getEdgeDescriptor() { - return edgeDescriptor; + public VertexPatternExpr getInternalVertex() { + return internalVertex; } - public List<VertexPatternExpr> getInternalVertices() { - return internalVertices; + public EdgeDescriptor getEdgeDescriptor() { + return edgeDescriptor; } public void setLeftVertex(VertexPatternExpr leftVertex) { @@ -78,14 +84,30 @@ this.rightVertex = rightVertex; } - public void replaceInternalVertices(List<VertexPatternExpr> internalVertices) { - this.internalVertices.clear(); - this.internalVertices.addAll(internalVertices); + public void setInternalVertex(VertexPatternExpr internalVertex) { + this.internalVertex = internalVertex; + } + + public List<EdgeIdentifier> generateIdentifiers(GraphIdentifier graphIdentifier) { + List<EdgeIdentifier> edgeIdentifiers = new ArrayList<>(); + for (ElementLabel leftLabel : leftVertex.getLabels()) { + for (ElementLabel rightLabel : rightVertex.getLabels()) { + for (ElementLabel edgeLabel : edgeDescriptor.getEdgeLabels()) { + if (edgeDescriptor.getEdgeDirection() != EdgeDescriptor.EdgeDirection.RIGHT_TO_LEFT) { + edgeIdentifiers.add(new EdgeIdentifier(graphIdentifier, leftLabel, edgeLabel, rightLabel)); + } + if (edgeDescriptor.getEdgeDirection() != EdgeDescriptor.EdgeDirection.LEFT_TO_RIGHT) { + edgeIdentifiers.add(new EdgeIdentifier(graphIdentifier, rightLabel, edgeLabel, leftLabel)); + } + } + } + } + return edgeIdentifiers; } @Override public int hashCode() { - return Objects.hash(leftVertex, rightVertex, edgeDescriptor, internalVertices); + return Objects.hash(leftVertex, rightVertex, internalVertex, edgeDescriptor); } @Override @@ -98,8 +120,8 @@ } EdgePatternExpr that = (EdgePatternExpr) object; return Objects.equals(this.leftVertex, that.leftVertex) && Objects.equals(this.rightVertex, that.rightVertex) - && Objects.equals(this.edgeDescriptor, that.edgeDescriptor) - && Objects.equals(this.internalVertices, that.internalVertices); + && Objects.equals(this.internalVertex, that.internalVertex) + && Objects.equals(this.edgeDescriptor, that.edgeDescriptor); } @Override
diff --git a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/expression/GraphConstructor.java b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/expression/GraphConstructor.java index 245be24..e5b10eb 100644 --- a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/expression/GraphConstructor.java +++ b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/expression/GraphConstructor.java
@@ -23,16 +23,19 @@ import java.util.UUID; import org.apache.asterix.common.exceptions.CompilationException; -import org.apache.asterix.graphix.lang.rewrites.visitor.IGraphixLangVisitor; import org.apache.asterix.graphix.lang.struct.ElementLabel; +import org.apache.asterix.graphix.lang.visitor.base.IGraphixLangVisitor; import org.apache.asterix.lang.common.base.AbstractExpression; import org.apache.asterix.lang.common.base.AbstractLangExpression; import org.apache.asterix.lang.common.base.Expression; import org.apache.asterix.lang.common.visitor.base.ILangVisitor; /** - * An expression which describes the schema of a graph, containing a list of vertices ({@link VertexConstructor}) and - * a list of edges ({@link EdgeConstructor}) that connect the aforementioned vertices. + * An expression which describes the schema of a graph, containing: + * <ul> + * <li>A list of vertices ({@link VertexConstructor}).</li> + * <li>A list of edges ({@link EdgeConstructor}) that connect the aforementioned vertices.</li> + * </ul> */ public class GraphConstructor extends AbstractExpression { private final List<VertexConstructor> vertexConstructors; @@ -88,9 +91,11 @@ /** * A vertex constructor (not be confused with a query vertex) is composed of the following: - * - An AST containing the vertex body expression, as well as the raw body string itself. - * - A single vertex label that uniquely identifies the vertex. - * - A list of primary key fields, used in the JOIN clause with edges. + * <ul> + * <li>An AST containing the vertex body expression, as well as the raw body string itself.</li> + * <li>A single vertex label that uniquely identifies the vertex.</li> + * <li>A list of primary key fields, used in the JOIN clause with edges.</li> + * </ul> */ public static class VertexConstructor extends AbstractLangExpression { private final List<Integer> primaryKeySourceIndicators; @@ -160,12 +165,14 @@ /** * An edge constructor (not be confused with a query edge) is composed of the following: - * - An AST containing the edge body expression, as well as the raw body string itself. - * - A single edge label that uniquely identifies the edge. - * - A single label that denotes the source vertices of this edge, as well as another label that denotes the - * destination vertices of this edge. - * - A list of source key fields, used in the JOIN clause with the corresponding source vertices. - * - A list of destination key fields, used in the JOIN clause with the corresponding destination vertices. + * <ul> + * <li>An AST containing the edge body expression, as well as the raw body string itself.</li> + * <li>A single edge label that uniquely identifies the edge.</li> + * <li>A single label that denotes the source vertices of this edge, as well as another label that denotes the + * destination vertices of this edge.</li> + * <li>A list of source key fields, used in the JOIN clause with the corresponding source vertices.</li> + * <li>A list of destination key fields, used in the JOIN clause with the corresponding destination vertices.</li> + * </ul> */ public static class EdgeConstructor extends AbstractLangExpression { private final List<Integer> destinationKeySourceIndicators;
diff --git a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/expression/PathPatternExpr.java b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/expression/PathPatternExpr.java index 1057731..2e1e7c2 100644 --- a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/expression/PathPatternExpr.java +++ b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/expression/PathPatternExpr.java
@@ -21,18 +21,23 @@ import java.util.ArrayList; import java.util.List; import java.util.Objects; +import java.util.stream.Collectors; import org.apache.asterix.common.exceptions.CompilationException; -import org.apache.asterix.graphix.lang.rewrites.visitor.IGraphixLangVisitor; +import org.apache.asterix.graphix.lang.visitor.base.IGraphixLangVisitor; import org.apache.asterix.lang.common.base.AbstractExpression; import org.apache.asterix.lang.common.clause.LetClause; import org.apache.asterix.lang.common.expression.VariableExpr; import org.apache.asterix.lang.common.visitor.base.ILangVisitor; /** - * A path is composed of a list of {@link VertexPatternExpr} instances and a list of {@link EdgePatternExpr} that - * utilize the aforementioned vertices. Users can also optionally specify a variable, and attach {@link LetClause} nodes - * to aid in lowering this expression (i.e. for lowering sub-paths). + * A path is composed of: + * <ul> + * <li>A list of {@link VertexPatternExpr} instances.</li> + * <li>A list of {@link EdgePatternExpr} instances that utilize the aforementioned vertices.</li> + * <li>An optional variable binding all vertices and edges to a path record.</li> + * <li>A list of {@link LetClause} nodes that represent expanded sub-paths.</li> + * </ul> */ public class PathPatternExpr extends AbstractExpression { private final List<LetClause> reboundSubPathExpressions; @@ -79,4 +84,13 @@ public <R, T> R accept(ILangVisitor<R, T> visitor, T arg) throws CompilationException { return ((IGraphixLangVisitor<R, T>) visitor).visit(this, arg); } + + @Override + public String toString() { + String edgeString = edgeExpressions.stream().map(EdgePatternExpr::toString).collect(Collectors.joining(",")); + String variableString = (variableExpr != null) ? (" AS " + variableExpr) : ""; + return String.format("%s%s%s", + vertexExpressions.stream().map(VertexPatternExpr::toString).collect(Collectors.joining(",")), + (edgeString.equals("") ? "" : ", " + edgeString), variableString); + } }
diff --git a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/expression/VertexPatternExpr.java b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/expression/VertexPatternExpr.java index 54cbfa4..0897eb6 100644 --- a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/expression/VertexPatternExpr.java +++ b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/expression/VertexPatternExpr.java
@@ -24,24 +24,31 @@ import java.util.stream.Collectors; import org.apache.asterix.common.exceptions.CompilationException; -import org.apache.asterix.graphix.common.metadata.GraphElementIdentifier; import org.apache.asterix.graphix.common.metadata.GraphIdentifier; -import org.apache.asterix.graphix.lang.rewrites.visitor.IGraphixLangVisitor; +import org.apache.asterix.graphix.common.metadata.VertexIdentifier; import org.apache.asterix.graphix.lang.struct.ElementLabel; +import org.apache.asterix.graphix.lang.visitor.base.IGraphixLangVisitor; import org.apache.asterix.lang.common.base.AbstractExpression; +import org.apache.asterix.lang.common.base.Expression; import org.apache.asterix.lang.common.expression.VariableExpr; import org.apache.asterix.lang.common.visitor.base.ILangVisitor; /** - * A query vertex (not to be confused with a vertex constructor) is composed of a set of labels (which may be empty) - * and a variable (which may initially be null). + * A query vertex (not to be confused with a vertex constructor) is composed of: + * <ul> + * <li>A set of labels (which may be empty).</li> + * <li>A variable (which may initially be null).</li> + * <li>A filter expression (which may be null).</li> + * </ul> */ public class VertexPatternExpr extends AbstractExpression { private final Set<ElementLabel> labels; + private final Expression filterExpr; private VariableExpr variableExpr; - public VertexPatternExpr(VariableExpr variableExpr, Set<ElementLabel> labels) { + public VertexPatternExpr(VariableExpr variableExpr, Expression filterExpr, Set<ElementLabel> labels) { this.variableExpr = variableExpr; + this.filterExpr = filterExpr; this.labels = labels; } @@ -49,6 +56,10 @@ return labels; } + public Expression getFilterExpr() { + return filterExpr; + } + public VariableExpr getVariableExpr() { return variableExpr; } @@ -57,10 +68,8 @@ this.variableExpr = variableExpr; } - public List<GraphElementIdentifier> generateIdentifiers(GraphIdentifier graphIdentifier) { - return labels.stream() - .map(v -> new GraphElementIdentifier(graphIdentifier, GraphElementIdentifier.Kind.VERTEX, v)) - .collect(Collectors.toList()); + public List<VertexIdentifier> generateIdentifiers(GraphIdentifier graphIdentifier) { + return labels.stream().map(v -> new VertexIdentifier(graphIdentifier, v)).collect(Collectors.toList()); } @Override @@ -77,14 +86,16 @@ return false; } VertexPatternExpr that = (VertexPatternExpr) object; - return Objects.equals(this.labels, that.labels) && Objects.equals(this.variableExpr, that.variableExpr); + return Objects.equals(this.labels, that.labels) && Objects.equals(this.variableExpr, that.variableExpr) + && Objects.equals(this.filterExpr, that.filterExpr); } @Override public String toString() { String labelsString = labels.stream().map(ElementLabel::toString).collect(Collectors.joining("|")); String variableString = (variableExpr != null) ? variableExpr.getVar().toString() : ""; - return String.format("(%s:%s)", variableString, labelsString); + String filterString = (filterExpr != null) ? (" WHERE " + filterExpr + " ") : ""; + return String.format("(%s:%s%s)", variableString, labelsString, filterString); } @Override
diff --git a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/parser/GraphixParserHint.java b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/parser/GraphixParserHint.java new file mode 100644 index 0000000..95e7403 --- /dev/null +++ b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/parser/GraphixParserHint.java
@@ -0,0 +1,45 @@ +/* + * 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.graphix.lang.parser; + +import org.apache.asterix.graphix.algebra.compiler.option.ElementEvaluationOption; + +/** + * Graphix SQL++ specific hints. Note that this is not an extension of the SQL++ hint class: + * {@link org.apache.asterix.lang.sqlpp.parser.SqlppHint}, so callers must use their own facilities for hint finding. + */ +public enum GraphixParserHint { + EXPAND_AND_UNION_HINT(ElementEvaluationOption.EXPAND_AND_UNION.getOptionValue()), + SWITCH_AND_CYCLE_HINT(ElementEvaluationOption.SWITCH_AND_CYCLE.getOptionValue()); + + private final String id; + + GraphixParserHint(String id) { + this.id = id; + } + + public String getId() { + return id; + } + + @Override + public String toString() { + return getId(); + } +}
diff --git a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrite/GraphixQueryRewriter.java b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrite/GraphixQueryRewriter.java new file mode 100644 index 0000000..7355a4d --- /dev/null +++ b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrite/GraphixQueryRewriter.java
@@ -0,0 +1,343 @@ +/* + * 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.graphix.lang.rewrite; + +import java.io.PrintWriter; +import java.io.StringWriter; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.apache.asterix.common.config.CompilerProperties; +import org.apache.asterix.common.exceptions.CompilationException; +import org.apache.asterix.common.exceptions.ErrorCode; +import org.apache.asterix.common.metadata.DataverseName; +import org.apache.asterix.graphix.common.metadata.GraphIdentifier; +import org.apache.asterix.graphix.lang.clause.FromGraphClause; +import org.apache.asterix.graphix.lang.clause.LowerListClause; +import org.apache.asterix.graphix.lang.clause.LowerSwitchClause; +import org.apache.asterix.graphix.lang.parser.GraphixParserFactory; +import org.apache.asterix.graphix.lang.rewrite.common.BranchLookupTable; +import org.apache.asterix.graphix.lang.rewrite.common.ElementLookupTable; +import org.apache.asterix.graphix.lang.rewrite.print.SqlppASTPrintQueryVisitor; +import org.apache.asterix.graphix.lang.rewrite.resolve.ExhaustiveSearchResolver; +import org.apache.asterix.graphix.lang.rewrite.resolve.SchemaKnowledgeTable; +import org.apache.asterix.graphix.lang.rewrite.visitor.ElementLookupTableVisitor; +import org.apache.asterix.graphix.lang.rewrite.visitor.FunctionResolutionVisitor; +import org.apache.asterix.graphix.lang.rewrite.visitor.GraphixFunctionCallVisitor; +import org.apache.asterix.graphix.lang.rewrite.visitor.GraphixLoweringVisitor; +import org.apache.asterix.graphix.lang.rewrite.visitor.PatternGraphGroupVisitor; +import org.apache.asterix.graphix.lang.rewrite.visitor.PopulateUnknownsVisitor; +import org.apache.asterix.graphix.lang.rewrite.visitor.PostRewriteCheckVisitor; +import org.apache.asterix.graphix.lang.rewrite.visitor.PreRewriteCheckVisitor; +import org.apache.asterix.graphix.lang.rewrite.visitor.QueryCanonicalizationVisitor; +import org.apache.asterix.graphix.lang.rewrite.visitor.SubqueryVertexJoinVisitor; +import org.apache.asterix.graphix.lang.rewrite.visitor.VariableScopingCheckVisitor; +import org.apache.asterix.graphix.lang.statement.GraphElementDeclaration; +import org.apache.asterix.graphix.lang.struct.PatternGroup; +import org.apache.asterix.lang.common.base.Expression; +import org.apache.asterix.lang.common.base.ILangExpression; +import org.apache.asterix.lang.common.base.IParserFactory; +import org.apache.asterix.lang.common.base.IReturningStatement; +import org.apache.asterix.lang.common.rewrites.LangRewritingContext; +import org.apache.asterix.lang.common.statement.Query; +import org.apache.asterix.lang.common.struct.VarIdentifier; +import org.apache.asterix.lang.common.util.ExpressionUtils; +import org.apache.asterix.lang.common.visitor.base.ILangVisitor; +import org.apache.asterix.lang.sqlpp.rewrites.SqlppFunctionBodyRewriter; +import org.apache.asterix.lang.sqlpp.rewrites.SqlppQueryRewriter; +import org.apache.asterix.metadata.entities.Dataverse; +import org.apache.hyracks.algebricks.common.exceptions.AlgebricksException; +import org.apache.hyracks.api.exceptions.SourceLocation; +import org.apache.hyracks.util.LogRedactionUtil; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +/** + * Rewriter for Graphix queries, which will lower all graph AST nodes into SQL++ AST nodes. We perform the following: + * <ol> + * <li>Perform an error-checking on the fresh AST (immediately after parsing).</li> + * <li>Populate the unknowns in our AST (e.g. vertex / edge variables, projections, GROUP-BY keys).</li> + * <li>Perform a variable-scoping pass to identify illegal variables (either duplicate or out-of-scope).</li> + * <li>Resolve all of our function calls (Graphix, SQL++, and user-defined).</li> + * <li>Perform resolution of unlabeled vertices / edges, as well as edge directions.</li> + * <li>For all Graphix subqueries whose vertices are correlated, rewrite this correlation to be explicit.</li> + * <li>Using the labels of the vertices / edges in our AST, fetch the relevant graph elements from our metadata.</li> + * <li>Perform a canonical Graphix lowering pass to remove ambiguities (e.g. undirected edges).</li> + * <li>Perform a lowering pass to transform Graphix AST nodes to SQL++ AST nodes.</li> + * <li>Perform another lowering pass to transform Graphix CALL-EXPR nodes to SQL++ AST nodes.</li> + * <li>Perform all SQL++ rewrites on our newly lowered AST.</li> + * </ol> + */ +public class GraphixQueryRewriter extends SqlppQueryRewriter { + private static final Logger LOGGER = LogManager.getLogger(GraphixQueryRewriter.class); + + private final GraphixParserFactory parserFactory; + private final SqlppQueryRewriter bodyRewriter; + + public GraphixQueryRewriter(IParserFactory parserFactory) { + super(parserFactory); + this.parserFactory = (GraphixParserFactory) parserFactory; + this.bodyRewriter = getFunctionAndViewBodyRewriter(); + } + + @Override + public void rewrite(LangRewritingContext langRewritingContext, IReturningStatement topStatement, + boolean allowNonStoredUDFCalls, boolean inlineUdfsAndViews, Collection<VarIdentifier> externalVars) + throws CompilationException { + LOGGER.debug("Starting Graphix AST rewrites."); + + // Perform an initial error-checking pass to validate our user query. + LOGGER.trace("Performing pre-Graphix-rewrite check (user query validation)."); + GraphixRewritingContext graphixRewritingContext = (GraphixRewritingContext) langRewritingContext; + topStatement.accept(new PreRewriteCheckVisitor(graphixRewritingContext), null); + + // Perform the Graphix rewrites. + rewriteGraphixASTNodes(graphixRewritingContext, topStatement, allowNonStoredUDFCalls); + + // Sanity check: ensure that no graph AST nodes exist after this point. + Map<String, Object> queryConfig = graphixRewritingContext.getMetadataProvider().getConfig(); + if (queryConfig.containsKey(CompilerProperties.COMPILER_INTERNAL_SANITYCHECK_KEY)) { + String configValue = (String) queryConfig.get(CompilerProperties.COMPILER_INTERNAL_SANITYCHECK_KEY); + if (!configValue.equalsIgnoreCase("false")) { + LOGGER.trace("Performing post-Graphix-rewrite check (making sure no graph AST nodes exist)."); + topStatement.accept(new PostRewriteCheckVisitor(), null); + } + } + + // Perform the remainder of the SQL++ rewrites. + LOGGER.debug("Ending Graphix AST rewrites. Now starting SQL++ AST rewrites."); + rewriteSQLPPASTNodes(langRewritingContext, topStatement, allowNonStoredUDFCalls, inlineUdfsAndViews, + externalVars); + + // Update the variable counter on our context. + topStatement.setVarCounter(graphixRewritingContext.getVarCounter().get()); + LOGGER.debug("Ending SQL++ AST rewrites."); + } + + public void loadNormalizedGraphElement(GraphixRewritingContext graphixRewritingContext, + GraphElementDeclaration graphElementDeclaration) throws CompilationException { + if (graphElementDeclaration.getNormalizedBody() == null) { + Dataverse defaultDataverse = graphixRewritingContext.getMetadataProvider().getDefaultDataverse(); + Dataverse targetDataverse; + + // We might need to change our dataverse, if the element definition requires a different one. + GraphIdentifier graphIdentifier = graphElementDeclaration.getGraphIdentifier(); + DataverseName graphDataverseName = graphIdentifier.getDataverseName(); + if (graphDataverseName == null || graphDataverseName.equals(defaultDataverse.getDataverseName())) { + targetDataverse = defaultDataverse; + + } else { + try { + targetDataverse = graphixRewritingContext.getMetadataProvider().findDataverse(graphDataverseName); + + } catch (AlgebricksException e) { + throw new CompilationException(ErrorCode.UNKNOWN_DATAVERSE, e, + graphElementDeclaration.getSourceLocation(), graphDataverseName); + } + } + graphixRewritingContext.getMetadataProvider().setDefaultDataverse(targetDataverse); + + // Get the body of the rewritten query. + Expression rawBody = graphElementDeclaration.getRawBody(); + try { + SourceLocation sourceLocation = graphElementDeclaration.getSourceLocation(); + Query wrappedQuery = ExpressionUtils.createWrappedQuery(rawBody, sourceLocation); + bodyRewriter.rewrite(graphixRewritingContext, wrappedQuery, false, false, List.of()); + graphElementDeclaration.setNormalizedBody(wrappedQuery.getBody()); + + } catch (CompilationException e) { + throw new CompilationException(ErrorCode.COMPILATION_ERROR, rawBody.getSourceLocation(), + "Bad definition for a graph element: " + e.getMessage()); + + } finally { + // Switch back to the working dataverse. + graphixRewritingContext.getMetadataProvider().setDefaultDataverse(defaultDataverse); + } + } + } + + /** + * Lower a Graphix AST into a SQLPP AST. The only nodes that should survive are the following: + * 1. {@link org.apache.asterix.graphix.lang.clause.FromGraphClause} + * 2. {@link LowerListClause} + * 3. {@link LowerSwitchClause} + */ + public void rewriteGraphixASTNodes(GraphixRewritingContext graphixRewritingContext, + IReturningStatement topStatement, boolean allowNonStoredUDFCalls) throws CompilationException { + // Generate names for unnamed graph elements, projections in our SELECT CLAUSE, and keys in our GROUP BY. + LOGGER.trace("Populating unknowns (both graph and non-graph) in our AST."); + rewriteExpr(topStatement, new PopulateUnknownsVisitor(graphixRewritingContext)); + + // Verify that variables are properly within scope. + LOGGER.trace("Verifying that variables are unique and are properly scoped."); + rewriteExpr(topStatement, new VariableScopingCheckVisitor(graphixRewritingContext)); + + // Resolve all of our (Graphix, SQL++, and user-defined) function calls. + LOGGER.trace("Resolving Graphix, SQL++, and user-defined function calls."); + rewriteExpr(topStatement, new FunctionResolutionVisitor(graphixRewritingContext, allowNonStoredUDFCalls)); + + // Rewrite implicit correlated vertex JOINs as explicit JOINs. + LOGGER.trace("Rewriting correlated implicit vertex JOINs into explicit JOINs."); + rewriteExpr(topStatement, new SubqueryVertexJoinVisitor(graphixRewritingContext)); + + // Resolve our vertex labels, edge labels, and edge directions. + LOGGER.trace("Performing label and edge direction resolution."); + Map<GraphIdentifier, SchemaKnowledgeTable> knowledgeTableMap = new HashMap<>(); + Map<GraphIdentifier, PatternGroup> resolutionPatternMap = new HashMap<>(); + topStatement.accept(new PatternGraphGroupVisitor(resolutionPatternMap, graphixRewritingContext) { + @Override + public Expression visit(FromGraphClause fromGraphClause, ILangExpression arg) throws CompilationException { + GraphIdentifier graphIdentifier = fromGraphClause.getGraphIdentifier(metadataProvider); + SchemaKnowledgeTable schemaTable = new SchemaKnowledgeTable(fromGraphClause, graphixRewritingContext); + knowledgeTableMap.put(graphIdentifier, schemaTable); + return super.visit(fromGraphClause, arg); + } + }, null); + for (Map.Entry<GraphIdentifier, PatternGroup> mapEntry : resolutionPatternMap.entrySet()) { + SchemaKnowledgeTable knowledgeTable = knowledgeTableMap.get(mapEntry.getKey()); + new ExhaustiveSearchResolver(knowledgeTable).resolve(mapEntry.getValue()); + } + + // Fetch all relevant graph element declarations, using the element labels. + LOGGER.trace("Fetching relevant edge and vertex bodies from our graph schema."); + ElementLookupTable elementLookupTable = new ElementLookupTable(); + ElementLookupTableVisitor elementLookupTableVisitor = + new ElementLookupTableVisitor(graphixRewritingContext, elementLookupTable, parserFactory); + rewriteExpr(topStatement, elementLookupTableVisitor); + for (GraphElementDeclaration graphElementDeclaration : elementLookupTable) { + loadNormalizedGraphElement(graphixRewritingContext, graphElementDeclaration); + } + + // Expand / enumerate vertex and edge patterns to snuff out all ambiguities. + LOGGER.trace("Performing a canonicalization pass to expand or enumerate patterns."); + BranchLookupTable branchLookupTable = new BranchLookupTable(); + QueryCanonicalizationVisitor queryCanonicalizationVisitor = + new QueryCanonicalizationVisitor(branchLookupTable, graphixRewritingContext); + rewriteExpr(topStatement, queryCanonicalizationVisitor); + + // Transform all graph AST nodes (i.e. perform the representation lowering). + LOGGER.trace("Lowering the Graphix AST-specific nodes representation to a SQL++ representation."); + GraphixLoweringVisitor graphixLoweringVisitor = + new GraphixLoweringVisitor(graphixRewritingContext, elementLookupTable, branchLookupTable); + rewriteExpr(topStatement, graphixLoweringVisitor); + + // Lower all of our Graphix function calls (and perform schema-enrichment). + LOGGER.trace("Lowering the Graphix CALL-EXPR nodes to a pure SQL++ representation."); + rewriteExpr(topStatement, new GraphixFunctionCallVisitor(graphixRewritingContext)); + } + + /** + * Rewrite a SQLPP AST. We do not perform the following: + * <ul> + * <li>Function call resolution (this is handled in {@link FunctionResolutionVisitor}).</li> + * <li>Column name generation (this is handled in {@link PopulateUnknownsVisitor}).</li> + * <li>SQL-compat rewrites (not supported).</li> + * </ul> + */ + public void rewriteSQLPPASTNodes(LangRewritingContext langRewritingContext, IReturningStatement topStatement, + boolean allowNonStoredUDFCalls, boolean inlineUdfsAndViews, Collection<VarIdentifier> externalVars) + throws CompilationException { + super.setup(langRewritingContext, topStatement, externalVars, allowNonStoredUDFCalls, inlineUdfsAndViews); + super.substituteGroupbyKeyExpression(); + super.rewriteGroupBys(); + super.rewriteSetOperations(); + super.inlineColumnAlias(); + super.rewriteWindowExpressions(); + super.rewriteGroupingSets(); + super.variableCheckAndRewrite(); + super.extractAggregatesFromCaseExpressions(); + super.rewriteGroupByAggregationSugar(); + super.rewriteWindowAggregationSugar(); + super.rewriteOperatorExpression(); + super.rewriteCaseExpressions(); + super.rewriteListInputFunctions(); + super.rewriteRightJoins(); + super.loadAndInlineUdfsAndViews(); + super.rewriteSpecialFunctionNames(); + super.inlineWithExpressions(); + } + + @Override + protected SqlppFunctionBodyRewriter getFunctionAndViewBodyRewriter() { + return new SqlppFunctionBodyRewriter(parserFactory) { + @Override + public void rewrite(LangRewritingContext langRewritingContext, IReturningStatement topStatement, + boolean allowNonStoredUDFCalls, boolean inlineUdfsAndViews, Collection<VarIdentifier> externalVars) + throws CompilationException { + // Perform an initial error-checking pass to validate our body. + GraphixRewritingContext graphixRewritingContext = (GraphixRewritingContext) langRewritingContext; + topStatement.accept(new PreRewriteCheckVisitor(graphixRewritingContext), null); + + // Perform the Graphix rewrites. + rewriteGraphixASTNodes(graphixRewritingContext, topStatement, allowNonStoredUDFCalls); + + // Sanity check: ensure that no graph AST nodes exist after this point. + Map<String, Object> queryConfig = graphixRewritingContext.getMetadataProvider().getConfig(); + if (queryConfig.containsKey(CompilerProperties.COMPILER_INTERNAL_SANITYCHECK_KEY)) { + String configValue = (String) queryConfig.get(CompilerProperties.COMPILER_INTERNAL_SANITYCHECK_KEY); + if (!configValue.equalsIgnoreCase("false")) { + topStatement.accept(new PostRewriteCheckVisitor(), null); + } + } + + // Perform the remainder of the SQL++ (body specific) rewrites. + super.setup(langRewritingContext, topStatement, externalVars, allowNonStoredUDFCalls, + inlineUdfsAndViews); + super.substituteGroupbyKeyExpression(); + super.rewriteGroupBys(); + super.rewriteSetOperations(); + super.inlineColumnAlias(); + super.rewriteWindowExpressions(); + super.rewriteGroupingSets(); + super.variableCheckAndRewrite(); + super.extractAggregatesFromCaseExpressions(); + super.rewriteGroupByAggregationSugar(); + super.rewriteWindowAggregationSugar(); + super.rewriteOperatorExpression(); + super.rewriteCaseExpressions(); + super.rewriteListInputFunctions(); + super.rewriteRightJoins(); + + // Update the variable counter in our context. + topStatement.setVarCounter(langRewritingContext.getVarCounter().get()); + } + }; + } + + private <R, T> void rewriteExpr(IReturningStatement returningStatement, ILangVisitor<R, T> visitor) + throws CompilationException { + if (LOGGER.isTraceEnabled()) { + StringWriter stringWriter = new StringWriter(); + PrintWriter printWriter = new PrintWriter(stringWriter); + returningStatement.accept(new SqlppASTPrintQueryVisitor(printWriter), null); + String planAsString = LogRedactionUtil.userData(stringWriter.toString()); + LOGGER.trace("Plan before rewrite: {}\n", planAsString); + } + returningStatement.accept(visitor, null); + if (LOGGER.isTraceEnabled()) { + StringWriter stringWriter = new StringWriter(); + PrintWriter printWriter = new PrintWriter(stringWriter); + returningStatement.accept(new SqlppASTPrintQueryVisitor(printWriter), null); + String planAsString = LogRedactionUtil.userData(stringWriter.toString()); + LOGGER.trace("Plan after rewrite: {}\n", planAsString); + } + } +}
diff --git a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/GraphixRewriterFactory.java b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrite/GraphixRewriterFactory.java similarity index 96% rename from asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/GraphixRewriterFactory.java rename to asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrite/GraphixRewriterFactory.java index 6b1cf30..7b6b79b 100644 --- a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/GraphixRewriterFactory.java +++ b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrite/GraphixRewriterFactory.java
@@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.asterix.graphix.lang.rewrites; +package org.apache.asterix.graphix.lang.rewrite; import org.apache.asterix.graphix.lang.parser.GraphixParserFactory; import org.apache.asterix.lang.common.base.IParserFactory;
diff --git a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrite/GraphixRewritingContext.java b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrite/GraphixRewritingContext.java new file mode 100644 index 0000000..6018fce --- /dev/null +++ b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrite/GraphixRewritingContext.java
@@ -0,0 +1,129 @@ +/* + * 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.graphix.lang.rewrite; + +import java.util.HashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Set; +import java.util.stream.Stream; + +import org.apache.asterix.common.exceptions.CompilationException; +import org.apache.asterix.common.exceptions.ErrorCode; +import org.apache.asterix.graphix.algebra.compiler.option.ElementEvaluationOption; +import org.apache.asterix.graphix.algebra.compiler.option.IGraphixCompilerOption; +import org.apache.asterix.graphix.algebra.compiler.option.SchemaDecorateEdgeOption; +import org.apache.asterix.graphix.algebra.compiler.option.SchemaDecorateVertexOption; +import org.apache.asterix.graphix.algebra.compiler.option.SemanticsNavigationOption; +import org.apache.asterix.graphix.algebra.compiler.option.SemanticsPatternOption; +import org.apache.asterix.graphix.common.metadata.GraphIdentifier; +import org.apache.asterix.graphix.lang.statement.DeclareGraphStatement; +import org.apache.asterix.lang.common.expression.VariableExpr; +import org.apache.asterix.lang.common.rewrites.LangRewritingContext; +import org.apache.asterix.lang.common.statement.FunctionDecl; +import org.apache.asterix.lang.common.statement.ViewDecl; +import org.apache.asterix.lang.common.struct.VarIdentifier; +import org.apache.asterix.lang.sqlpp.util.SqlppVariableUtil; +import org.apache.asterix.metadata.declared.MetadataProvider; +import org.apache.hyracks.api.exceptions.IWarningCollector; + +/** + * Wrapper class for {@link LangRewritingContext} and for Graphix specific rewriting. + */ +public class GraphixRewritingContext extends LangRewritingContext { + private final Map<GraphIdentifier, DeclareGraphStatement> declaredGraphs = new HashMap<>(); + private final Map<String, Integer> uniqueCopyCounter = new HashMap<>(); + private final Map<String, String> configFileOptions; + + public GraphixRewritingContext(MetadataProvider metadataProvider, List<FunctionDecl> declaredFunctions, + List<ViewDecl> declaredViews, Set<DeclareGraphStatement> declareGraphStatements, + IWarningCollector warningCollector, int varCounter, Map<String, String> configFileOptions) { + super(metadataProvider, declaredFunctions, declaredViews, warningCollector, varCounter); + declareGraphStatements.forEach(d -> { + GraphIdentifier graphIdentifier = new GraphIdentifier(d.getDataverseName(), d.getGraphName()); + this.declaredGraphs.put(graphIdentifier, d); + }); + this.configFileOptions = configFileOptions; + } + + public Map<GraphIdentifier, DeclareGraphStatement> getDeclaredGraphs() { + return declaredGraphs; + } + + public VariableExpr getGraphixVariableCopy(String existingIdentifierValue) { + uniqueCopyCounter.put(existingIdentifierValue, uniqueCopyCounter.getOrDefault(existingIdentifierValue, 0) + 1); + int currentCount = uniqueCopyCounter.get(existingIdentifierValue); + String variableName = String.format("#GGVC(%s,%s)", existingIdentifierValue, currentCount); + return new VariableExpr(new VarIdentifier(variableName)); + } + + public VariableExpr getGraphixVariableCopy(VariableExpr existingVariable) { + VarIdentifier existingIdentifier = existingVariable.getVar(); + String variableName = SqlppVariableUtil.toUserDefinedVariableName(existingIdentifier).getValue(); + return getGraphixVariableCopy(variableName); + } + + public IGraphixCompilerOption getSetting(String settingName) throws CompilationException { + IGraphixCompilerOption[] enumValues; + switch (settingName) { + case ElementEvaluationOption.OPTION_KEY_NAME: + enumValues = ElementEvaluationOption.values(); + return parseSetting(settingName, enumValues, ElementEvaluationOption.OPTION_DEFAULT); + + case SchemaDecorateEdgeOption.OPTION_KEY_NAME: + enumValues = SchemaDecorateEdgeOption.values(); + return parseSetting(settingName, enumValues, SchemaDecorateEdgeOption.OPTION_DEFAULT); + + case SchemaDecorateVertexOption.OPTION_KEY_NAME: + enumValues = SchemaDecorateVertexOption.values(); + return parseSetting(settingName, enumValues, SchemaDecorateVertexOption.OPTION_DEFAULT); + + case SemanticsNavigationOption.OPTION_KEY_NAME: + enumValues = SemanticsNavigationOption.values(); + return parseSetting(settingName, enumValues, SemanticsNavigationOption.OPTION_DEFAULT); + + case SemanticsPatternOption.OPTION_KEY_NAME: + enumValues = SemanticsPatternOption.values(); + return parseSetting(settingName, enumValues, SemanticsPatternOption.OPTION_DEFAULT); + + default: + throw new CompilationException(ErrorCode.COMPILATION_ILLEGAL_STATE, "Illegal setting requested!"); + } + } + + private IGraphixCompilerOption parseSetting(String settingName, IGraphixCompilerOption[] settingValues, + IGraphixCompilerOption defaultValue) throws CompilationException { + // Always check our metadata configuration first. + Object metadataConfigValue = getMetadataProvider().getConfig().get(settingName); + if (metadataConfigValue != null) { + String configValueString = ((String) metadataConfigValue).toLowerCase(Locale.ROOT); + return Stream.of(settingValues).filter(o -> o.getOptionValue().equals(configValueString)).findFirst() + .orElseThrow(() -> new CompilationException(ErrorCode.PARAMETER_NO_VALUE, configValueString)); + } + + // If our setting is not in metadata, check our config file. + String configFileValue = configFileOptions.get(settingName); + if (configFileValue != null) { + return Stream.of(settingValues).filter(o -> o.getOptionValue().equals(configFileValue)).findFirst() + .orElseThrow(() -> new CompilationException(ErrorCode.PARAMETER_NO_VALUE, configFileValue)); + } + + // Otherwise, return our default value. + return defaultValue; + } +}
diff --git a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrite/canonical/CanonicalElementBranchConsumer.java b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrite/canonical/CanonicalElementBranchConsumer.java new file mode 100644 index 0000000..8bd208b --- /dev/null +++ b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrite/canonical/CanonicalElementBranchConsumer.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.graphix.lang.rewrite.canonical; + +import java.util.List; + +import org.apache.asterix.common.exceptions.CompilationException; +import org.apache.asterix.common.exceptions.ErrorCode; +import org.apache.asterix.graphix.lang.expression.EdgePatternExpr; +import org.apache.asterix.graphix.lang.expression.PathPatternExpr; +import org.apache.asterix.graphix.lang.expression.VertexPatternExpr; +import org.apache.asterix.graphix.lang.rewrite.common.BranchLookupTable; +import org.apache.asterix.graphix.lang.struct.EdgeDescriptor; +import org.apache.asterix.lang.common.base.AbstractExpression; + +public class CanonicalElementBranchConsumer implements ICanonicalElementConsumer { + private final BranchLookupTable branchLookupTable; + + public CanonicalElementBranchConsumer(BranchLookupTable branchLookupTable) { + this.branchLookupTable = branchLookupTable; + } + + @Override + public void accept(AbstractExpression ambiguousElement, List<? extends AbstractExpression> canonicalElements) + throws CompilationException { + if (ambiguousElement instanceof VertexPatternExpr) { + throw new CompilationException(ErrorCode.COMPILATION_ERROR, + "Cannot evaluate an ambiguous dangling vertex using SWITCH_AND_CYCLE. Try EXPAND_AND_UNION.", + ambiguousElement.getSourceLocation()); + } + EdgePatternExpr ambiguousEdgeElement = (EdgePatternExpr) ambiguousElement; + if (ambiguousEdgeElement.getEdgeDescriptor().getPatternType() == EdgeDescriptor.PatternType.EDGE) { + for (AbstractExpression canonicalElement : canonicalElements) { + EdgePatternExpr canonicalEdge = (EdgePatternExpr) canonicalElement; + branchLookupTable.putBranch(ambiguousEdgeElement, canonicalEdge); + } + + } else { // ambiguousEdgeElement.getEdgeDescriptor().getPatternType() == EdgeDescriptor.PatternType.PATH + for (AbstractExpression canonicalElement : canonicalElements) { + PathPatternExpr canonicalPath = (PathPatternExpr) canonicalElement; + branchLookupTable.putBranch(ambiguousEdgeElement, canonicalPath); + } + } + } +}
diff --git a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrite/canonical/CanonicalElementExpansionConsumer.java b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrite/canonical/CanonicalElementExpansionConsumer.java new file mode 100644 index 0000000..a35e61a --- /dev/null +++ b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrite/canonical/CanonicalElementExpansionConsumer.java
@@ -0,0 +1,331 @@ +/* + * 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.graphix.lang.rewrite.canonical; + +import static org.apache.asterix.graphix.lang.rewrite.lower.action.PathPatternAction.buildPathRecord; +import static org.apache.asterix.graphix.lang.rewrite.util.CanonicalElementUtil.replaceEdgeInIterator; +import static org.apache.asterix.graphix.lang.rewrite.util.CanonicalElementUtil.replaceVertexInIterator; + +import java.util.ArrayDeque; +import java.util.ArrayList; +import java.util.Deque; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.ListIterator; +import java.util.Map; +import java.util.Set; +import java.util.function.Consumer; +import java.util.stream.Collectors; + +import org.apache.asterix.common.exceptions.CompilationException; +import org.apache.asterix.graphix.lang.annotation.SubqueryVertexJoinAnnotation; +import org.apache.asterix.graphix.lang.expression.EdgePatternExpr; +import org.apache.asterix.graphix.lang.expression.PathPatternExpr; +import org.apache.asterix.graphix.lang.expression.VertexPatternExpr; +import org.apache.asterix.graphix.lang.rewrite.GraphixRewritingContext; +import org.apache.asterix.graphix.lang.rewrite.visitor.GraphixDeepCopyVisitor; +import org.apache.asterix.graphix.lang.struct.EdgeDescriptor; +import org.apache.asterix.graphix.lang.visitor.base.AbstractGraphixQueryVisitor; +import org.apache.asterix.lang.common.base.AbstractExpression; +import org.apache.asterix.lang.common.base.Expression; +import org.apache.asterix.lang.common.base.ILangExpression; +import org.apache.asterix.lang.common.clause.LetClause; +import org.apache.asterix.lang.common.expression.RecordConstructor; +import org.apache.asterix.lang.common.expression.VariableExpr; +import org.apache.asterix.lang.sqlpp.clause.SelectBlock; +import org.apache.asterix.lang.sqlpp.clause.SelectSetOperation; +import org.apache.asterix.lang.sqlpp.expression.SelectExpression; +import org.apache.asterix.lang.sqlpp.optype.SetOpType; +import org.apache.asterix.lang.sqlpp.struct.SetOperationInput; +import org.apache.asterix.lang.sqlpp.struct.SetOperationRight; + +public class CanonicalElementExpansionConsumer implements ICanonicalElementConsumer { + private final GraphixDeepCopyVisitor deepCopyVisitor = new GraphixDeepCopyVisitor(); + private final Deque<SelectBlock> blackSelectBlockStack = new ArrayDeque<>(); + private final Deque<SelectBlock> redSelectBlockStack = new ArrayDeque<>(); + private final GraphixRewritingContext graphixRewritingContext; + + // We should return a copy of our original SELECT-EXPR. + private final Set<SetOperationInput> remainingSetOpInputs; + + public CanonicalElementExpansionConsumer(SelectExpression originalSelectExpression, + GraphixRewritingContext graphixRewritingContext) { + this.graphixRewritingContext = graphixRewritingContext; + this.remainingSetOpInputs = new HashSet<>(); + this.remainingSetOpInputs.add(originalSelectExpression.getSelectSetOperation().getLeftInput()); + this.remainingSetOpInputs.addAll(originalSelectExpression.getSelectSetOperation().getRightInputs().stream() + .map(SetOperationRight::getSetOperationRightInput).collect(Collectors.toSet())); + } + + @Override + public void accept(AbstractExpression ambiguousElement, List<? extends AbstractExpression> canonicalElements) + throws CompilationException { + Deque<SelectBlock> readStack, writeStack; + if (blackSelectBlockStack.isEmpty()) { + writeStack = blackSelectBlockStack; + readStack = redSelectBlockStack; + + } else { + writeStack = redSelectBlockStack; + readStack = blackSelectBlockStack; + } + + ICanonicalPatternUpdater pathPatternReplacer; + if (ambiguousElement instanceof VertexPatternExpr) { + pathPatternReplacer = new VertexPatternUpdater(ambiguousElement); + + } else { // ambiguousElement instanceof EdgePatternExpr + EdgePatternExpr ambiguousEdgePattern = (EdgePatternExpr) ambiguousElement; + EdgeDescriptor ambiguousEdgeDescriptor = ambiguousEdgePattern.getEdgeDescriptor(); + if (ambiguousEdgeDescriptor.getPatternType() == EdgeDescriptor.PatternType.EDGE) { + pathPatternReplacer = new EdgePatternUpdater(ambiguousElement); + + } else { // ambiguousEdgeDescriptor().getPatternType() == EdgeDescriptor.PatternType.PATH + pathPatternReplacer = new PathPatternUpdater(ambiguousElement); + } + } + + // Consume all of our read stack. + while (!readStack.isEmpty()) { + SelectBlock workingSelectBlock = readStack.pop(); + for (AbstractExpression canonicalElement : canonicalElements) { + SelectBlock workingSelectBlockCopy = deepCopyVisitor.visit(workingSelectBlock, null); + workingSelectBlockCopy.accept(new AbstractGraphixQueryVisitor() { + @Override + public Expression visit(PathPatternExpr pathPatternExpr, ILangExpression arg) + throws CompilationException { + pathPatternReplacer.accept(canonicalElement, pathPatternExpr); + return pathPatternExpr; + } + }, null); + writeStack.push(workingSelectBlockCopy); + } + } + } + + public void finalize(SelectExpression selectExpression, Consumer<SelectBlock> selectBlockCallback) { + SelectSetOperation selectSetOperation = selectExpression.getSelectSetOperation(); + SetOperationInput leftSetOperationInput = selectSetOperation.getLeftInput(); + + // Exhaust all of our generated SELECT-BLOCKs. + Deque<SelectBlock> finalStack = (redSelectBlockStack.isEmpty()) ? blackSelectBlockStack : redSelectBlockStack; + if (!remainingSetOpInputs.contains(leftSetOperationInput)) { + leftSetOperationInput.setSelectBlock(finalStack.peek()); + selectBlockCallback.accept(finalStack.pop()); + } + for (SetOperationRight rightInput : selectSetOperation.getRightInputs()) { + SetOperationInput rightSetOperationInput = rightInput.getSetOperationRightInput(); + if (!remainingSetOpInputs.contains(rightSetOperationInput)) { + rightSetOperationInput.setSelectBlock(finalStack.peek()); + selectBlockCallback.accept(finalStack.pop()); + } + } + while (!finalStack.isEmpty()) { + SetOperationInput newSetOpInput = new SetOperationInput(finalStack.peek(), null); + selectSetOperation.getRightInputs().add(new SetOperationRight(SetOpType.UNION, false, newSetOpInput)); + selectBlockCallback.accept(finalStack.pop()); + } + } + + public void resetSelectBlock(SelectBlock selectBlock) { + blackSelectBlockStack.clear(); + redSelectBlockStack.clear(); + blackSelectBlockStack.push(selectBlock); + + // Remove from our original SET-OP input set, the SET-OP this SELECT-BLOCK corresponds to. + remainingSetOpInputs.removeIf(s -> s.selectBlock() && s.getSelectBlock().equals(selectBlock)); + } + + // We provide the following interface to handle dangling vertices, edges, and paths. + @FunctionalInterface + private interface ICanonicalPatternUpdater { + void accept(AbstractExpression canonicalExpr, PathPatternExpr pathPatternExpr) throws CompilationException; + } + + private class VertexPatternUpdater implements ICanonicalPatternUpdater { + private final AbstractExpression ambiguousElement; + + private VertexPatternUpdater(AbstractExpression ambiguousElement) { + this.ambiguousElement = ambiguousElement; + } + + @Override + public void accept(AbstractExpression canonicalExpr, PathPatternExpr pathPatternExpr) + throws CompilationException { + List<VertexPatternExpr> vertexExpressions = pathPatternExpr.getVertexExpressions(); + ListIterator<VertexPatternExpr> vertexIterator = vertexExpressions.listIterator(); + VertexPatternExpr ambiguousVertex = (VertexPatternExpr) ambiguousElement; + VertexPatternExpr canonicalVertex = (VertexPatternExpr) canonicalExpr; + + // Our replacement map must also include any subquery-correlated-join vertices. + Map<VertexPatternExpr, VertexPatternExpr> replacementMap = new HashMap<>(); + replacementMap.put(ambiguousVertex, canonicalVertex); + for (VertexPatternExpr vertexPatternExpr : vertexExpressions) { + if (vertexPatternExpr.findHint(SubqueryVertexJoinAnnotation.class) != null) { + SubqueryVertexJoinAnnotation hint = vertexPatternExpr.findHint(SubqueryVertexJoinAnnotation.class); + if (hint.getSourceVertexVariable().equals(ambiguousVertex.getVariableExpr())) { + VertexPatternExpr canonicalVertexCopy = deepCopyVisitor.visit(canonicalVertex, null); + canonicalVertexCopy.setVariableExpr(vertexPatternExpr.getVariableExpr()); + replacementMap.put(vertexPatternExpr, canonicalVertexCopy); + } + } + } + replaceVertexInIterator(replacementMap, vertexIterator, deepCopyVisitor); + } + } + + private class EdgePatternUpdater implements ICanonicalPatternUpdater { + private final AbstractExpression ambiguousElement; + + private EdgePatternUpdater(AbstractExpression ambiguousElement) { + this.ambiguousElement = ambiguousElement; + } + + @Override + public void accept(AbstractExpression canonicalExpr, PathPatternExpr pathPatternExpr) + throws CompilationException { + EdgePatternExpr ambiguousEdgePattern = (EdgePatternExpr) ambiguousElement; + VertexPatternExpr ambiguousLeftVertex = ambiguousEdgePattern.getLeftVertex(); + VertexPatternExpr ambiguousRightVertex = ambiguousEdgePattern.getRightVertex(); + EdgePatternExpr canonicalEdge = (EdgePatternExpr) canonicalExpr; + VertexPatternExpr canonicalLeftVertex = canonicalEdge.getLeftVertex(); + VertexPatternExpr canonicalRightVertex = canonicalEdge.getRightVertex(); + + // Iterate through our edge list. + List<EdgePatternExpr> edgeExpressions = pathPatternExpr.getEdgeExpressions(); + ListIterator<EdgePatternExpr> edgeIterator = edgeExpressions.listIterator(); + replaceEdgeInIterator(Map.of(ambiguousEdgePattern, canonicalEdge), edgeIterator, deepCopyVisitor); + + // Iterate through our vertex list. + replaceEdgeVerticesAndJoinCopiesInIterator(pathPatternExpr, ambiguousLeftVertex, ambiguousRightVertex, + canonicalLeftVertex, canonicalRightVertex); + } + } + + private class PathPatternUpdater implements ICanonicalPatternUpdater { + private final AbstractExpression ambiguousElement; + + private PathPatternUpdater(AbstractExpression ambiguousElement) { + this.ambiguousElement = ambiguousElement; + } + + @Override + public void accept(AbstractExpression canonicalExpr, PathPatternExpr pathPatternExpr) + throws CompilationException { + EdgePatternExpr ambiguousEdgePattern = (EdgePatternExpr) ambiguousElement; + VertexPatternExpr ambiguousLeftVertex = ambiguousEdgePattern.getLeftVertex(); + VertexPatternExpr ambiguousRightVertex = ambiguousEdgePattern.getRightVertex(); + EdgeDescriptor ambiguousEdgeDescriptor = ambiguousEdgePattern.getEdgeDescriptor(); + VariableExpr edgeVariable = ambiguousEdgeDescriptor.getVariableExpr(); + PathPatternExpr canonicalPathPatternExpr = (PathPatternExpr) canonicalExpr; + List<EdgePatternExpr> canonicalEdges = canonicalPathPatternExpr.getEdgeExpressions(); + VertexPatternExpr canonicalLeftVertex = canonicalEdges.get(0).getLeftVertex(); + VertexPatternExpr canonicalRightVertex = canonicalEdges.get(canonicalEdges.size() - 1).getRightVertex(); + + // Iterate through our edge list. + List<EdgePatternExpr> edgeExpressions = pathPatternExpr.getEdgeExpressions(); + ListIterator<EdgePatternExpr> edgeIterator = edgeExpressions.listIterator(); + while (edgeIterator.hasNext()) { + EdgePatternExpr workingEdge = edgeIterator.next(); + if (workingEdge.equals(ambiguousEdgePattern)) { + edgeIterator.remove(); + + // We need to generate new variables for each edge in our new path. + List<EdgePatternExpr> canonicalEdgeListCopy = new ArrayList<>(); + for (EdgePatternExpr canonicalEdge : canonicalEdges) { + EdgePatternExpr canonicalEdgeCopy = deepCopyVisitor.visit(canonicalEdge, null); + EdgeDescriptor canonicalEdgeCopyDescriptor = canonicalEdgeCopy.getEdgeDescriptor(); + VariableExpr edgeVariableCopy = graphixRewritingContext.getGraphixVariableCopy(edgeVariable); + canonicalEdgeCopyDescriptor.setVariableExpr(edgeVariableCopy); + canonicalEdgeListCopy.add(canonicalEdgeCopy); + edgeIterator.add(canonicalEdgeCopy); + } + + // Determine our new vertex list (we want to keep the order of our current vertex list). + List<VertexPatternExpr> canonicalVertexListCopy = new ArrayList<>(); + ListIterator<VertexPatternExpr> pathPatternVertexIterator = + pathPatternExpr.getVertexExpressions().listIterator(); + while (pathPatternVertexIterator.hasNext()) { + VertexPatternExpr currentPathPatternVertex = pathPatternVertexIterator.next(); + VariableExpr currentVariable = currentPathPatternVertex.getVariableExpr(); + VariableExpr ambiguousLeftVar = ambiguousLeftVertex.getVariableExpr(); + VariableExpr ambiguousRightVar = ambiguousRightVertex.getVariableExpr(); + if (currentVariable.equals(ambiguousLeftVar)) { + // Add canonical path vertices. + List<VertexPatternExpr> canonicalVertices = canonicalPathPatternExpr.getVertexExpressions(); + for (VertexPatternExpr vertexExpr : canonicalVertices) { + canonicalVertexListCopy.add(deepCopyVisitor.visit(vertexExpr, null)); + VariableExpr vertexVar = vertexExpr.getVariableExpr(); + if (!vertexVar.equals(ambiguousLeftVar) && !vertexVar.equals(ambiguousRightVar)) { + pathPatternVertexIterator.add(vertexExpr); + } + } + } + } + + // Build a new path record. + RecordConstructor pathRecord = new RecordConstructor(); + pathRecord.setSourceLocation(workingEdge.getSourceLocation()); + buildPathRecord(canonicalVertexListCopy, canonicalEdgeListCopy, pathRecord); + LetClause pathBinding = new LetClause(deepCopyVisitor.visit(edgeVariable, null), pathRecord); + pathPatternExpr.getReboundSubPathList().add(pathBinding); + + } else { + if (workingEdge.getLeftVertex().equals(ambiguousLeftVertex)) { + workingEdge.setLeftVertex(deepCopyVisitor.visit(canonicalLeftVertex, null)); + } + if (workingEdge.getRightVertex().equals(ambiguousRightVertex)) { + workingEdge.setRightVertex(deepCopyVisitor.visit(canonicalRightVertex, null)); + } + } + } + + // Iterate through our vertex list. + replaceEdgeVerticesAndJoinCopiesInIterator(pathPatternExpr, ambiguousLeftVertex, ambiguousRightVertex, + canonicalLeftVertex, canonicalRightVertex); + } + } + + private void replaceEdgeVerticesAndJoinCopiesInIterator(PathPatternExpr pathPatternExpr, + VertexPatternExpr ambiguousLeftVertex, VertexPatternExpr ambiguousRightVertex, + VertexPatternExpr canonicalLeftVertex, VertexPatternExpr canonicalRightVertex) throws CompilationException { + List<VertexPatternExpr> vertexExpressions = pathPatternExpr.getVertexExpressions(); + ListIterator<VertexPatternExpr> vertexIterator = vertexExpressions.listIterator(); + Map<VertexPatternExpr, VertexPatternExpr> replacementVertexMap = new HashMap<>(); + replacementVertexMap.put(ambiguousLeftVertex, canonicalLeftVertex); + replacementVertexMap.put(ambiguousRightVertex, canonicalRightVertex); + for (VertexPatternExpr vertexPatternExpr : vertexExpressions) { + if (vertexPatternExpr.findHint(SubqueryVertexJoinAnnotation.class) != null) { + SubqueryVertexJoinAnnotation hint = vertexPatternExpr.findHint(SubqueryVertexJoinAnnotation.class); + if (hint.getSourceVertexVariable().equals(ambiguousLeftVertex.getVariableExpr())) { + VertexPatternExpr canonicalVertexCopy = deepCopyVisitor.visit(canonicalLeftVertex, null); + canonicalVertexCopy.setVariableExpr(vertexPatternExpr.getVariableExpr()); + replacementVertexMap.put(vertexPatternExpr, canonicalVertexCopy); + + } else if (hint.getSourceVertexVariable().equals(ambiguousRightVertex.getVariableExpr())) { + VertexPatternExpr canonicalVertexCopy = deepCopyVisitor.visit(canonicalRightVertex, null); + canonicalVertexCopy.setVariableExpr(vertexPatternExpr.getVariableExpr()); + replacementVertexMap.put(vertexPatternExpr, canonicalVertexCopy); + } + } + } + replaceVertexInIterator(replacementVertexMap, vertexIterator, deepCopyVisitor); + } +}
diff --git a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrite/canonical/CanonicalElementGeneratorFactory.java b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrite/canonical/CanonicalElementGeneratorFactory.java new file mode 100644 index 0000000..5720660 --- /dev/null +++ b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrite/canonical/CanonicalElementGeneratorFactory.java
@@ -0,0 +1,189 @@ +/* + * 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.graphix.lang.rewrite.canonical; + +import static org.apache.asterix.graphix.lang.rewrite.util.CanonicalElementUtil.deepCopyPathPattern; +import static org.apache.asterix.graphix.lang.rewrite.util.CanonicalElementUtil.expandEdgeDirection; +import static org.apache.asterix.graphix.lang.rewrite.util.CanonicalElementUtil.expandFixedPathPattern; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.Set; + +import org.apache.asterix.common.exceptions.CompilationException; +import org.apache.asterix.graphix.lang.expression.EdgePatternExpr; +import org.apache.asterix.graphix.lang.expression.PathPatternExpr; +import org.apache.asterix.graphix.lang.expression.VertexPatternExpr; +import org.apache.asterix.graphix.lang.rewrite.GraphixRewritingContext; +import org.apache.asterix.graphix.lang.rewrite.resolve.IInternalVertexSupplier; +import org.apache.asterix.graphix.lang.rewrite.resolve.SchemaKnowledgeTable; +import org.apache.asterix.graphix.lang.rewrite.visitor.GraphixDeepCopyVisitor; +import org.apache.asterix.graphix.lang.struct.EdgeDescriptor; +import org.apache.asterix.graphix.lang.struct.EdgeDescriptor.EdgeDirection; +import org.apache.asterix.graphix.lang.struct.ElementLabel; +import org.apache.asterix.lang.common.expression.VariableExpr; + +/** + * Generate a list of canonical {@link VertexPatternExpr}, {@link EdgePatternExpr}, or {@link PathPatternExpr} + * instances, given input {@link VertexPatternExpr} or {@link EdgePatternExpr}. We expect the input elements to have + * all unknowns resolved-- this pass is to generate canonical elements with the knowledge that each label / direction + * is possible according to our graph schema. + */ +public class CanonicalElementGeneratorFactory { + private final GraphixDeepCopyVisitor deepCopyVisitor; + private final SchemaKnowledgeTable schemaKnowledgeTable; + private final GraphixRewritingContext graphixRewritingContext; + + public CanonicalElementGeneratorFactory(GraphixRewritingContext graphixRewritingContext, + SchemaKnowledgeTable schemaKnowledgeTable) { + this.deepCopyVisitor = new GraphixDeepCopyVisitor(); + this.graphixRewritingContext = graphixRewritingContext; + this.schemaKnowledgeTable = schemaKnowledgeTable; + } + + public List<VertexPatternExpr> generateCanonicalVertices(VertexPatternExpr vertexPatternExpr) + throws CompilationException { + List<VertexPatternExpr> canonicalVertexList = new ArrayList<>(); + for (ElementLabel elementLabel : vertexPatternExpr.getLabels()) { + VertexPatternExpr vertexCopy = deepCopyVisitor.visit(vertexPatternExpr, null); + vertexCopy.getLabels().clear(); + vertexCopy.getLabels().add(elementLabel); + canonicalVertexList.add(vertexCopy); + } + return canonicalVertexList; + } + + public List<EdgePatternExpr> generateCanonicalEdges(EdgePatternExpr edgePatternExpr) throws CompilationException { + EdgeDescriptor edgeDescriptor = edgePatternExpr.getEdgeDescriptor(); + VertexPatternExpr leftVertex = edgePatternExpr.getLeftVertex(); + VertexPatternExpr rightVertex = edgePatternExpr.getRightVertex(); + + // Each variable below represents a loop nesting. + Set<ElementLabel> edgeLabelList = edgeDescriptor.getEdgeLabels(); + Set<ElementLabel> leftLabelList = leftVertex.getLabels(); + Set<ElementLabel> rightLabelList = rightVertex.getLabels(); + Set<EdgeDirection> directionList = expandEdgeDirection(edgeDescriptor); + + List<EdgePatternExpr> canonicalEdgeList = new ArrayList<>(); + for (ElementLabel edgeLabel : edgeLabelList) { + for (ElementLabel leftLabel : leftLabelList) { + for (ElementLabel rightLabel : rightLabelList) { + for (EdgeDirection edgeDirection : directionList) { + + // Generate an edge according to our loop parameters. + EdgePatternExpr edgeCopy = deepCopyVisitor.visit(edgePatternExpr, null); + edgeCopy.getLeftVertex().getLabels().clear(); + edgeCopy.getRightVertex().getLabels().clear(); + edgeCopy.getEdgeDescriptor().getEdgeLabels().clear(); + edgeCopy.getLeftVertex().getLabels().add(leftLabel); + edgeCopy.getRightVertex().getLabels().add(rightLabel); + edgeCopy.getEdgeDescriptor().getEdgeLabels().add(edgeLabel); + edgeCopy.getEdgeDescriptor().setEdgeDirection(edgeDirection); + + // If we have a valid edge, insert this into our list. + if (schemaKnowledgeTable.isValidEdge(edgeCopy)) { + canonicalEdgeList.add(edgeCopy); + } + } + } + } + } + return canonicalEdgeList; + } + + public List<PathPatternExpr> generateCanonicalPaths(EdgePatternExpr edgePatternExpr, + boolean minimizeExpansionLength) throws CompilationException { + EdgeDescriptor edgeDescriptor = edgePatternExpr.getEdgeDescriptor(); + VertexPatternExpr leftVertex = edgePatternExpr.getLeftVertex(); + VertexPatternExpr rightVertex = edgePatternExpr.getRightVertex(); + VertexPatternExpr internalVertex = edgePatternExpr.getInternalVertex(); + + // Each variable below represents a loop nesting. + Set<ElementLabel> edgeLabelList = edgeDescriptor.getEdgeLabels(); + Set<ElementLabel> leftLabelList = leftVertex.getLabels(); + Set<ElementLabel> rightLabelList = rightVertex.getLabels(); + Set<EdgeDirection> directionList = expandEdgeDirection(edgeDescriptor); + Set<ElementLabel> internalLabelList = internalVertex.getLabels(); + + // Determine the length of **expanded** path. For {N,M} w/ E labels... MIN(M, (1 + 2(E-1))). + int minimumExpansionLength, expansionLength; + if (minimizeExpansionLength) { + int maximumExpansionLength = 1 + 2 * (edgeLabelList.size() - 1); + minimumExpansionLength = Objects.requireNonNullElse(edgeDescriptor.getMinimumHops(), 1); + expansionLength = Objects.requireNonNullElse(edgeDescriptor.getMaximumHops(), maximumExpansionLength); + if (minimumExpansionLength > expansionLength) { + minimumExpansionLength = expansionLength; + } + + } else { + minimumExpansionLength = Objects.requireNonNullElse(edgeDescriptor.getMinimumHops(), 1); + expansionLength = edgeDescriptor.getMaximumHops(); + } + + // Generate all valid paths, from minimumExpansionLength to maximumExpansionLength. + List<List<EdgePatternExpr>> canonicalPathList = new ArrayList<>(); + IInternalVertexSupplier internalVertexSupplier = () -> { + VertexPatternExpr internalVertexCopy = deepCopyVisitor.visit(internalVertex, null); + VariableExpr internalVariable = internalVertex.getVariableExpr(); + VariableExpr internalVariableCopy = graphixRewritingContext.getGraphixVariableCopy(internalVariable); + internalVertexCopy.setVariableExpr(internalVariableCopy); + return internalVertexCopy; + }; + for (int pathLength = minimumExpansionLength; pathLength <= expansionLength; pathLength++) { + List<List<EdgePatternExpr>> expandedPathPattern = expandFixedPathPattern(pathLength, edgePatternExpr, + edgeLabelList, internalLabelList, directionList, deepCopyVisitor, internalVertexSupplier); + for (List<EdgePatternExpr> pathPattern : expandedPathPattern) { + for (ElementLabel leftLabel : leftLabelList) { + for (ElementLabel rightLabel : rightLabelList) { + List<EdgePatternExpr> pathCopy = deepCopyPathPattern(pathPattern, deepCopyVisitor); + + // Set the labels of our leftmost vertex... + pathCopy.get(0).getLeftVertex().getLabels().clear(); + pathCopy.get(0).getLeftVertex().getLabels().add(leftLabel); + + // ...and our rightmost vertex. + pathCopy.get(pathCopy.size() - 1).getRightVertex().getLabels().clear(); + pathCopy.get(pathCopy.size() - 1).getRightVertex().getLabels().add(rightLabel); + + // Add our path if all edges are valid. + if (pathCopy.stream().allMatch(schemaKnowledgeTable::isValidEdge)) { + canonicalPathList.add(pathCopy); + } + } + } + } + } + + // Wrap our edge lists into path patterns. + List<PathPatternExpr> pathPatternList = new ArrayList<>(); + for (List<EdgePatternExpr> edgeList : canonicalPathList) { + List<VertexPatternExpr> vertexList = new ArrayList<>(); + edgeList.forEach(e -> { + vertexList.add(e.getLeftVertex()); + vertexList.add(e.getRightVertex()); + }); + VariableExpr variableExpr = deepCopyVisitor.visit(edgeDescriptor.getVariableExpr(), null); + PathPatternExpr pathPatternExpr = new PathPatternExpr(vertexList, edgeList, variableExpr); + pathPatternExpr.setSourceLocation(edgePatternExpr.getSourceLocation()); + pathPatternList.add(pathPatternExpr); + } + return pathPatternList; + } +}
diff --git a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/canonical/ICanonicalExpander.java b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrite/canonical/ICanonicalElementConsumer.java similarity index 74% rename from asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/canonical/ICanonicalExpander.java rename to asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrite/canonical/ICanonicalElementConsumer.java index 65f42e5..2392423 100644 --- a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/canonical/ICanonicalExpander.java +++ b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrite/canonical/ICanonicalElementConsumer.java
@@ -16,14 +16,15 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.asterix.graphix.lang.rewrites.canonical; +package org.apache.asterix.graphix.lang.rewrite.canonical; import java.util.List; import org.apache.asterix.common.exceptions.CompilationException; -import org.apache.asterix.graphix.lang.clause.GraphSelectBlock; +import org.apache.asterix.lang.common.base.AbstractExpression; @FunctionalInterface -public interface ICanonicalExpander<T> { - void apply(T patternExpr, List<GraphSelectBlock> inputSelectBlocks) throws CompilationException; +public interface ICanonicalElementConsumer { + void accept(AbstractExpression ambiguousElement, List<? extends AbstractExpression> canonicalElements) + throws CompilationException; }
diff --git a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrite/common/BranchLookupTable.java b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrite/common/BranchLookupTable.java new file mode 100644 index 0000000..a21e04a --- /dev/null +++ b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrite/common/BranchLookupTable.java
@@ -0,0 +1,49 @@ +/* + * 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.graphix.lang.rewrite.common; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.apache.asterix.graphix.lang.expression.EdgePatternExpr; +import org.apache.asterix.graphix.lang.expression.PathPatternExpr; + +/** + * Lookup table for branch (i.e. partial {@link EdgePatternExpr} instances that define FA transition functions)-- + * indexed by {@link EdgePatternExpr} instances. + */ +public class BranchLookupTable { + private final Map<EdgePatternExpr, List<EdgePatternExpr>> branchEdgeMap = new HashMap<>(); + + public void putBranch(EdgePatternExpr associatedEdge, EdgePatternExpr branch) { + branchEdgeMap.putIfAbsent(associatedEdge, new ArrayList<>()); + branchEdgeMap.get(associatedEdge).add(branch); + } + + public void putBranch(EdgePatternExpr associatedEdge, PathPatternExpr branch) { + branchEdgeMap.putIfAbsent(associatedEdge, new ArrayList<>()); + branchEdgeMap.get(associatedEdge).addAll(branch.getEdgeExpressions()); + } + + public List<EdgePatternExpr> getBranches(EdgePatternExpr associatedEdge) { + return branchEdgeMap.get(associatedEdge); + } +}
diff --git a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrite/common/ElementLookupTable.java b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrite/common/ElementLookupTable.java new file mode 100644 index 0000000..bf1eb62 --- /dev/null +++ b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrite/common/ElementLookupTable.java
@@ -0,0 +1,75 @@ +/* + * 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.graphix.lang.rewrite.common; + +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import org.apache.asterix.graphix.common.metadata.EdgeIdentifier; +import org.apache.asterix.graphix.common.metadata.IElementIdentifier; +import org.apache.asterix.graphix.common.metadata.VertexIdentifier; +import org.apache.asterix.graphix.lang.statement.GraphElementDeclaration; + +/** + * Lookup table for {@link GraphElementDeclaration} instances, vertex keys, edge destination keys, and edge source + * keys-- indexed by {@link IElementIdentifier} instances. + */ +public class ElementLookupTable implements Iterable<GraphElementDeclaration> { + private final Map<IElementIdentifier, GraphElementDeclaration> graphElementDeclMap = new HashMap<>(); + private final Map<VertexIdentifier, List<List<String>>> vertexKeyMap = new HashMap<>(); + private final Map<EdgeIdentifier, List<List<String>>> edgeDestKeysMap = new HashMap<>(); + private final Map<EdgeIdentifier, List<List<String>>> edgeSourceKeysMap = new HashMap<>(); + + public void put(IElementIdentifier identifier, GraphElementDeclaration graphElementDeclaration) { + graphElementDeclMap.put(identifier, graphElementDeclaration); + } + + public void putVertexKey(VertexIdentifier identifier, List<List<String>> primaryKey) { + vertexKeyMap.put(identifier, primaryKey); + } + + public void putEdgeKeys(EdgeIdentifier identifier, List<List<String>> sourceKey, + List<List<String>> destinationKey) { + edgeSourceKeysMap.put(identifier, sourceKey); + edgeDestKeysMap.put(identifier, destinationKey); + } + + public GraphElementDeclaration getElementDecl(IElementIdentifier identifier) { + return graphElementDeclMap.get(identifier); + } + + public List<List<String>> getVertexKey(VertexIdentifier identifier) { + return vertexKeyMap.get(identifier); + } + + public List<List<String>> getEdgeDestKey(EdgeIdentifier identifier) { + return edgeDestKeysMap.get(identifier); + } + + public List<List<String>> getEdgeSourceKey(EdgeIdentifier identifier) { + return edgeSourceKeysMap.get(identifier); + } + + @Override + public Iterator<GraphElementDeclaration> iterator() { + return graphElementDeclMap.values().iterator(); + } +}
diff --git a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrite/lower/AliasLookupTable.java b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrite/lower/AliasLookupTable.java new file mode 100644 index 0000000..d16e85d --- /dev/null +++ b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrite/lower/AliasLookupTable.java
@@ -0,0 +1,62 @@ +/* + * 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.graphix.lang.rewrite.lower; + +import java.util.HashMap; +import java.util.Map; + +import org.apache.asterix.common.exceptions.CompilationException; +import org.apache.asterix.graphix.lang.rewrite.visitor.GraphixDeepCopyVisitor; +import org.apache.asterix.lang.common.expression.VariableExpr; + +/** + * Lookup table for JOIN and ITERATION aliases, indexed by their representative (i.e. element) variables. + */ +public class AliasLookupTable { + private final GraphixDeepCopyVisitor deepCopyVisitor = new GraphixDeepCopyVisitor(); + private final Map<VariableExpr, VariableExpr> joinAliasMap = new HashMap<>(); + private final Map<VariableExpr, VariableExpr> iterationAliasMap = new HashMap<>(); + + public void addJoinAlias(VariableExpr elementVariable, VariableExpr aliasVariable) { + joinAliasMap.put(elementVariable, aliasVariable); + } + + public void addIterationAlias(VariableExpr elementVariable, VariableExpr aliasVariable) { + iterationAliasMap.put(elementVariable, aliasVariable); + } + + public VariableExpr getJoinAlias(VariableExpr elementVariable) throws CompilationException { + if (joinAliasMap.containsKey(elementVariable)) { + return deepCopyVisitor.visit(joinAliasMap.get(elementVariable), null); + } + return null; + } + + public VariableExpr getIterationAlias(VariableExpr elementVariable) throws CompilationException { + if (iterationAliasMap.containsKey(elementVariable)) { + return deepCopyVisitor.visit(iterationAliasMap.get(elementVariable), null); + } + return null; + } + + public void reset() { + joinAliasMap.clear(); + iterationAliasMap.clear(); + } +}
diff --git a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrite/lower/EnvironmentActionFactory.java b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrite/lower/EnvironmentActionFactory.java new file mode 100644 index 0000000..66f3f0e --- /dev/null +++ b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrite/lower/EnvironmentActionFactory.java
@@ -0,0 +1,692 @@ +/* + * 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.graphix.lang.rewrite.lower; + +import static org.apache.asterix.graphix.lang.rewrite.util.LowerRewritingUtil.buildAccessorList; +import static org.apache.asterix.graphix.lang.rewrite.util.LowerRewritingUtil.buildVertexEdgeJoin; + +import java.util.List; +import java.util.Map; +import java.util.function.Function; + +import org.apache.asterix.common.exceptions.CompilationException; +import org.apache.asterix.common.exceptions.ErrorCode; +import org.apache.asterix.graphix.common.metadata.EdgeIdentifier; +import org.apache.asterix.graphix.common.metadata.GraphIdentifier; +import org.apache.asterix.graphix.common.metadata.IElementIdentifier; +import org.apache.asterix.graphix.common.metadata.VertexIdentifier; +import org.apache.asterix.graphix.lang.annotation.LoweringExemptAnnotation; +import org.apache.asterix.graphix.lang.clause.FromGraphClause; +import org.apache.asterix.graphix.lang.expression.EdgePatternExpr; +import org.apache.asterix.graphix.lang.expression.PathPatternExpr; +import org.apache.asterix.graphix.lang.expression.VertexPatternExpr; +import org.apache.asterix.graphix.lang.rewrite.GraphixRewritingContext; +import org.apache.asterix.graphix.lang.rewrite.common.ElementLookupTable; +import org.apache.asterix.graphix.lang.rewrite.lower.action.AbstractInlineAction; +import org.apache.asterix.graphix.lang.rewrite.lower.action.IEnvironmentAction; +import org.apache.asterix.graphix.lang.rewrite.lower.action.MatchSemanticAction; +import org.apache.asterix.graphix.lang.rewrite.lower.action.PathPatternAction; +import org.apache.asterix.graphix.lang.rewrite.visitor.ElementBodyAnalysisVisitor.ElementBodyAnalysisContext; +import org.apache.asterix.graphix.lang.rewrite.visitor.GraphixDeepCopyVisitor; +import org.apache.asterix.graphix.lang.rewrite.visitor.VariableRemapCloneVisitor; +import org.apache.asterix.graphix.lang.statement.GraphElementDeclaration; +import org.apache.asterix.lang.common.base.Expression; +import org.apache.asterix.lang.common.base.ILangExpression; +import org.apache.asterix.lang.common.clause.LetClause; +import org.apache.asterix.lang.common.clause.WhereClause; +import org.apache.asterix.lang.common.expression.CallExpr; +import org.apache.asterix.lang.common.expression.LiteralExpr; +import org.apache.asterix.lang.common.expression.RecordConstructor; +import org.apache.asterix.lang.common.expression.VariableExpr; +import org.apache.asterix.lang.common.literal.TrueLiteral; +import org.apache.asterix.lang.sqlpp.clause.JoinClause; +import org.apache.asterix.lang.sqlpp.optype.JoinType; +import org.apache.asterix.lang.sqlpp.util.SqlppRewriteUtil; + +/** + * Build {@link IEnvironmentAction} instances to manipulate a {@link LoweringEnvironment}. + */ +public class EnvironmentActionFactory { + private final Map<IElementIdentifier, ElementBodyAnalysisContext> analysisContextMap; + private final ElementLookupTable elementLookupTable; + private final AliasLookupTable aliasLookupTable; + private final GraphixRewritingContext graphixRewritingContext; + private final VariableRemapCloneVisitor remapCloneVisitor; + private final GraphixDeepCopyVisitor graphixDeepCopyVisitor; + + // The following must be provided before any creation methods are used. + private GraphIdentifier graphIdentifier; + + public EnvironmentActionFactory(Map<IElementIdentifier, ElementBodyAnalysisContext> analysisContextMap, + ElementLookupTable elementLookupTable, AliasLookupTable aliasLookupTable, + GraphixRewritingContext graphixRewritingContext) { + this.analysisContextMap = analysisContextMap; + this.elementLookupTable = elementLookupTable; + this.aliasLookupTable = aliasLookupTable; + this.graphixRewritingContext = graphixRewritingContext; + this.graphixDeepCopyVisitor = new GraphixDeepCopyVisitor(); + this.remapCloneVisitor = new VariableRemapCloneVisitor(graphixRewritingContext); + } + + public void reset(GraphIdentifier graphIdentifier) { + this.aliasLookupTable.reset(); + this.graphIdentifier = graphIdentifier; + } + + /** + * @see PathPatternAction + */ + public IEnvironmentAction buildPathPatternAction(PathPatternExpr pathPatternExpr) { + return new PathPatternAction(pathPatternExpr); + } + + /** + * @see MatchSemanticAction + */ + public IEnvironmentAction buildMatchSemanticAction(FromGraphClause fromGraphClause) throws CompilationException { + return new MatchSemanticAction(graphixRewritingContext, fromGraphClause, aliasLookupTable); + } + + /** + * Build an {@link IEnvironmentAction} to introduce a WHERE clause into an environment with the given expression. + */ + public IEnvironmentAction buildFilterExprAction(Expression filterExpr, VariableExpr elementVariable, + VariableExpr iterationVariable) throws CompilationException { + VariableExpr iterationVarCopy = graphixDeepCopyVisitor.visit(iterationVariable, null); + VariableExpr elementVarCopy = graphixDeepCopyVisitor.visit(elementVariable, null); + iterationVarCopy.setSourceLocation(filterExpr.getSourceLocation()); + remapCloneVisitor.addSubstitution(elementVarCopy, iterationVarCopy); + return loweringEnvironment -> loweringEnvironment.acceptTransformer(lowerList -> { + ILangExpression remapCopyFilterExpr = remapCloneVisitor.substitute(filterExpr); + WhereClause filterWhereClause = new WhereClause((Expression) remapCopyFilterExpr); + filterWhereClause.setSourceLocation(filterExpr.getSourceLocation()); + lowerList.addNonRepresentativeClause(filterWhereClause); + remapCloneVisitor.resetSubstitutions(); + }); + } + + /** + * Build an {@link IEnvironmentAction} to handle a dangling vertex / vertex that is (currently) disconnected. + * Even though we introduce CROSS-JOINs here, we will not actually perform this CROSS-JOIN if this is the first + * vertex we are lowering. There are three possible {@link IEnvironmentAction}s generated here: + * <ul> + * <li>An action for inlined vertices that have no projections.</li> + * <li>An action for inlined vertices with projections.</li> + * <li>An action for non-inlined vertices.</li> + * </ul> + */ + public IEnvironmentAction buildDanglingVertexAction(VertexPatternExpr vertexPatternExpr) + throws CompilationException { + if (vertexPatternExpr.findHint(LoweringExemptAnnotation.class) == LoweringExemptAnnotation.INSTANCE) { + return loweringEnvironment -> { + }; + } + VariableExpr vertexVar = vertexPatternExpr.getVariableExpr(); + VariableExpr iterationVar = graphixRewritingContext.getGraphixVariableCopy(vertexVar); + VariableExpr intermediateVar = graphixRewritingContext.getGraphixVariableCopy(vertexVar); + + // We should only be working with one identifier (given that we only have one label). + List<VertexIdentifier> vertexElementIDs = vertexPatternExpr.generateIdentifiers(graphIdentifier); + if (vertexElementIDs.size() != 1) { + throw new CompilationException(ErrorCode.COMPILATION_ILLEGAL_STATE, "Found non-canonical vertex pattern!"); + } + VertexIdentifier vertexIdentifier = vertexElementIDs.get(0); + ElementBodyAnalysisContext vertexAnalysisContext = analysisContextMap.get(vertexIdentifier); + if (vertexAnalysisContext.isExpressionInline() && vertexAnalysisContext.isSelectClauseInline()) { + return new AbstractInlineAction(graphixRewritingContext, vertexAnalysisContext, iterationVar) { + @Override + public void apply(LoweringEnvironment loweringEnvironment) throws CompilationException { + // Introduce our iteration expression. + loweringEnvironment.acceptTransformer(lowerList -> { + CallExpr datasetCallExpression = vertexAnalysisContext.getDatasetCallExpression(); + VariableExpr iterationVarCopy = graphixDeepCopyVisitor.visit(iterationVar, null); + JoinClause joinClause = new JoinClause(JoinType.INNER, datasetCallExpression, iterationVarCopy, + null, new LiteralExpr(TrueLiteral.INSTANCE), null); + joinClause.setSourceLocation(vertexPatternExpr.getSourceLocation()); + lowerList.addNonRepresentativeClause(joinClause); + }); + + // Inline our vertex body. + super.apply(loweringEnvironment); + + // If we have a filter expression, add it as a WHERE clause here. + final Expression filterExpr = vertexPatternExpr.getFilterExpr(); + if (filterExpr != null) { + loweringEnvironment.acceptAction(buildFilterExprAction(filterExpr, vertexVar, iterationVar)); + } + + // Bind our intermediate (join) variable and vertex variable. + loweringEnvironment.acceptTransformer(lowerList -> { + VariableExpr iterationVarCopy1 = graphixDeepCopyVisitor.visit(iterationVar, null); + VariableExpr iterationVarCopy2 = graphixDeepCopyVisitor.visit(iterationVar, null); + VariableExpr intermediateVarCopy = graphixDeepCopyVisitor.visit(intermediateVar, null); + VariableExpr vertexVarCopy = graphixDeepCopyVisitor.visit(vertexVar, null); + LetClause nonRepresentativeBinding = new LetClause(intermediateVarCopy, iterationVarCopy1); + lowerList.addNonRepresentativeClause(nonRepresentativeBinding); + lowerList.addVertexBinding(vertexVarCopy, iterationVarCopy2); + }); + aliasLookupTable.addIterationAlias(vertexVar, iterationVar); + aliasLookupTable.addJoinAlias(vertexVar, intermediateVar); + } + }; + + } else if (vertexAnalysisContext.isExpressionInline()) { + return new AbstractInlineAction(graphixRewritingContext, vertexAnalysisContext, iterationVar) { + @Override + public void apply(LoweringEnvironment loweringEnvironment) throws CompilationException { + // Introduce our iteration expression. + loweringEnvironment.acceptTransformer(lowerList -> { + CallExpr datasetCallExpression = vertexAnalysisContext.getDatasetCallExpression(); + VariableExpr iterationVarCopy = graphixDeepCopyVisitor.visit(iterationVar, null); + JoinClause joinClause = new JoinClause(JoinType.INNER, datasetCallExpression, iterationVarCopy, + null, new LiteralExpr(TrueLiteral.INSTANCE), null); + joinClause.setSourceLocation(vertexPatternExpr.getSourceLocation()); + lowerList.addNonRepresentativeClause(joinClause); + }); + + // Inline our vertex body. + super.apply(loweringEnvironment); + + // If we have a filter expression, add it as a WHERE clause here. + final Expression filterExpr = vertexPatternExpr.getFilterExpr(); + if (filterExpr != null) { + loweringEnvironment.acceptAction(buildFilterExprAction(filterExpr, vertexVar, iterationVar)); + } + + // Build a record constructor from our context to bind to our vertex variable. + loweringEnvironment.acceptTransformer(lowerList -> { + VariableExpr intermediateVarCopy = graphixDeepCopyVisitor.visit(intermediateVar, null); + RecordConstructor recordConstructor1 = buildRecordConstructor(); + RecordConstructor recordConstructor2 = buildRecordConstructor(); + LetClause nonRepresentativeBinding = new LetClause(intermediateVarCopy, recordConstructor1); + lowerList.addNonRepresentativeClause(nonRepresentativeBinding); + lowerList.addVertexBinding(vertexVar, recordConstructor2); + }); + aliasLookupTable.addIterationAlias(vertexVar, iterationVar); + aliasLookupTable.addJoinAlias(vertexVar, intermediateVar); + } + }; + + } else { + GraphElementDeclaration elementDeclaration = elementLookupTable.getElementDecl(vertexIdentifier); + return loweringEnvironment -> { + // Introduce our iteration expression. + loweringEnvironment.acceptTransformer(lowerList -> { + ILangExpression declBodyCopy = SqlppRewriteUtil.deepCopy(elementDeclaration.getNormalizedBody()); + VariableExpr iterationVarCopy = graphixDeepCopyVisitor.visit(iterationVar, null); + JoinClause joinClause = new JoinClause(JoinType.INNER, (Expression) declBodyCopy, iterationVarCopy, + null, new LiteralExpr(TrueLiteral.INSTANCE), null); + joinClause.setSourceLocation(vertexPatternExpr.getSourceLocation()); + lowerList.addNonRepresentativeClause(joinClause); + }); + + // If we have a filter expression, add it as a WHERE clause here. + final Expression filterExpr = vertexPatternExpr.getFilterExpr(); + if (filterExpr != null) { + loweringEnvironment.acceptAction(buildFilterExprAction(filterExpr, vertexVar, iterationVar)); + } + + // Bind our intermediate (join) variable and vertex variable. + loweringEnvironment.acceptTransformer(lowerList -> { + VariableExpr iterationVarCopy1 = graphixDeepCopyVisitor.visit(iterationVar, null); + VariableExpr iterationVarCopy2 = graphixDeepCopyVisitor.visit(iterationVar, null); + VariableExpr intermediateVarCopy = graphixDeepCopyVisitor.visit(intermediateVar, null); + LetClause nonRepresentativeBinding = new LetClause(intermediateVarCopy, iterationVarCopy1); + lowerList.addNonRepresentativeClause(nonRepresentativeBinding); + lowerList.addVertexBinding(vertexVar, iterationVarCopy2); + }); + aliasLookupTable.addIterationAlias(vertexVar, iterationVar); + aliasLookupTable.addJoinAlias(vertexVar, intermediateVar); + }; + } + } + + /** + * Build an {@link IEnvironmentAction} to handle an edge that we can fold into (attach from) an already introduced + * vertex. A folded edge is implicitly inlined. There are two possible {@link IEnvironmentAction}s generated here: + * <ul> + * <li>An action for inlined, folded edges that have no projections.</li> + * <li>An action for inlined, folded edges that have projections.</li> + * </ul> + */ + public IEnvironmentAction buildFoldedEdgeAction(VertexPatternExpr vertexPatternExpr, + EdgePatternExpr edgePatternExpr) throws CompilationException { + VariableExpr vertexVar = vertexPatternExpr.getVariableExpr(); + VariableExpr edgeVar = edgePatternExpr.getEdgeDescriptor().getVariableExpr(); + VariableExpr intermediateVar = graphixRewritingContext.getGraphixVariableCopy(edgeVar); + + // We should only be working with one identifier (given that we only have one label). + List<EdgeIdentifier> edgeElementIDs = edgePatternExpr.generateIdentifiers(graphIdentifier); + if (edgeElementIDs.size() != 1) { + throw new CompilationException(ErrorCode.COMPILATION_ILLEGAL_STATE, "Found non-canonical edge pattern!"); + } + EdgeIdentifier edgeIdentifier = edgeElementIDs.get(0); + ElementBodyAnalysisContext edgeAnalysisContext = analysisContextMap.get(edgeIdentifier); + if (edgeAnalysisContext.isSelectClauseInline()) { + return new AbstractInlineAction(graphixRewritingContext, edgeAnalysisContext, null) { + @Override + public void apply(LoweringEnvironment loweringEnvironment) throws CompilationException { + // We want to bind directly to the iteration variable of our vertex, not the join variable. + elementVariable = aliasLookupTable.getIterationAlias(vertexVar); + + // Inline our edge body. + super.apply(loweringEnvironment); + + // If we have a filter expression, add it as a WHERE clause here. + final Expression filterExpr = edgePatternExpr.getEdgeDescriptor().getFilterExpr(); + if (filterExpr != null) { + loweringEnvironment.acceptAction(buildFilterExprAction(filterExpr, edgeVar, elementVariable)); + } + + // Build a binding for our edge variable. + loweringEnvironment.acceptTransformer(lowerList -> { + VariableExpr elementVarCopy1 = graphixDeepCopyVisitor.visit(elementVariable, null); + VariableExpr elementVarCopy2 = graphixDeepCopyVisitor.visit(elementVariable, null); + VariableExpr intermediateVarCopy = graphixDeepCopyVisitor.visit(intermediateVar, null); + LetClause nonRepresentativeBinding = new LetClause(intermediateVarCopy, elementVarCopy1); + lowerList.addNonRepresentativeClause(nonRepresentativeBinding); + lowerList.addEdgeBinding(edgeVar, elementVarCopy2); + }); + aliasLookupTable.addIterationAlias(edgeVar, elementVariable); + aliasLookupTable.addJoinAlias(edgeVar, intermediateVar); + } + }; + + } else { + return new AbstractInlineAction(graphixRewritingContext, edgeAnalysisContext, null) { + @Override + public void apply(LoweringEnvironment loweringEnvironment) throws CompilationException { + // We want to bind directly to the iteration variable of our vertex, not the join variable. + elementVariable = aliasLookupTable.getIterationAlias(vertexVar); + + // Inline our edge body. + super.apply(loweringEnvironment); + + // If we have a filter expression, add it as a WHERE clause here. + final Expression filterExpr = edgePatternExpr.getEdgeDescriptor().getFilterExpr(); + if (filterExpr != null) { + loweringEnvironment.acceptAction(buildFilterExprAction(filterExpr, edgeVar, elementVariable)); + } + + // Build a record constructor from our context to bind to our edge and intermediate (join) var. + loweringEnvironment.acceptTransformer(lowerList -> { + VariableExpr intermediateVarCopy = graphixDeepCopyVisitor.visit(intermediateVar, null); + RecordConstructor recordConstructor1 = buildRecordConstructor(); + RecordConstructor recordConstructor2 = buildRecordConstructor(); + LetClause nonRepresentativeBinding = new LetClause(intermediateVarCopy, recordConstructor1); + lowerList.addNonRepresentativeClause(nonRepresentativeBinding); + lowerList.addEdgeBinding(edgeVar, recordConstructor2); + }); + aliasLookupTable.addIterationAlias(edgeVar, elementVariable); + aliasLookupTable.addJoinAlias(edgeVar, intermediateVar); + } + }; + } + } + + /** + * Build an {@link IEnvironmentAction} to handle an edge that we cannot fold into an already introduced vertex. + * There are three possible {@link IEnvironmentAction}s generated here: + * <ul> + * <li>An action for inlined edges that have no projections.</li> + * <li>An action for inlined edges that have projections.</li> + * <li>An action for non-inlined edges.</li> + * </ul> + */ + public IEnvironmentAction buildNonFoldedEdgeAction(VertexPatternExpr vertexPatternExpr, + EdgePatternExpr edgePatternExpr, Function<EdgeIdentifier, List<List<String>>> edgeKeyAccess) + throws CompilationException { + VariableExpr edgeVar = edgePatternExpr.getEdgeDescriptor().getVariableExpr(); + VariableExpr vertexVar = vertexPatternExpr.getVariableExpr(); + VariableExpr iterationVar = graphixRewritingContext.getGraphixVariableCopy(edgeVar); + VariableExpr intermediateVar = graphixRewritingContext.getGraphixVariableCopy(edgeVar); + + // We should only be working with one edge identifier... + List<EdgeIdentifier> edgeElementIDs = edgePatternExpr.generateIdentifiers(graphIdentifier); + if (edgeElementIDs.size() != 1) { + throw new CompilationException(ErrorCode.COMPILATION_ILLEGAL_STATE, "Found non-canonical edge pattern!"); + } + EdgeIdentifier edgeIdentifier = edgeElementIDs.get(0); + ElementBodyAnalysisContext edgeAnalysisContext = analysisContextMap.get(edgeIdentifier); + Expression datasetCallExpression = edgeAnalysisContext.getDatasetCallExpression(); + + // ...and only one vertex identifier (given that we only have one label). + List<VertexIdentifier> vertexElementIDs = vertexPatternExpr.generateIdentifiers(graphIdentifier); + if (vertexElementIDs.size() != 1) { + throw new CompilationException(ErrorCode.COMPILATION_ILLEGAL_STATE, "Found non-canonical vertex pattern!"); + } + VertexIdentifier vertexIdentifier = vertexElementIDs.get(0); + if (edgeAnalysisContext.isExpressionInline() && edgeAnalysisContext.isSelectClauseInline()) { + return new AbstractInlineAction(graphixRewritingContext, edgeAnalysisContext, iterationVar) { + @Override + public void apply(LoweringEnvironment loweringEnvironment) throws CompilationException { + // Join our edge iteration variable to our vertex variable. + loweringEnvironment.acceptTransformer(lowerList -> { + VariableExpr vertexJoinExpr = aliasLookupTable.getJoinAlias(vertexVar); + VariableExpr vertexVarCopy = graphixDeepCopyVisitor.visit(vertexJoinExpr, null); + VariableExpr iterationVarCopy1 = graphixDeepCopyVisitor.visit(iterationVar, null); + VariableExpr iterationVarCopy2 = graphixDeepCopyVisitor.visit(iterationVar, null); + Expression vertexEdgeJoin = buildVertexEdgeJoin( + buildAccessorList(vertexVarCopy, elementLookupTable.getVertexKey(vertexIdentifier)), + buildAccessorList(iterationVarCopy1, edgeKeyAccess.apply(edgeIdentifier))); + JoinClause joinClause = new JoinClause(JoinType.INNER, datasetCallExpression, iterationVarCopy2, + null, vertexEdgeJoin, null); + joinClause.setSourceLocation(edgePatternExpr.getSourceLocation()); + lowerList.addNonRepresentativeClause(joinClause); + }); + + // Inline our edge body. + super.apply(loweringEnvironment); + + // If we have a filter expression, add it as a WHERE clause here. + final Expression filterExpr = edgePatternExpr.getEdgeDescriptor().getFilterExpr(); + if (filterExpr != null) { + loweringEnvironment.acceptAction(buildFilterExprAction(filterExpr, edgeVar, iterationVar)); + } + + // Bind our intermediate (join) variable and edge variable. + loweringEnvironment.acceptTransformer(lowerList -> { + VariableExpr iterationVarCopy1 = graphixDeepCopyVisitor.visit(iterationVar, null); + VariableExpr iterationVarCopy2 = graphixDeepCopyVisitor.visit(iterationVar, null); + VariableExpr intermediateVarCopy = graphixDeepCopyVisitor.visit(intermediateVar, null); + LetClause nonRepresentativeBinding = new LetClause(intermediateVarCopy, iterationVarCopy1); + lowerList.addNonRepresentativeClause(nonRepresentativeBinding); + lowerList.addEdgeBinding(edgeVar, iterationVarCopy2); + }); + aliasLookupTable.addIterationAlias(edgeVar, iterationVar); + aliasLookupTable.addJoinAlias(edgeVar, intermediateVar); + } + }; + + } else if (edgeAnalysisContext.isExpressionInline()) { + return new AbstractInlineAction(graphixRewritingContext, edgeAnalysisContext, iterationVar) { + @Override + public void apply(LoweringEnvironment loweringEnvironment) throws CompilationException { + // Join our edge iteration variable to our vertex variable. + loweringEnvironment.acceptTransformer(lowerList -> { + VariableExpr vertexJoinExpr = aliasLookupTable.getJoinAlias(vertexVar); + VariableExpr vertexVarCopy = graphixDeepCopyVisitor.visit(vertexJoinExpr, null); + VariableExpr iterationVarCopy1 = graphixDeepCopyVisitor.visit(iterationVar, null); + VariableExpr iterationVarCopy2 = graphixDeepCopyVisitor.visit(iterationVar, null); + Expression vertexEdgeJoin = buildVertexEdgeJoin( + buildAccessorList(vertexVarCopy, elementLookupTable.getVertexKey(vertexIdentifier)), + buildAccessorList(iterationVarCopy1, edgeKeyAccess.apply(edgeIdentifier))); + JoinClause joinClause = new JoinClause(JoinType.INNER, datasetCallExpression, iterationVarCopy2, + null, vertexEdgeJoin, null); + joinClause.setSourceLocation(edgePatternExpr.getSourceLocation()); + lowerList.addNonRepresentativeClause(joinClause); + }); + + // Inline our edge body. + super.apply(loweringEnvironment); + + // If we have a filter expression, add it as a WHERE clause here. + final Expression filterExpr = edgePatternExpr.getEdgeDescriptor().getFilterExpr(); + if (filterExpr != null) { + loweringEnvironment.acceptAction(buildFilterExprAction(filterExpr, edgeVar, iterationVar)); + } + + // Build a record constructor from our context to bind to our edge variable. + loweringEnvironment.acceptTransformer(lowerList -> { + VariableExpr intermediateVarCopy = graphixDeepCopyVisitor.visit(intermediateVar, null); + RecordConstructor recordConstructor1 = buildRecordConstructor(); + RecordConstructor recordConstructor2 = buildRecordConstructor(); + LetClause nonRepresentativeBinding = new LetClause(intermediateVarCopy, recordConstructor1); + lowerList.addNonRepresentativeClause(nonRepresentativeBinding); + lowerList.addEdgeBinding(edgeVar, recordConstructor2); + }); + aliasLookupTable.addIterationAlias(edgeVar, iterationVar); + aliasLookupTable.addJoinAlias(edgeVar, intermediateVar); + } + }; + + } else { + GraphElementDeclaration elementDeclaration = elementLookupTable.getElementDecl(edgeIdentifier); + return loweringEnvironment -> { + // Join our edge body to our vertex variable. + loweringEnvironment.acceptTransformer(lowerList -> { + VariableExpr vertexJoinExpr = aliasLookupTable.getJoinAlias(vertexVar); + VariableExpr iterationVarCopy1 = graphixDeepCopyVisitor.visit(iterationVar, null); + VariableExpr iterationVarCopy2 = graphixDeepCopyVisitor.visit(iterationVar, null); + Expression vertexEdgeJoin = buildVertexEdgeJoin( + buildAccessorList(vertexJoinExpr, elementLookupTable.getVertexKey(vertexIdentifier)), + buildAccessorList(iterationVarCopy1, edgeKeyAccess.apply(edgeIdentifier))); + ILangExpression declBodyCopy = SqlppRewriteUtil.deepCopy(elementDeclaration.getNormalizedBody()); + JoinClause joinClause = new JoinClause(JoinType.INNER, (Expression) declBodyCopy, iterationVarCopy2, + null, vertexEdgeJoin, null); + joinClause.setSourceLocation(edgePatternExpr.getSourceLocation()); + lowerList.addNonRepresentativeClause(joinClause); + }); + + // If we have a filter expression, add it as a WHERE clause here. + final Expression filterExpr = edgePatternExpr.getEdgeDescriptor().getFilterExpr(); + if (filterExpr != null) { + loweringEnvironment.acceptAction(buildFilterExprAction(filterExpr, edgeVar, iterationVar)); + } + + // Bind our intermediate (join) variable and edge variable. + loweringEnvironment.acceptTransformer(lowerList -> { + VariableExpr iterationVarCopy1 = graphixDeepCopyVisitor.visit(iterationVar, null); + VariableExpr iterationVarCopy2 = graphixDeepCopyVisitor.visit(iterationVar, null); + VariableExpr intermediateVarCopy = graphixDeepCopyVisitor.visit(intermediateVar, null); + LetClause nonRepresentativeBinding = new LetClause(intermediateVarCopy, iterationVarCopy1); + lowerList.addNonRepresentativeClause(nonRepresentativeBinding); + lowerList.addEdgeBinding(edgeVar, iterationVarCopy2); + }); + aliasLookupTable.addIterationAlias(edgeVar, iterationVar); + aliasLookupTable.addJoinAlias(edgeVar, intermediateVar); + }; + } + } + + /** + * Build an {@link IEnvironmentAction} to introduce a WHERE-CLAUSE that will correlate a vertex and edge. + */ + public IEnvironmentAction buildRawJoinVertexAction(VertexPatternExpr vertexPatternExpr, + EdgePatternExpr edgePatternExpr, Function<EdgeIdentifier, List<List<String>>> edgeKeyAccess) + throws CompilationException { + VariableExpr edgeVar = edgePatternExpr.getEdgeDescriptor().getVariableExpr(); + VariableExpr vertexVar = vertexPatternExpr.getVariableExpr(); + + // We should only be working with one edge identifier... + List<EdgeIdentifier> edgeElementIDs = edgePatternExpr.generateIdentifiers(graphIdentifier); + if (edgeElementIDs.size() != 1) { + throw new CompilationException(ErrorCode.COMPILATION_ILLEGAL_STATE, "Found non-canonical edge pattern!"); + } + EdgeIdentifier edgeIdentifier = edgeElementIDs.get(0); + + // ...and only one vertex identifier (given that we only have one label). + List<VertexIdentifier> vertexElementIDs = vertexPatternExpr.generateIdentifiers(graphIdentifier); + if (vertexElementIDs.size() != 1) { + throw new CompilationException(ErrorCode.COMPILATION_ILLEGAL_STATE, "Found non-canonical vertex pattern!"); + } + VertexIdentifier vertexIdentifier = vertexElementIDs.get(0); + return loweringEnvironment -> { + // No aliases need to be introduced, we just need to add a WHERE-CONJUNCT. + VariableExpr edgeJoinExpr = aliasLookupTable.getJoinAlias(edgeVar); + VariableExpr vertexJoinExpr = aliasLookupTable.getJoinAlias(vertexVar); + VariableExpr edgeJoinExprCopy = graphixDeepCopyVisitor.visit(edgeJoinExpr, null); + VariableExpr vertexJoinExprCopy = graphixDeepCopyVisitor.visit(vertexJoinExpr, null); + loweringEnvironment.acceptTransformer(lowerList -> { + Expression vertexEdgeJoin = buildVertexEdgeJoin( + buildAccessorList(vertexJoinExprCopy, elementLookupTable.getVertexKey(vertexIdentifier)), + buildAccessorList(edgeJoinExprCopy, edgeKeyAccess.apply(edgeIdentifier))); + WhereClause whereClause = new WhereClause(vertexEdgeJoin); + whereClause.setSourceLocation(edgePatternExpr.getSourceLocation()); + lowerList.addNonRepresentativeClause(whereClause); + }); + }; + } + + /** + * Build an {@link IEnvironmentAction} to handle a vertex that is bound to an existing (already introduced) edge. + * There are three possible {@link IEnvironmentAction}s generated here: + * <ul> + * <li>An action for inlined vertices that have no projections.</li> + * <li>An action for inlined vertices that have projections.</li> + * <li>An action for non-inlined vertices.</li> + * </ul> + */ + public IEnvironmentAction buildBoundVertexAction(VertexPatternExpr vertexPatternExpr, + EdgePatternExpr edgePatternExpr, Function<EdgeIdentifier, List<List<String>>> edgeKeyAccess) + throws CompilationException { + VariableExpr edgeVar = edgePatternExpr.getEdgeDescriptor().getVariableExpr(); + VariableExpr vertexVar = vertexPatternExpr.getVariableExpr(); + VariableExpr iterationVar = graphixRewritingContext.getGraphixVariableCopy(vertexVar); + VariableExpr intermediateVar = graphixRewritingContext.getGraphixVariableCopy(vertexVar); + + // We should only be working with one edge identifier... + List<EdgeIdentifier> edgeElementIDs = edgePatternExpr.generateIdentifiers(graphIdentifier); + if (edgeElementIDs.size() != 1) { + throw new CompilationException(ErrorCode.COMPILATION_ILLEGAL_STATE, "Found non-canonical edge pattern!"); + } + EdgeIdentifier edgeIdentifier = edgeElementIDs.get(0); + + // ...and only one vertex identifier (given that we only have one label). + List<VertexIdentifier> vertexElementIDs = vertexPatternExpr.generateIdentifiers(graphIdentifier); + if (vertexElementIDs.size() != 1) { + throw new CompilationException(ErrorCode.COMPILATION_ILLEGAL_STATE, "Found non-canonical vertex pattern!"); + } + VertexIdentifier vertexIdentifier = vertexElementIDs.get(0); + ElementBodyAnalysisContext vertexAnalysisContext = analysisContextMap.get(vertexIdentifier); + Expression datasetCallExpression = vertexAnalysisContext.getDatasetCallExpression(); + if (vertexAnalysisContext.isExpressionInline() && vertexAnalysisContext.isSelectClauseInline()) { + return new AbstractInlineAction(graphixRewritingContext, vertexAnalysisContext, iterationVar) { + @Override + public void apply(LoweringEnvironment loweringEnvironment) throws CompilationException { + // Join our vertex iteration variable to our edge variable. + loweringEnvironment.acceptTransformer(lowerList -> { + VariableExpr edgeJoinExpr = aliasLookupTable.getJoinAlias(edgeVar); + VariableExpr edgeJoinCopy = graphixDeepCopyVisitor.visit(edgeJoinExpr, null); + VariableExpr iterationVarCopy1 = graphixDeepCopyVisitor.visit(iterationVar, null); + VariableExpr iterationVarCopy2 = graphixDeepCopyVisitor.visit(iterationVar, null); + Expression vertexEdgeJoin = buildVertexEdgeJoin( + buildAccessorList(iterationVarCopy1, elementLookupTable.getVertexKey(vertexIdentifier)), + buildAccessorList(edgeJoinCopy, edgeKeyAccess.apply(edgeIdentifier))); + JoinClause joinClause = new JoinClause(JoinType.INNER, datasetCallExpression, iterationVarCopy2, + null, vertexEdgeJoin, null); + joinClause.setSourceLocation(vertexPatternExpr.getSourceLocation()); + lowerList.addNonRepresentativeClause(joinClause); + }); + + // Inline our vertex body. + super.apply(loweringEnvironment); + + // If we have a filter expression, add it as a WHERE clause here. + final Expression filterExpr = vertexPatternExpr.getFilterExpr(); + if (filterExpr != null) { + loweringEnvironment.acceptAction(buildFilterExprAction(filterExpr, vertexVar, iterationVar)); + } + + // Bind our intermediate (join) variable and vertex variable. + loweringEnvironment.acceptTransformer(lowerList -> { + VariableExpr iterationVarCopy1 = graphixDeepCopyVisitor.visit(iterationVar, null); + VariableExpr iterationVarCopy2 = graphixDeepCopyVisitor.visit(iterationVar, null); + VariableExpr intermediateVarCopy = graphixDeepCopyVisitor.visit(intermediateVar, null); + LetClause nonRepresentativeBinding = new LetClause(intermediateVarCopy, iterationVarCopy1); + lowerList.addNonRepresentativeClause(nonRepresentativeBinding); + lowerList.addVertexBinding(vertexVar, iterationVarCopy2); + }); + aliasLookupTable.addIterationAlias(vertexVar, iterationVar); + aliasLookupTable.addJoinAlias(vertexVar, intermediateVar); + } + }; + + } else if (vertexAnalysisContext.isExpressionInline()) { + return new AbstractInlineAction(graphixRewritingContext, vertexAnalysisContext, iterationVar) { + @Override + public void apply(LoweringEnvironment loweringEnvironment) throws CompilationException { + // Join our vertex iteration variable to our edge variable. + loweringEnvironment.acceptTransformer(lowerList -> { + VariableExpr edgeJoinExpr = aliasLookupTable.getJoinAlias(edgeVar); + VariableExpr edgeJoinCopy = graphixDeepCopyVisitor.visit(edgeJoinExpr, null); + VariableExpr iterationVarCopy1 = graphixDeepCopyVisitor.visit(iterationVar, null); + VariableExpr iterationVarCopy2 = graphixDeepCopyVisitor.visit(iterationVar, null); + Expression vertexEdgeJoin = buildVertexEdgeJoin( + buildAccessorList(iterationVarCopy1, elementLookupTable.getVertexKey(vertexIdentifier)), + buildAccessorList(edgeJoinCopy, edgeKeyAccess.apply(edgeIdentifier))); + JoinClause joinClause = new JoinClause(JoinType.INNER, datasetCallExpression, iterationVarCopy2, + null, vertexEdgeJoin, null); + joinClause.setSourceLocation(vertexPatternExpr.getSourceLocation()); + lowerList.addNonRepresentativeClause(joinClause); + }); + + // Inline our vertex body. + super.apply(loweringEnvironment); + + // If we have a filter expression, add it as a WHERE clause here. + final Expression filterExpr = vertexPatternExpr.getFilterExpr(); + if (filterExpr != null) { + loweringEnvironment.acceptAction(buildFilterExprAction(filterExpr, vertexVar, iterationVar)); + } + + // Build a record constructor from our context to bind to our vertex variable. + loweringEnvironment.acceptTransformer(lowerList -> { + VariableExpr intermediateVarCopy = graphixDeepCopyVisitor.visit(intermediateVar, null); + RecordConstructor recordConstructor1 = buildRecordConstructor(); + RecordConstructor recordConstructor2 = buildRecordConstructor(); + LetClause nonRepresentativeBinding = new LetClause(intermediateVarCopy, recordConstructor1); + lowerList.addNonRepresentativeClause(nonRepresentativeBinding); + lowerList.addVertexBinding(vertexVar, recordConstructor2); + }); + aliasLookupTable.addIterationAlias(vertexVar, iterationVar); + aliasLookupTable.addJoinAlias(vertexVar, intermediateVar); + } + }; + + } else { + GraphElementDeclaration elementDeclaration = elementLookupTable.getElementDecl(vertexIdentifier); + return loweringEnvironment -> { + // Join our vertex body to our edge variable. + loweringEnvironment.acceptTransformer(lowerList -> { + VariableExpr edgeJoinExpr = aliasLookupTable.getJoinAlias(edgeVar); + VariableExpr edgeJoinCopy = graphixDeepCopyVisitor.visit(edgeJoinExpr, null); + VariableExpr iterationVarCopy1 = graphixDeepCopyVisitor.visit(iterationVar, null); + VariableExpr iterationVarCopy2 = graphixDeepCopyVisitor.visit(iterationVar, null); + Expression vertexEdgeJoin = buildVertexEdgeJoin( + buildAccessorList(iterationVarCopy1, elementLookupTable.getVertexKey(vertexIdentifier)), + buildAccessorList(edgeJoinCopy, edgeKeyAccess.apply(edgeIdentifier))); + ILangExpression declBodyCopy = SqlppRewriteUtil.deepCopy(elementDeclaration.getNormalizedBody()); + JoinClause joinClause = new JoinClause(JoinType.INNER, (Expression) declBodyCopy, iterationVarCopy2, + null, vertexEdgeJoin, null); + joinClause.setSourceLocation(vertexPatternExpr.getSourceLocation()); + lowerList.addNonRepresentativeClause(joinClause); + }); + + // If we have a filter expression, add it as a WHERE clause here. + final Expression filterExpr = vertexPatternExpr.getFilterExpr(); + if (filterExpr != null) { + loweringEnvironment.acceptAction(buildFilterExprAction(filterExpr, vertexVar, iterationVar)); + } + + // Bind our intermediate (join) variable and vertex variable. + loweringEnvironment.acceptTransformer(lowerList -> { + VariableExpr iterationVarCopy1 = graphixDeepCopyVisitor.visit(iterationVar, null); + VariableExpr iterationVarCopy2 = graphixDeepCopyVisitor.visit(iterationVar, null); + VariableExpr intermediateVarCopy = graphixDeepCopyVisitor.visit(intermediateVar, null); + LetClause nonRepresentativeBinding = new LetClause(intermediateVarCopy, iterationVarCopy1); + lowerList.addNonRepresentativeClause(nonRepresentativeBinding); + lowerList.addVertexBinding(vertexVar, iterationVarCopy2); + }); + aliasLookupTable.addIterationAlias(vertexVar, iterationVar); + aliasLookupTable.addJoinAlias(vertexVar, intermediateVar); + }; + } + } +}
diff --git a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrite/lower/LoweringEnvironment.java b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrite/lower/LoweringEnvironment.java new file mode 100644 index 0000000..ea80458 --- /dev/null +++ b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrite/lower/LoweringEnvironment.java
@@ -0,0 +1,366 @@ +/* + * 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.graphix.lang.rewrite.lower; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.ListIterator; +import java.util.Map; +import java.util.Objects; +import java.util.function.Consumer; + +import org.apache.asterix.common.exceptions.CompilationException; +import org.apache.asterix.common.exceptions.ErrorCode; +import org.apache.asterix.graphix.common.metadata.GraphIdentifier; +import org.apache.asterix.graphix.lang.clause.FromGraphClause; +import org.apache.asterix.graphix.lang.clause.LowerListClause; +import org.apache.asterix.graphix.lang.clause.LowerSwitchClause; +import org.apache.asterix.graphix.lang.clause.LowerSwitchClause.ClauseInputEnvironment; +import org.apache.asterix.graphix.lang.clause.LowerSwitchClause.ClauseOutputEnvironment; +import org.apache.asterix.graphix.lang.expression.EdgePatternExpr; +import org.apache.asterix.graphix.lang.rewrite.GraphixRewritingContext; +import org.apache.asterix.graphix.lang.rewrite.lower.action.IEnvironmentAction; +import org.apache.asterix.graphix.lang.rewrite.lower.action.ILowerListTransformer; +import org.apache.asterix.graphix.lang.rewrite.lower.struct.ClauseCollection; +import org.apache.asterix.graphix.lang.rewrite.lower.struct.CollectionTable; +import org.apache.asterix.graphix.lang.rewrite.lower.struct.StateContainer; +import org.apache.asterix.graphix.lang.rewrite.util.LowerRewritingUtil; +import org.apache.asterix.graphix.lang.rewrite.visitor.GraphixDeepCopyVisitor; +import org.apache.asterix.graphix.lang.rewrite.visitor.GraphixLoweringVisitor; +import org.apache.asterix.graphix.lang.rewrite.visitor.VariableRemapCloneVisitor; +import org.apache.asterix.graphix.lang.struct.ElementLabel; +import org.apache.asterix.lang.common.base.AbstractClause; +import org.apache.asterix.lang.common.base.Clause; +import org.apache.asterix.lang.common.base.Expression; +import org.apache.asterix.lang.common.base.IVisitorExtension; +import org.apache.asterix.lang.common.base.Literal; +import org.apache.asterix.lang.common.clause.LetClause; +import org.apache.asterix.lang.common.clause.WhereClause; +import org.apache.asterix.lang.common.expression.FieldAccessor; +import org.apache.asterix.lang.common.expression.LiteralExpr; +import org.apache.asterix.lang.common.expression.VariableExpr; +import org.apache.asterix.lang.common.literal.TrueLiteral; +import org.apache.asterix.lang.common.struct.OperatorType; +import org.apache.asterix.lang.sqlpp.clause.AbstractBinaryCorrelateClause; +import org.apache.asterix.lang.sqlpp.clause.AbstractBinaryCorrelateWithConditionClause; +import org.apache.asterix.lang.sqlpp.clause.JoinClause; +import org.apache.asterix.lang.sqlpp.clause.Projection; +import org.apache.asterix.lang.sqlpp.clause.SelectBlock; +import org.apache.asterix.lang.sqlpp.clause.SelectClause; +import org.apache.asterix.lang.sqlpp.clause.SelectRegular; +import org.apache.asterix.lang.sqlpp.clause.SelectSetOperation; +import org.apache.asterix.lang.sqlpp.expression.SelectExpression; +import org.apache.asterix.lang.sqlpp.optype.JoinType; +import org.apache.asterix.lang.sqlpp.struct.SetOperationInput; +import org.apache.asterix.lang.sqlpp.util.SqlppVariableUtil; +import org.apache.asterix.lang.sqlpp.visitor.FreeVariableVisitor; +import org.apache.hyracks.api.exceptions.SourceLocation; + +/** + * @see GraphixLoweringVisitor + */ +public class LoweringEnvironment { + private final GraphixRewritingContext graphixRewritingContext; + private final GraphixDeepCopyVisitor graphixDeepCopyVisitor; + private final ClauseCollection mainClauseCollection; + private final GraphIdentifier graphIdentifier; + private final SourceLocation sourceLocation; + + // The following are created through beginLeftMatch / beginTempLowerList / beginBranches. + private ClauseCollection leftClauseCollection; + private ClauseCollection tempClauseCollection; + private ClauseCollection branchClauseCollection; + private CollectionTable collectionTable; + private boolean isInlineLegal; + + public LoweringEnvironment(GraphixRewritingContext graphixRewritingContext, GraphIdentifier graphIdentifier, + SourceLocation sourceLocation) { + this.mainClauseCollection = new ClauseCollection(sourceLocation); + this.graphixDeepCopyVisitor = new GraphixDeepCopyVisitor(); + this.graphixRewritingContext = graphixRewritingContext; + this.graphIdentifier = graphIdentifier; + this.sourceLocation = sourceLocation; + this.leftClauseCollection = null; + this.tempClauseCollection = null; + this.branchClauseCollection = null; + } + + public GraphIdentifier getGraphIdentifier() { + return graphIdentifier; + } + + public void acceptAction(IEnvironmentAction environmentAction) throws CompilationException { + environmentAction.apply(this); + } + + public void acceptTransformer(ILowerListTransformer sequenceTransformer) throws CompilationException { + // Fixed point lowering will always take precedence. + sequenceTransformer.accept(Objects.requireNonNullElseGet(tempClauseCollection, + () -> Objects.requireNonNullElseGet(branchClauseCollection, + () -> Objects.requireNonNullElse(leftClauseCollection, mainClauseCollection)))); + } + + public void setInlineLegal(boolean isInlineLegal) { + this.isInlineLegal = isInlineLegal; + } + + public boolean isInlineLegal() { + return isInlineLegal; + } + + public void beginLeftMatch() throws CompilationException { + if (leftClauseCollection != null) { + throw new CompilationException(ErrorCode.COMPILATION_ILLEGAL_STATE, + "LEFT-MATCH lowering is currently in progress!"); + } + leftClauseCollection = new ClauseCollection(sourceLocation); + } + + public void endLeftMatch() throws CompilationException { + if (leftClauseCollection.getNonRepresentativeClauses().isEmpty()) { + // This is an extraneous LEFT-MATCH. Do not modify anything. + leftClauseCollection = null; + return; + } + + // Build our substitution visitor and environment. + VariableRemapCloneVisitor remapCloneVisitor = new VariableRemapCloneVisitor(graphixRewritingContext); + VariableExpr nestingVariable = graphixRewritingContext.getGraphixVariableCopy("_LeftMatch"); + final Consumer<VariableExpr> substitutionAdder = v -> { + VariableExpr nestingVariableCopy = new VariableExpr(nestingVariable.getVar()); + FieldAccessor fieldAccessor = new FieldAccessor(nestingVariableCopy, v.getVar()); + remapCloneVisitor.addSubstitution(v, fieldAccessor); + }; + + // Build up our projection list. + List<Projection> projectionList = new ArrayList<>(); + List<AbstractClause> leftLowerClauses = leftClauseCollection.getNonRepresentativeClauses(); + for (AbstractClause workingClause : leftLowerClauses) { + if (workingClause.getClauseType() == Clause.ClauseType.WHERE_CLAUSE) { + continue; + } + + // Identify our right variable. + VariableExpr rightVariable; + if (workingClause.getClauseType() == Clause.ClauseType.LET_CLAUSE) { + rightVariable = ((LetClause) workingClause).getVarExpr(); + + } else if (workingClause instanceof AbstractBinaryCorrelateClause) { + rightVariable = ((AbstractBinaryCorrelateClause) workingClause).getRightVariable(); + + } else { + throw new CompilationException(ErrorCode.COMPILATION_ILLEGAL_STATE, "Illegal clause found!"); + } + projectionList.add(new Projection(Projection.Kind.NAMED_EXPR, rightVariable, + SqlppVariableUtil.toUserDefinedVariableName(rightVariable.getVar()).getValue())); + substitutionAdder.accept(rightVariable); + } + + // Nestle our clauses in a SELECT-BLOCK. + LowerListClause leftLowerClause = new LowerListClause(leftClauseCollection); + SelectClause selectClause = new SelectClause(null, new SelectRegular(projectionList), false); + SelectBlock selectBlock = new SelectBlock(selectClause, null, null, null, null); + selectBlock.setFromClause(new FromGraphClause(leftLowerClause)); + SetOperationInput setOperationInput = new SetOperationInput(selectBlock, null); + SelectSetOperation selectSetOperation = new SelectSetOperation(setOperationInput, null); + SelectExpression selectExpression = new SelectExpression(null, selectSetOperation, null, null, true); + + // Merge the collection we just built with our main sequence. + IVisitorExtension visitorExtension = leftLowerClause.getVisitorExtension(); + Expression conditionExpression = generateJoinCondition(leftLowerClauses.listIterator(), visitorExtension); + VariableExpr nestingVariableCopy = graphixDeepCopyVisitor.visit(nestingVariable, null); + JoinClause leftJoinClause = new JoinClause(JoinType.LEFTOUTER, selectExpression, nestingVariableCopy, null, + (Expression) remapCloneVisitor.substitute(conditionExpression), Literal.Type.MISSING); + mainClauseCollection.addNonRepresentativeClause(leftJoinClause); + + // Introduce our representative variables back into our main sequence. + for (LetClause representativeVertexBinding : leftClauseCollection.getRepresentativeVertexBindings()) { + VariableExpr representativeVariable = representativeVertexBinding.getVarExpr(); + VariableExpr representativeVariableCopy = graphixDeepCopyVisitor.visit(representativeVariable, null); + Expression rightExpression = representativeVertexBinding.getBindingExpr(); + Expression reboundExpression = (Expression) remapCloneVisitor.substitute(rightExpression); + mainClauseCollection.addVertexBinding(representativeVariableCopy, reboundExpression); + } + for (LetClause representativeEdgeBinding : leftClauseCollection.getRepresentativeEdgeBindings()) { + VariableExpr representativeVariable = representativeEdgeBinding.getVarExpr(); + VariableExpr representativeVariableCopy = graphixDeepCopyVisitor.visit(representativeVariable, null); + Expression rightExpression = representativeEdgeBinding.getBindingExpr(); + Expression reboundExpression = (Expression) remapCloneVisitor.substitute(rightExpression); + mainClauseCollection.addEdgeBinding(representativeVariableCopy, reboundExpression); + } + for (LetClause representativePathBinding : leftClauseCollection.getRepresentativePathBindings()) { + VariableExpr representativeVariable = representativePathBinding.getVarExpr(); + VariableExpr representativeVariableCopy = graphixDeepCopyVisitor.visit(representativeVariable, null); + Expression rightExpression = representativePathBinding.getBindingExpr(); + Expression reboundExpression = (Expression) remapCloneVisitor.substitute(rightExpression); + mainClauseCollection.addPathBinding(representativeVariableCopy, reboundExpression); + } + + // Do not reintroduce our vertex, edge, and path bindings. + leftClauseCollection.getRepresentativeVertexBindings().clear(); + leftClauseCollection.getRepresentativeEdgeBindings().clear(); + leftClauseCollection.getRepresentativePathBindings().clear(); + leftClauseCollection = null; + } + + public void beginTempLowerList() throws CompilationException { + if (tempClauseCollection != null) { + throw new CompilationException(ErrorCode.COMPILATION_ILLEGAL_STATE, + "Temp branch lowering is currently in progress!"); + } + tempClauseCollection = new ClauseCollection(sourceLocation); + } + + public void endTempLowerList() { + // We discard the collection we just built. + tempClauseCollection = null; + } + + public void beginBranches() throws CompilationException { + if (collectionTable != null || branchClauseCollection != null) { + throw new CompilationException(ErrorCode.COMPILATION_ILLEGAL_STATE, + "Path branch lowering is currently in progress!"); + } + collectionTable = new CollectionTable(); + branchClauseCollection = new ClauseCollection(sourceLocation); + } + + public void flushBranch(EdgePatternExpr edgePatternExpr, boolean isJoiningLeftToRight) throws CompilationException { + if (branchClauseCollection.getRepresentativeVertexBindings().size() != 1) { + throw new CompilationException(ErrorCode.COMPILATION_ILLEGAL_STATE, + "Only one vertex should exist in the clause collection!"); + } + if (branchClauseCollection.getRepresentativeEdgeBindings().size() != 1) { + throw new CompilationException(ErrorCode.COMPILATION_ILLEGAL_STATE, + "Only one edge should exist in the clause collection!"); + } + collectionTable.putCollection(edgePatternExpr, isJoiningLeftToRight, branchClauseCollection); + branchClauseCollection = new ClauseCollection(sourceLocation); + } + + public void endBranches(ClauseOutputEnvironment clauseOutputEnvironment, + ClauseInputEnvironment clauseInputEnvironment, Map<ElementLabel, VariableExpr> inputMap, + Map<ElementLabel, VariableExpr> outputMap, AliasLookupTable aliasLookupTable, SourceLocation sourceLocation) + throws CompilationException { + // Build the input map for our collection table. + Map<ElementLabel, StateContainer> inputStateMap = new HashMap<>(); + for (Map.Entry<ElementLabel, VariableExpr> mapEntry : inputMap.entrySet()) { + VariableExpr iterationAlias = aliasLookupTable.getIterationAlias(mapEntry.getValue()); + VariableExpr joinAlias = aliasLookupTable.getJoinAlias(mapEntry.getValue()); + inputStateMap.put(mapEntry.getKey(), new StateContainer(iterationAlias, joinAlias)); + } + collectionTable.setInputMap(inputStateMap); + + // ...and the output map for out collection table. + Map<ElementLabel, StateContainer> outputStateMap = new HashMap<>(); + for (Map.Entry<ElementLabel, VariableExpr> mapEntry : outputMap.entrySet()) { + VariableExpr iterationAlias = aliasLookupTable.getIterationAlias(mapEntry.getValue()); + VariableExpr joinAlias = aliasLookupTable.getJoinAlias(mapEntry.getValue()); + outputStateMap.put(mapEntry.getKey(), new StateContainer(iterationAlias, joinAlias)); + } + collectionTable.setOutputMap(outputStateMap); + + // Add our GRAPH-CLAUSE to our main sequence. + LowerSwitchClause lowerSwitchClause = + new LowerSwitchClause(collectionTable, clauseInputEnvironment, clauseOutputEnvironment); + lowerSwitchClause.setSourceLocation(sourceLocation); + mainClauseCollection.addNonRepresentativeClause(lowerSwitchClause); + branchClauseCollection = null; + collectionTable = null; + } + + public void endLowering(FromGraphClause targetFromClause) { + targetFromClause.setLowerClause(new LowerListClause(mainClauseCollection)); + } + + private static Expression generateJoinCondition(ListIterator<AbstractClause> lowerClauseIterator, + IVisitorExtension visitorExtension) throws CompilationException { + final List<Expression> joinConditionExpressions = new ArrayList<>(); + final Collection<VariableExpr> freeVariables = new HashSet<>(); + final FreeVariableVisitor freeVariableVisitor = new FreeVariableVisitor() { + @Override + public Void visit(IVisitorExtension visitorExtension, Collection<VariableExpr> freeVars) + throws CompilationException { + Collection<VariableExpr> bindingVariables = new HashSet<>(); + Collection<VariableExpr> conditionFreeVars = new HashSet<>(); + Collection<VariableExpr> clauseFreeVars = new HashSet<>(); + while (lowerClauseIterator.hasNext()) { + AbstractClause lowerClause = lowerClauseIterator.next(); + clauseFreeVars.clear(); + if (lowerClause instanceof AbstractBinaryCorrelateClause) { + AbstractBinaryCorrelateClause correlateClause = (AbstractBinaryCorrelateClause) lowerClause; + correlateClause.getRightExpression().accept(this, clauseFreeVars); + if (lowerClause.getClauseType() == Clause.ClauseType.UNNEST_CLAUSE) { + clauseFreeVars.removeAll(bindingVariables); + if (!clauseFreeVars.isEmpty()) { + throw new CompilationException(ErrorCode.COMPILATION_ILLEGAL_STATE, + "Encountered UNNEST-CLAUSE with free variables."); + } + + } else { + AbstractBinaryCorrelateWithConditionClause clauseWithCondition = + (AbstractBinaryCorrelateWithConditionClause) correlateClause; + conditionFreeVars.clear(); + clauseWithCondition.getConditionExpression().accept(this, conditionFreeVars); + conditionFreeVars.removeAll(bindingVariables); + conditionFreeVars.remove(correlateClause.getRightVariable()); + if (!conditionFreeVars.isEmpty()) { + // We have found a JOIN with a free variable. + joinConditionExpressions.add(clauseWithCondition.getConditionExpression()); + clauseWithCondition.setConditionExpression(new LiteralExpr(TrueLiteral.INSTANCE)); + } + clauseFreeVars.addAll(conditionFreeVars); + } + + // Adds binding variables. + bindingVariables.add(correlateClause.getRightVariable()); + freeVars.addAll(clauseFreeVars); + + } else if (lowerClause.getClauseType() == Clause.ClauseType.WHERE_CLAUSE) { + WhereClause whereClause = (WhereClause) lowerClause; + whereClause.getWhereExpr().accept(this, clauseFreeVars); + clauseFreeVars.removeAll(bindingVariables); + if (!clauseFreeVars.isEmpty()) { + joinConditionExpressions.add(whereClause.getWhereExpr()); + lowerClauseIterator.remove(); + } + freeVars.addAll(clauseFreeVars); + + } else if (lowerClause.getClauseType() == Clause.ClauseType.LET_CLAUSE) { + LetClause letClause = (LetClause) lowerClause; + letClause.getBindingExpr().accept(this, clauseFreeVars); + clauseFreeVars.removeAll(bindingVariables); + bindingVariables.add(letClause.getVarExpr()); + if (!clauseFreeVars.isEmpty()) { + throw new CompilationException(ErrorCode.COMPILATION_ILLEGAL_STATE, + "Encountered LET-CLAUSE with free variables."); + } + } + } + return null; + } + }; + freeVariableVisitor.visit(visitorExtension, freeVariables); + return joinConditionExpressions.isEmpty() ? new LiteralExpr(TrueLiteral.INSTANCE) + : LowerRewritingUtil.buildConnectedClauses(joinConditionExpressions, OperatorType.AND); + } +}
diff --git a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrite/lower/action/AbstractInlineAction.java b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrite/lower/action/AbstractInlineAction.java new file mode 100644 index 0000000..da91946 --- /dev/null +++ b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrite/lower/action/AbstractInlineAction.java
@@ -0,0 +1,151 @@ +/* + * 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.graphix.lang.rewrite.lower.action; + +import static org.apache.asterix.graphix.lang.rewrite.visitor.ElementBodyAnalysisVisitor.ElementBodyAnalysisContext; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.Function; + +import org.apache.asterix.common.exceptions.CompilationException; +import org.apache.asterix.common.exceptions.ErrorCode; +import org.apache.asterix.graphix.lang.rewrite.GraphixRewritingContext; +import org.apache.asterix.graphix.lang.rewrite.lower.LoweringEnvironment; +import org.apache.asterix.graphix.lang.rewrite.visitor.GraphixDeepCopyVisitor; +import org.apache.asterix.graphix.lang.rewrite.visitor.VariableRemapCloneVisitor; +import org.apache.asterix.lang.common.base.Expression; +import org.apache.asterix.lang.common.base.ILangExpression; +import org.apache.asterix.lang.common.clause.LetClause; +import org.apache.asterix.lang.common.clause.WhereClause; +import org.apache.asterix.lang.common.expression.FieldBinding; +import org.apache.asterix.lang.common.expression.LiteralExpr; +import org.apache.asterix.lang.common.expression.RecordConstructor; +import org.apache.asterix.lang.common.expression.VariableExpr; +import org.apache.asterix.lang.common.literal.StringLiteral; +import org.apache.asterix.lang.sqlpp.clause.AbstractBinaryCorrelateClause; +import org.apache.asterix.lang.sqlpp.clause.Projection; +import org.apache.asterix.lang.sqlpp.clause.UnnestClause; + +/** + * Inline an element body into a {@link LoweringEnvironment}. This includes: + * <ol> + * <li>Copying {@link UnnestClause}, {@link LetClause}, and {@link WhereClause} AST nodes from our body analysis</li> + * <li>Creating {@link RecordConstructor} AST nodes to inline + * {@link org.apache.asterix.lang.sqlpp.clause.SelectRegular} nodes.</li> + * </ol> + */ +public abstract class AbstractInlineAction implements IEnvironmentAction { + protected final GraphixRewritingContext graphixRewritingContext; + protected final ElementBodyAnalysisContext bodyAnalysisContext; + protected final VariableRemapCloneVisitor remapCloneVisitor; + protected final GraphixDeepCopyVisitor deepCopyVisitor; + + // This may be mutated by our child. + protected VariableExpr elementVariable; + + protected AbstractInlineAction(GraphixRewritingContext graphixRewritingContext, + ElementBodyAnalysisContext bodyAnalysisContext, VariableExpr elementVariable) { + this.graphixRewritingContext = graphixRewritingContext; + this.bodyAnalysisContext = bodyAnalysisContext; + this.elementVariable = elementVariable; + this.remapCloneVisitor = new VariableRemapCloneVisitor(graphixRewritingContext); + this.deepCopyVisitor = new GraphixDeepCopyVisitor(); + } + + @Override + public void apply(LoweringEnvironment loweringEnvironment) throws CompilationException { + final Function<VariableExpr, VariableExpr> substitutionAdder = v -> { + VariableExpr reboundVariableExpr = graphixRewritingContext.getGraphixVariableCopy(v); + remapCloneVisitor.addSubstitution(v, reboundVariableExpr); + return reboundVariableExpr; + }; + + // To inline, we need to ensure that we substitute variables accordingly. + remapCloneVisitor.resetSubstitutions(); + if (bodyAnalysisContext.getFromTermVariable() != null) { + VariableExpr fromTermVariableExpr = bodyAnalysisContext.getFromTermVariable(); + VariableExpr elementVariableExpr = new VariableExpr(elementVariable.getVar()); + remapCloneVisitor.addSubstitution(fromTermVariableExpr, elementVariableExpr); + } + + // If we have any UNNEST clauses, we need to add these. + if (bodyAnalysisContext.getUnnestClauses() != null) { + for (AbstractBinaryCorrelateClause unnestClause : bodyAnalysisContext.getUnnestClauses()) { + loweringEnvironment.acceptTransformer(lowerList -> { + UnnestClause copiedClause = (UnnestClause) remapCloneVisitor.substitute(unnestClause); + if (copiedClause.hasPositionalVariable()) { + substitutionAdder.apply(copiedClause.getPositionalVariable()); + } + VariableExpr reboundUnnestVariable = substitutionAdder.apply(copiedClause.getRightVariable()); + UnnestClause newUnnestClause = + new UnnestClause(copiedClause.getUnnestType(), copiedClause.getRightExpression(), + reboundUnnestVariable, null, copiedClause.getOuterUnnestMissingValueType()); + newUnnestClause.setSourceLocation(unnestClause.getSourceLocation()); + lowerList.addNonRepresentativeClause(newUnnestClause); + }); + } + } + + // If we have any LET clauses, we need to substitute them in our WHERE and SELECT clauses. + if (bodyAnalysisContext.getLetClauses() != null) { + for (LetClause letClause : bodyAnalysisContext.getLetClauses()) { + // Remap this LET-CLAUSE to include our new variables. Move this to our correlated clauses. + LetClause copiedClause = (LetClause) remapCloneVisitor.substitute(letClause); + VariableExpr reboundLetVariable = substitutionAdder.apply(copiedClause.getVarExpr()); + VariableExpr reboundLetVariableCopy = deepCopyVisitor.visit(reboundLetVariable, null); + Expression copiedBindingExpr = copiedClause.getBindingExpr(); + loweringEnvironment.acceptTransformer(lowerList -> { + LetClause reboundLetClause = new LetClause(reboundLetVariableCopy, copiedBindingExpr); + reboundLetClause.setSourceLocation(letClause.getSourceLocation()); + lowerList.addNonRepresentativeClause(reboundLetClause); + }); + } + } + + // If we have any WHERE clauses, we need to add these. + if (bodyAnalysisContext.getWhereClauses() != null) { + for (WhereClause whereClause : bodyAnalysisContext.getWhereClauses()) { + WhereClause copiedClause = (WhereClause) remapCloneVisitor.substitute(whereClause); + loweringEnvironment.acceptTransformer(lowerList -> { + WhereClause newWhereClause = new WhereClause(copiedClause.getWhereExpr()); + newWhereClause.setSourceLocation(whereClause.getSourceLocation()); + lowerList.addNonRepresentativeClause(newWhereClause); + }); + } + } + } + + protected RecordConstructor buildRecordConstructor() throws CompilationException { + if (bodyAnalysisContext.getSelectClauseProjections() != null) { + // Map our original variable to our element variable. + List<FieldBinding> fieldBindings = new ArrayList<>(); + for (Projection projection : bodyAnalysisContext.getSelectClauseProjections()) { + LiteralExpr fieldNameExpr = new LiteralExpr(new StringLiteral(projection.getName())); + ILangExpression fieldValueExpr = remapCloneVisitor.substitute(projection.getExpression()); + fieldBindings.add(new FieldBinding(fieldNameExpr, (Expression) fieldValueExpr)); + } + return new RecordConstructor(fieldBindings); + + } else { + throw new CompilationException(ErrorCode.COMPILATION_ILLEGAL_STATE, + "Non-inlineable SELECT clause encountered, but was body was marked as inline!"); + } + } +}
diff --git a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/lower/action/IEnvironmentAction.java b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrite/lower/action/IEnvironmentAction.java similarity index 87% rename from asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/lower/action/IEnvironmentAction.java rename to asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrite/lower/action/IEnvironmentAction.java index 4ab6030..90926bc 100644 --- a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/lower/action/IEnvironmentAction.java +++ b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrite/lower/action/IEnvironmentAction.java
@@ -16,10 +16,10 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.asterix.graphix.lang.rewrites.lower.action; +package org.apache.asterix.graphix.lang.rewrite.lower.action; import org.apache.asterix.common.exceptions.CompilationException; -import org.apache.asterix.graphix.lang.rewrites.lower.LoweringEnvironment; +import org.apache.asterix.graphix.lang.rewrite.lower.LoweringEnvironment; @FunctionalInterface public interface IEnvironmentAction {
diff --git a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/lower/transform/ISequenceTransformer.java b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrite/lower/action/ILowerListTransformer.java similarity index 77% copy from asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/lower/transform/ISequenceTransformer.java copy to asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrite/lower/action/ILowerListTransformer.java index 4509792..72a48f4 100644 --- a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/lower/transform/ISequenceTransformer.java +++ b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrite/lower/action/ILowerListTransformer.java
@@ -16,11 +16,12 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.asterix.graphix.lang.rewrites.lower.transform; +package org.apache.asterix.graphix.lang.rewrite.lower.action; import org.apache.asterix.common.exceptions.CompilationException; +import org.apache.asterix.graphix.lang.rewrite.lower.struct.ClauseCollection; @FunctionalInterface -public interface ISequenceTransformer { - void accept(CorrelatedClauseSequence clauseSequence) throws CompilationException; +public interface ILowerListTransformer { + void accept(ClauseCollection lowerList) throws CompilationException; }
diff --git a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrite/lower/action/MatchSemanticAction.java b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrite/lower/action/MatchSemanticAction.java new file mode 100644 index 0000000..0fa6483 --- /dev/null +++ b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrite/lower/action/MatchSemanticAction.java
@@ -0,0 +1,332 @@ +/* + * 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.graphix.lang.rewrite.lower.action; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.ListIterator; +import java.util.Map; +import java.util.Set; +import java.util.function.Function; + +import org.apache.asterix.common.exceptions.CompilationException; +import org.apache.asterix.common.functions.FunctionSignature; +import org.apache.asterix.graphix.algebra.compiler.option.SemanticsNavigationOption; +import org.apache.asterix.graphix.algebra.compiler.option.SemanticsPatternOption; +import org.apache.asterix.graphix.lang.clause.FromGraphClause; +import org.apache.asterix.graphix.lang.clause.LowerListClause; +import org.apache.asterix.graphix.lang.clause.LowerSwitchClause; +import org.apache.asterix.graphix.lang.clause.MatchClause; +import org.apache.asterix.graphix.lang.expression.EdgePatternExpr; +import org.apache.asterix.graphix.lang.expression.VertexPatternExpr; +import org.apache.asterix.graphix.lang.rewrite.GraphixRewritingContext; +import org.apache.asterix.graphix.lang.rewrite.lower.AliasLookupTable; +import org.apache.asterix.graphix.lang.rewrite.lower.LoweringEnvironment; +import org.apache.asterix.graphix.lang.rewrite.lower.struct.ClauseCollection; +import org.apache.asterix.graphix.lang.rewrite.visitor.GraphixDeepCopyVisitor; +import org.apache.asterix.graphix.lang.rewrite.visitor.VariableRemapCloneVisitor; +import org.apache.asterix.graphix.lang.struct.EdgeDescriptor; +import org.apache.asterix.graphix.lang.struct.ElementLabel; +import org.apache.asterix.graphix.lang.visitor.base.AbstractGraphixQueryVisitor; +import org.apache.asterix.lang.common.base.AbstractClause; +import org.apache.asterix.lang.common.base.Clause; +import org.apache.asterix.lang.common.base.Expression; +import org.apache.asterix.lang.common.base.ILangExpression; +import org.apache.asterix.lang.common.clause.LetClause; +import org.apache.asterix.lang.common.clause.WhereClause; +import org.apache.asterix.lang.common.expression.CallExpr; +import org.apache.asterix.lang.common.expression.FieldAccessor; +import org.apache.asterix.lang.common.expression.OperatorExpr; +import org.apache.asterix.lang.common.expression.VariableExpr; +import org.apache.asterix.lang.common.struct.OperatorType; +import org.apache.asterix.lang.sqlpp.clause.AbstractBinaryCorrelateClause; +import org.apache.asterix.lang.sqlpp.clause.JoinClause; +import org.apache.asterix.lang.sqlpp.clause.SelectBlock; +import org.apache.asterix.lang.sqlpp.clause.SelectSetOperation; +import org.apache.asterix.lang.sqlpp.expression.SelectExpression; +import org.apache.asterix.lang.sqlpp.optype.JoinType; +import org.apache.asterix.lang.sqlpp.util.SqlppVariableUtil; +import org.apache.asterix.om.functions.BuiltinFunctions; + +/** + * Define the semantics of evaluating a basic graph pattern query (i.e. how much isomorphism do we enforce), and b) + * b) the semantics of navigating between vertices (i.e. what type of uniqueness in the path should be enforced). We + * assume that all elements are named at this point and that our {@link FromGraphClause} is in canonical form. + * <p> + * We enforce the following basic graph pattern query semantics (by default, we enforce total isomorphism): + * <ul> + * <li>For total isomorphism, no vertex and no edge can appear more than once across all {@link MatchClause} + * nodes.</li> + * <li>For vertex-isomorphism, we enforce that no vertex can appear more than once across all {@link MatchClause} + * nodes.</li> + * <li>For edge-isomorphism, we enforce that no edge can appear more than once across all {@link MatchClause} + * nodes.</li> + * <li>For homomorphism, we enforce nothing. Edge adjacency is already implicitly preserved.</li> + * </ul> + * <p> + * We enforce the following navigation query semantics (by default, we enforce no-repeat-anything): + * <ul> + * <li>For no-repeat-vertices, no vertex instance can appear more than once in a path instance.</li> + * <li>For no-repeat-edges, no edge instance can appear more than once in a path instance.</li> + * <li>For no-repeat-anything, no vertex or edge instance can appear more than once in a path instance.</li> + * </ul> + */ +public class MatchSemanticAction implements IEnvironmentAction { + // We will walk through our FROM-GRAPH-CLAUSE and determine our isomorphism conjuncts. + private final SemanticsNavigationOption navigationSemantics; + private final SemanticsPatternOption patternSemantics; + private final FromGraphClause fromGraphClause; + private final AliasLookupTable aliasLookupTable; + private final VariableRemapCloneVisitor remapCloneVisitor; + private final GraphixDeepCopyVisitor deepCopyVisitor; + + public MatchSemanticAction(GraphixRewritingContext graphixRewritingContext, FromGraphClause fromGraphClause, + AliasLookupTable aliasLookupTable) throws CompilationException { + this.fromGraphClause = fromGraphClause; + this.aliasLookupTable = aliasLookupTable; + this.remapCloneVisitor = new VariableRemapCloneVisitor(graphixRewritingContext); + this.deepCopyVisitor = new GraphixDeepCopyVisitor(); + + // Determine our BGP query semantics. + String patternConfigKeyName = SemanticsPatternOption.OPTION_KEY_NAME; + this.patternSemantics = (SemanticsPatternOption) graphixRewritingContext.getSetting(patternConfigKeyName); + + // Determine our navigation query semantics. + String navConfigKeyName = SemanticsNavigationOption.OPTION_KEY_NAME; + this.navigationSemantics = (SemanticsNavigationOption) graphixRewritingContext.getSetting(navConfigKeyName); + } + + private static List<OperatorExpr> generateIsomorphismConjuncts(List<VariableExpr> variableList) { + List<OperatorExpr> isomorphismConjuncts = new ArrayList<>(); + + // Find all unique pairs from our list of variables. + for (int i = 0; i < variableList.size(); i++) { + for (int j = i + 1; j < variableList.size(); j++) { + OperatorExpr inequalityConjunct = new OperatorExpr(); + inequalityConjunct.addOperator(OperatorType.NEQ); + inequalityConjunct.addOperand(variableList.get(i)); + inequalityConjunct.addOperand(variableList.get(j)); + isomorphismConjuncts.add(inequalityConjunct); + } + } + + return isomorphismConjuncts; + } + + @Override + public void apply(LoweringEnvironment loweringEnvironment) throws CompilationException { + Map<ElementLabel, List<VariableExpr>> vertexVariableMap = new HashMap<>(); + Map<ElementLabel, List<VariableExpr>> edgeVariableMap = new HashMap<>(); + + // Populate the collections above. + fromGraphClause.accept(new AbstractGraphixQueryVisitor() { + private void populateVariableMap(ElementLabel label, VariableExpr variableExpr, + Map<ElementLabel, List<VariableExpr>> labelVariableMap) { + Function<VariableExpr, Boolean> mapMatchFinder = v -> { + final String variableName = SqlppVariableUtil.toUserDefinedName(variableExpr.getVar().getValue()); + return variableName.equals(SqlppVariableUtil.toUserDefinedName(v.getVar().getValue())); + }; + if (labelVariableMap.containsKey(label)) { + if (labelVariableMap.get(label).stream().noneMatch(mapMatchFinder::apply)) { + labelVariableMap.get(label).add(variableExpr); + } + + } else { + List<VariableExpr> variableList = new ArrayList<>(); + variableList.add(variableExpr); + labelVariableMap.put(label, variableList); + } + } + + @Override + public Expression visit(FromGraphClause fromGraphClause, ILangExpression arg) throws CompilationException { + // We only want to explore the top level of our FROM-GRAPH-CLAUSEs. + for (MatchClause matchClause : fromGraphClause.getMatchClauses()) { + matchClause.accept(this, arg); + } + return null; + } + + @Override + public Expression visit(VertexPatternExpr vertexPatternExpr, ILangExpression arg) + throws CompilationException { + VariableExpr vertexVariable = vertexPatternExpr.getVariableExpr(); + VariableExpr iterationVariable = aliasLookupTable.getIterationAlias(vertexVariable); + ElementLabel elementLabel = vertexPatternExpr.getLabels().iterator().next(); + iterationVariable.setSourceLocation(vertexVariable.getSourceLocation()); + populateVariableMap(elementLabel, iterationVariable, vertexVariableMap); + return null; + } + + @Override + public Expression visit(EdgePatternExpr edgePatternExpr, ILangExpression arg) throws CompilationException { + EdgeDescriptor edgeDescriptor = edgePatternExpr.getEdgeDescriptor(); + VariableExpr edgeVariable = edgeDescriptor.getVariableExpr(); + VariableExpr iterationVariable = aliasLookupTable.getIterationAlias(edgeVariable); + ElementLabel elementLabel = edgeDescriptor.getEdgeLabels().iterator().next(); + iterationVariable.setSourceLocation(edgeVariable.getSourceLocation()); + populateVariableMap(elementLabel, iterationVariable, edgeVariableMap); + return null; + } + }, null); + + // Construct our isomorphism conjuncts. + List<OperatorExpr> isomorphismConjuncts = new ArrayList<>(); + if (patternSemantics == SemanticsPatternOption.ISOMORPHISM + || patternSemantics == SemanticsPatternOption.VERTEX_ISOMORPHISM) { + vertexVariableMap.values().stream().map(MatchSemanticAction::generateIsomorphismConjuncts) + .forEach(isomorphismConjuncts::addAll); + } + if (patternSemantics == SemanticsPatternOption.ISOMORPHISM + || patternSemantics == SemanticsPatternOption.EDGE_ISOMORPHISM) { + edgeVariableMap.values().stream().map(MatchSemanticAction::generateIsomorphismConjuncts) + .forEach(isomorphismConjuncts::addAll); + } + + // Iterate through our clause sequence. + remapCloneVisitor.resetSubstitutions(); + loweringEnvironment.acceptTransformer(new ILowerListTransformer() { + private final Set<VariableExpr> visitedVariables = new HashSet<>(); + + @Override + public void accept(ClauseCollection lowerList) throws CompilationException { + List<AbstractClause> nonRepresentativeClauses = lowerList.getNonRepresentativeClauses(); + ListIterator<AbstractClause> clauseIterator = nonRepresentativeClauses.listIterator(); + while (clauseIterator.hasNext()) { + AbstractClause workingClause = clauseIterator.next(); + if (workingClause.getClauseType() == Clause.ClauseType.LET_CLAUSE) { + LetClause letClause = (LetClause) workingClause; + visitedVariables.add(letClause.getVarExpr()); + + } else if (workingClause.getClauseType() == Clause.ClauseType.EXTENSION) { + // Navigation semantics will be introduced at the plan translator. + LowerSwitchClause lowerSwitchClause = (LowerSwitchClause) workingClause; + lowerSwitchClause.setNavigationSemantics(navigationSemantics); + + } else if (workingClause.getClauseType() == Clause.ClauseType.WHERE_CLAUSE) { + continue; + + } else { + AbstractBinaryCorrelateClause correlateClause = (AbstractBinaryCorrelateClause) workingClause; + visitedVariables.add(correlateClause.getRightVariable()); + + // If we encounter a LEFT-JOIN, then we have created a LEFT-MATCH branch. + if (workingClause.getClauseType() == Clause.ClauseType.JOIN_CLAUSE) { + JoinClause joinClause = (JoinClause) workingClause; + if (joinClause.getJoinType() == JoinType.LEFTOUTER) { + acceptLeftMatchJoin(clauseIterator, joinClause); + } + } + } + + // Only introduce our conjunct if we have visited both variables (eagerly). + Set<OperatorExpr> appliedIsomorphismConjuncts = new HashSet<>(); + for (OperatorExpr isomorphismConjunct : isomorphismConjuncts) { + List<Expression> operandList = isomorphismConjunct.getExprList(); + VariableExpr termVariable1 = ((VariableExpr) operandList.get(0)); + VariableExpr termVariable2 = ((VariableExpr) operandList.get(1)); + if (visitedVariables.contains(termVariable1) && visitedVariables.contains(termVariable2)) { + clauseIterator.add(new WhereClause(isomorphismConjunct)); + appliedIsomorphismConjuncts.add(isomorphismConjunct); + } + } + isomorphismConjuncts.removeAll(appliedIsomorphismConjuncts); + } + } + + private void acceptLeftMatchJoin(ListIterator<AbstractClause> clauseIterator, JoinClause joinClause) + throws CompilationException { + // We can make the following assumptions about our JOIN here (i.e. the casts here are valid). + Expression rightExpression = joinClause.getRightExpression(); + SelectExpression selectExpression = (SelectExpression) rightExpression; + SelectSetOperation selectSetOperation = selectExpression.getSelectSetOperation(); + SelectBlock selectBlock = selectSetOperation.getLeftInput().getSelectBlock(); + FromGraphClause fromGraphClause = (FromGraphClause) selectBlock.getFromClause(); + LowerListClause lowerClause = (LowerListClause) fromGraphClause.getLowerClause(); + Set<VariableExpr> localLiveVariables = new HashSet<>(); + ListIterator<AbstractClause> leftClauseIterator = + lowerClause.getClauseCollection().getNonRepresentativeClauses().listIterator(); + while (leftClauseIterator.hasNext()) { + AbstractClause workingClause = leftClauseIterator.next(); + VariableExpr rightVariable; + if (workingClause.getClauseType() == Clause.ClauseType.WHERE_CLAUSE) { + continue; + + } else if (workingClause.getClauseType() == Clause.ClauseType.LET_CLAUSE) { + rightVariable = ((LetClause) workingClause).getVarExpr(); + + } else { + rightVariable = ((AbstractBinaryCorrelateClause) workingClause).getRightVariable(); + } + + // Add our isomorphism conjunct to our main iterator. + localLiveVariables.add(rightVariable); + Set<OperatorExpr> appliedIsomorphismConjuncts = new HashSet<>(); + for (OperatorExpr isomorphismConjunct : isomorphismConjuncts) { + List<Expression> operandList = isomorphismConjunct.getExprList(); + VariableExpr termVariable1 = ((VariableExpr) operandList.get(0)); + VariableExpr termVariable2 = ((VariableExpr) operandList.get(1)); + VariableExpr leftVariable; + if (termVariable1.equals(rightVariable)) { + leftVariable = termVariable2; + + } else if (termVariable2.equals(rightVariable)) { + leftVariable = termVariable1; + + } else { + continue; + } + + // Is our left variable introduced in our LEFT-CLAUSE-COLLECTION? Add the conjunct here. + if (localLiveVariables.contains(leftVariable)) { + leftClauseIterator.add(new WhereClause(isomorphismConjunct)); + appliedIsomorphismConjuncts.add(isomorphismConjunct); + visitedVariables.add(rightVariable); + continue; + } + + // Have we seen our left variable somewhere else? Add our conjunct in our main collection. + if (visitedVariables.contains(leftVariable)) { + VariableExpr joinVariable1 = deepCopyVisitor.visit(joinClause.getRightVariable(), null); + VariableExpr joinVariable2 = deepCopyVisitor.visit(joinClause.getRightVariable(), null); + FieldAccessor fieldAccessor1 = new FieldAccessor(joinVariable1, rightVariable.getVar()); + FieldAccessor fieldAccessor2 = new FieldAccessor(joinVariable2, rightVariable.getVar()); + remapCloneVisitor.addSubstitution(rightVariable, fieldAccessor1); + ILangExpression qualifiedConjunct = remapCloneVisitor.substitute(isomorphismConjunct); + + // Our right variable can also be optional. + FunctionSignature functionSignature = new FunctionSignature(BuiltinFunctions.IS_MISSING); + CallExpr isMissingCallExpr = new CallExpr(functionSignature, List.of(fieldAccessor2)); + OperatorExpr disjunctionExpr = new OperatorExpr(); + disjunctionExpr.addOperator(OperatorType.OR); + disjunctionExpr.addOperand(isMissingCallExpr); + disjunctionExpr.addOperand((Expression) qualifiedConjunct); + clauseIterator.add(new WhereClause(disjunctionExpr)); + appliedIsomorphismConjuncts.add(isomorphismConjunct); + visitedVariables.add(rightVariable); + } + } + isomorphismConjuncts.removeAll(appliedIsomorphismConjuncts); + } + } + }); + } +}
diff --git a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/lower/action/PathPatternAction.java b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrite/lower/action/PathPatternAction.java similarity index 76% rename from asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/lower/action/PathPatternAction.java rename to asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrite/lower/action/PathPatternAction.java index bb9f849..6a6f849 100644 --- a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/lower/action/PathPatternAction.java +++ b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrite/lower/action/PathPatternAction.java
@@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.asterix.graphix.lang.rewrites.lower.action; +package org.apache.asterix.graphix.lang.rewrite.lower.action; import java.util.ArrayList; import java.util.Collection; @@ -24,18 +24,17 @@ import java.util.stream.Collectors; import org.apache.asterix.common.exceptions.CompilationException; -import org.apache.asterix.graphix.lang.clause.CorrLetClause; import org.apache.asterix.graphix.lang.expression.EdgePatternExpr; import org.apache.asterix.graphix.lang.expression.PathPatternExpr; import org.apache.asterix.graphix.lang.expression.VertexPatternExpr; -import org.apache.asterix.graphix.lang.rewrites.lower.LoweringEnvironment; +import org.apache.asterix.graphix.lang.rewrite.lower.LoweringEnvironment; +import org.apache.asterix.graphix.type.MaterializePathTypeComputer; import org.apache.asterix.lang.common.base.Expression; import org.apache.asterix.lang.common.clause.LetClause; import org.apache.asterix.lang.common.expression.FieldBinding; import org.apache.asterix.lang.common.expression.ListConstructor; import org.apache.asterix.lang.common.expression.LiteralExpr; import org.apache.asterix.lang.common.expression.RecordConstructor; -import org.apache.asterix.lang.common.expression.VariableExpr; import org.apache.asterix.lang.common.literal.StringLiteral; /** @@ -44,9 +43,6 @@ * edge variables. */ public class PathPatternAction implements IEnvironmentAction { - public final static String PATH_VERTICES_FIELD_NAME = "Vertices"; - public final static String PATH_EDGES_FIELD_NAME = "Edges"; - private final PathPatternExpr pathPatternExpr; public PathPatternAction(PathPatternExpr pathPatternExpr) { @@ -56,28 +52,27 @@ @Override public void apply(LoweringEnvironment loweringEnvironment) throws CompilationException { if (pathPatternExpr.getVariableExpr() != null) { - loweringEnvironment.acceptTransformer(clauseSequence -> { + loweringEnvironment.acceptTransformer(lowerList -> { // We have a named non-sub-path. RecordConstructor recordConstructor = new RecordConstructor(); + recordConstructor.setSourceLocation(pathPatternExpr.getSourceLocation()); List<VertexPatternExpr> vertexExpressions = pathPatternExpr.getVertexExpressions(); List<EdgePatternExpr> edgeExpressions = pathPatternExpr.getEdgeExpressions(); buildPathRecord(vertexExpressions, edgeExpressions, recordConstructor); - VariableExpr pathVariableExpr = new VariableExpr(pathPatternExpr.getVariableExpr().getVar()); - clauseSequence.addDeferredClause(new CorrLetClause(recordConstructor, pathVariableExpr, null)); + lowerList.addPathBinding(pathPatternExpr.getVariableExpr(), recordConstructor); }); } for (LetClause reboundSubPathExpression : pathPatternExpr.getReboundSubPathList()) { - loweringEnvironment.acceptTransformer(clauseSequence -> { + loweringEnvironment.acceptTransformer(lowerList -> { // We have sub-paths we need to introduce. - VariableExpr pathVariableExpr = new VariableExpr(reboundSubPathExpression.getVarExpr().getVar()); Expression reboundExpression = reboundSubPathExpression.getBindingExpr(); - clauseSequence.addDeferredClause(new CorrLetClause(reboundExpression, pathVariableExpr, null)); + lowerList.addPathBinding(reboundSubPathExpression.getVarExpr(), reboundExpression); }); } } public static void buildPathRecord(Collection<VertexPatternExpr> vertexExpressions, - List<EdgePatternExpr> edgeExpressions, RecordConstructor outputRecordConstructor) { + Collection<EdgePatternExpr> edgeExpressions, RecordConstructor outputRecordConstructor) { List<FieldBinding> fieldBindingList = new ArrayList<>(); // Assemble our vertices into a list. @@ -86,7 +81,9 @@ ListConstructor vertexVariableList = new ListConstructor(); vertexVariableList.setType(ListConstructor.Type.ORDERED_LIST_CONSTRUCTOR); vertexVariableList.setExprList(vertexVariableExprList); - LiteralExpr verticesFieldName = new LiteralExpr(new StringLiteral(PATH_VERTICES_FIELD_NAME)); + vertexVariableList.setSourceLocation(outputRecordConstructor.getSourceLocation()); + StringLiteral verticesFieldNameLiteral = new StringLiteral(MaterializePathTypeComputer.VERTICES_FIELD_NAME); + LiteralExpr verticesFieldName = new LiteralExpr(verticesFieldNameLiteral); fieldBindingList.add(new FieldBinding(verticesFieldName, vertexVariableList)); // Assemble our edges into a list. @@ -95,7 +92,9 @@ ListConstructor edgeVariableList = new ListConstructor(); edgeVariableList.setType(ListConstructor.Type.ORDERED_LIST_CONSTRUCTOR); edgeVariableList.setExprList(edgeVariableExprList); - LiteralExpr edgesFieldName = new LiteralExpr(new StringLiteral(PATH_EDGES_FIELD_NAME)); + edgeVariableList.setSourceLocation(outputRecordConstructor.getSourceLocation()); + StringLiteral edgesFieldNameLiteral = new StringLiteral(MaterializePathTypeComputer.EDGES_FIELD_NAME); + LiteralExpr edgesFieldName = new LiteralExpr(edgesFieldNameLiteral); fieldBindingList.add(new FieldBinding(edgesFieldName, edgeVariableList)); // Set our field bindings in our output record constructor.
diff --git a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrite/lower/struct/ClauseCollection.java b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrite/lower/struct/ClauseCollection.java new file mode 100644 index 0000000..0c10586 --- /dev/null +++ b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrite/lower/struct/ClauseCollection.java
@@ -0,0 +1,232 @@ +/* + * 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.graphix.lang.rewrite.lower.struct; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Objects; +import java.util.function.Function; +import java.util.stream.Collectors; + +import org.apache.asterix.common.exceptions.CompilationException; +import org.apache.asterix.common.exceptions.ErrorCode; +import org.apache.asterix.graphix.lang.clause.LowerSwitchClause; +import org.apache.asterix.lang.common.base.AbstractClause; +import org.apache.asterix.lang.common.base.Clause; +import org.apache.asterix.lang.common.base.Expression; +import org.apache.asterix.lang.common.clause.LetClause; +import org.apache.asterix.lang.common.expression.VariableExpr; +import org.apache.asterix.lang.sqlpp.clause.AbstractBinaryCorrelateClause; +import org.apache.hyracks.algebricks.common.utils.Pair; +import org.apache.hyracks.api.exceptions.SourceLocation; + +public class ClauseCollection implements Iterable<AbstractClause> { + private final List<LetClause> representativeVertexBindings = new ArrayList<>(); + private final List<LetClause> representativeEdgeBindings = new ArrayList<>(); + private final List<LetClause> representativePathBindings = new ArrayList<>(); + private final List<AbstractClause> nonRepresentativeClauses = new LinkedList<>(); + private final List<Pair<VariableExpr, LetClause>> eagerVertexBindings = new ArrayList<>(); + private final List<AbstractBinaryCorrelateClause> userCorrelateClauses = new ArrayList<>(); + private final SourceLocation sourceLocation; + + public ClauseCollection(SourceLocation sourceLocation) { + this.sourceLocation = sourceLocation; + } + + public void addVertexBinding(VariableExpr bindingVar, Expression boundExpression) throws CompilationException { + if (representativeVertexBindings.stream().anyMatch(v -> v.getVarExpr().equals(bindingVar))) { + throw new CompilationException(ErrorCode.COMPILATION_ILLEGAL_STATE, "Duplicate vertex binding found!"); + } + representativeVertexBindings.add(new LetClause(bindingVar, boundExpression)); + } + + public void addEdgeBinding(VariableExpr bindingVar, Expression boundExpression) throws CompilationException { + if (representativeEdgeBindings.stream().anyMatch(v -> v.getVarExpr().equals(bindingVar))) { + throw new CompilationException(ErrorCode.COMPILATION_ILLEGAL_STATE, "Duplicate edge binding found!"); + } + representativeEdgeBindings.add(new LetClause(bindingVar, boundExpression)); + } + + public void addPathBinding(VariableExpr bindingVar, Expression boundExpression) throws CompilationException { + if (representativePathBindings.stream().anyMatch(v -> v.getVarExpr().equals(bindingVar))) { + throw new CompilationException(ErrorCode.COMPILATION_ILLEGAL_STATE, "Duplicate path binding found!"); + } + representativePathBindings.add(new LetClause(bindingVar, boundExpression)); + } + + @SuppressWarnings("fallthrough") + public void addNonRepresentativeClause(AbstractClause abstractClause) throws CompilationException { + switch (abstractClause.getClauseType()) { + case JOIN_CLAUSE: + case UNNEST_CLAUSE: + case LET_CLAUSE: + case WHERE_CLAUSE: + nonRepresentativeClauses.add(abstractClause); + break; + + case EXTENSION: + if (abstractClause instanceof LowerSwitchClause) { + nonRepresentativeClauses.add(abstractClause); + break; + } + + default: + throw new CompilationException(ErrorCode.COMPILATION_ILLEGAL_STATE, abstractClause.getSourceLocation(), + "Illegal clause inserted into the lower clause list!"); + } + } + + public void addEagerVertexBinding(VariableExpr representativeVariable, LetClause vertexBinding) + throws CompilationException { + // An eager vertex binding should be found in the non-representative clauses... + boolean isIllegalVertexBinding = true; + for (AbstractClause nonRepresentativeClause : nonRepresentativeClauses) { + if (nonRepresentativeClause.getClauseType() != Clause.ClauseType.LET_CLAUSE) { + continue; + } + LetClause nonRepresentativeLetClause = (LetClause) nonRepresentativeClause; + if (nonRepresentativeLetClause.getBindingExpr().equals(vertexBinding.getBindingExpr())) { + isIllegalVertexBinding = false; + break; + } + } + if (isIllegalVertexBinding) { + throw new CompilationException(ErrorCode.COMPILATION_ILLEGAL_STATE, "Illegal eager vertex binding added!"); + } + + // ... and the representative vertex bindings. + isIllegalVertexBinding = true; + for (LetClause representativeVertexBinding : representativeVertexBindings) { + if (representativeVertexBinding.getBindingExpr().equals(vertexBinding.getBindingExpr()) + && representativeVariable.equals(representativeVertexBinding.getVarExpr())) { + isIllegalVertexBinding = false; + break; + } + } + if (isIllegalVertexBinding) { + throw new CompilationException(ErrorCode.COMPILATION_ILLEGAL_STATE, "Illegal eager vertex binding added!"); + } + eagerVertexBindings.add(new Pair<>(representativeVariable, vertexBinding)); + } + + public void addUserDefinedCorrelateClause(AbstractBinaryCorrelateClause correlateClause) { + userCorrelateClauses.add(correlateClause); + } + + public List<LetClause> getRepresentativeVertexBindings() { + return representativeVertexBindings; + } + + public List<Pair<VariableExpr, LetClause>> getEagerVertexBindings() { + return eagerVertexBindings; + } + + public List<LetClause> getRepresentativeEdgeBindings() { + return representativeEdgeBindings; + } + + public List<LetClause> getRepresentativePathBindings() { + return representativePathBindings; + } + + public List<AbstractClause> getNonRepresentativeClauses() { + return nonRepresentativeClauses; + } + + public List<AbstractBinaryCorrelateClause> getUserDefinedCorrelateClauses() { + return userCorrelateClauses; + } + + public SourceLocation getSourceLocation() { + return sourceLocation; + } + + @Override + public Iterator<AbstractClause> iterator() { + Iterator<AbstractClause> nonRepresentativeIterator = nonRepresentativeClauses.iterator(); + Iterator<LetClause> representativeVertexIterator = representativeVertexBindings.iterator(); + Iterator<LetClause> representativeEdgeIterator = representativeEdgeBindings.iterator(); + Iterator<LetClause> representativePathIterator = representativePathBindings.iterator(); + Iterator<AbstractBinaryCorrelateClause> userCorrelateIterator = userCorrelateClauses.iterator(); + return new Iterator<>() { + @Override + public boolean hasNext() { + return nonRepresentativeIterator.hasNext() || representativeVertexIterator.hasNext() + || representativeEdgeIterator.hasNext() || representativePathIterator.hasNext() + || userCorrelateIterator.hasNext(); + } + + @Override + public AbstractClause next() { + if (nonRepresentativeIterator.hasNext()) { + return nonRepresentativeIterator.next(); + } + if (representativeVertexIterator.hasNext()) { + return representativeVertexIterator.next(); + } + if (representativeEdgeIterator.hasNext()) { + return representativeEdgeIterator.next(); + } + if (representativePathIterator.hasNext()) { + return representativePathIterator.next(); + } + if (userCorrelateIterator.hasNext()) { + return userCorrelateIterator.next(); + } + throw new IllegalStateException(); + } + }; + } + + @Override + public int hashCode() { + return Objects.hash(nonRepresentativeClauses, representativeVertexBindings, representativeEdgeBindings, + eagerVertexBindings, representativePathBindings, userCorrelateClauses, sourceLocation); + } + + @Override + public boolean equals(Object object) { + if (this == object) { + return true; + } + if (!(object instanceof ClauseCollection)) { + return false; + } + ClauseCollection that = (ClauseCollection) object; + return Objects.equals(this.nonRepresentativeClauses, that.nonRepresentativeClauses) + && Objects.equals(this.representativeVertexBindings, that.representativeVertexBindings) + && Objects.equals(this.representativeEdgeBindings, that.representativeEdgeBindings) + && Objects.equals(this.representativePathBindings, that.representativePathBindings) + && Objects.equals(this.eagerVertexBindings, that.eagerVertexBindings) + && Objects.equals(this.userCorrelateClauses, that.userCorrelateClauses) + && Objects.equals(this.sourceLocation, that.sourceLocation); + } + + @Override + public String toString() { + final Function<List<LetClause>, String> bindingPrinter = letClauses -> letClauses.stream() + .map(b -> b.getVarExpr().getVar().toString()).collect(Collectors.joining(",")); + return String.format("clause-collection:\n\t%s\n\t%s\n\t%s\n", + "vertices: " + bindingPrinter.apply(representativeVertexBindings), + "edges: " + bindingPrinter.apply(representativeEdgeBindings), + "paths: " + bindingPrinter.apply(representativePathBindings)); + } +}
diff --git a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrite/lower/struct/CollectionTable.java b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrite/lower/struct/CollectionTable.java new file mode 100644 index 0000000..29b1fa4 --- /dev/null +++ b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrite/lower/struct/CollectionTable.java
@@ -0,0 +1,174 @@ +/* + * 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.graphix.lang.rewrite.lower.struct; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +import org.apache.asterix.graphix.lang.expression.EdgePatternExpr; +import org.apache.asterix.graphix.lang.struct.EdgeDescriptor; +import org.apache.asterix.graphix.lang.struct.ElementLabel; +import org.apache.hyracks.algebricks.common.utils.Pair; + +public class CollectionTable implements Iterable<ClauseCollection> { + private final Map<ElementLabel, List<Entry>> collectionMap = new HashMap<>(); + private Map<ElementLabel, StateContainer> inputMap; + private Map<ElementLabel, StateContainer> outputMap; + + public static class Entry { + private final ElementLabel edgeLabel; + private final ElementLabel destinationLabel; + private final ClauseCollection clauseCollection; + private final EdgeDescriptor.EdgeDirection edgeDirection; + + private Entry(ElementLabel edgeLabel, ElementLabel destinationLabel, ClauseCollection clauseCollection, + EdgeDescriptor.EdgeDirection edgeDirection) { + this.edgeLabel = edgeLabel; + this.destinationLabel = destinationLabel; + this.clauseCollection = clauseCollection; + this.edgeDirection = edgeDirection; + } + + public ElementLabel getEdgeLabel() { + return edgeLabel; + } + + public ElementLabel getDestinationLabel() { + return destinationLabel; + } + + public ClauseCollection getClauseCollection() { + return clauseCollection; + } + + public EdgeDescriptor.EdgeDirection getEdgeDirection() { + return edgeDirection; + } + + @Override + public int hashCode() { + return Objects.hash(edgeLabel, destinationLabel, clauseCollection, edgeDirection); + } + + @Override + public boolean equals(Object object) { + if (this == object) { + return true; + } + if (!(object instanceof Entry)) { + return false; + } + Entry that = (Entry) object; + return Objects.equals(this.edgeLabel, that.edgeLabel) + && Objects.equals(this.destinationLabel, that.destinationLabel) + && Objects.equals(this.clauseCollection, that.clauseCollection) + && Objects.equals(this.edgeDirection, that.edgeDirection); + } + } + + public void setInputMap(Map<ElementLabel, StateContainer> inputMap) { + this.inputMap = inputMap; + } + + public void setOutputMap(Map<ElementLabel, StateContainer> outputMap) { + this.outputMap = outputMap; + } + + public void putCollection(EdgePatternExpr edgePatternExpr, boolean isEvaluatingFromLeft, + ClauseCollection clauseCollection) { + // Determine our source and destination labels. + ElementLabel startLabel, edgeLabel, endLabel; + if (isEvaluatingFromLeft) { + startLabel = edgePatternExpr.getLeftVertex().getLabels().iterator().next(); + endLabel = edgePatternExpr.getRightVertex().getLabels().iterator().next(); + + } else { + startLabel = edgePatternExpr.getRightVertex().getLabels().iterator().next(); + endLabel = edgePatternExpr.getLeftVertex().getLabels().iterator().next(); + } + edgeLabel = edgePatternExpr.getEdgeDescriptor().getEdgeLabels().iterator().next(); + + // Insert our collection. + EdgeDescriptor.EdgeDirection edgeDirection = edgePatternExpr.getEdgeDescriptor().getEdgeDirection(); + putCollection(startLabel, edgeLabel, endLabel, clauseCollection, edgeDirection); + } + + public void putCollection(ElementLabel startLabel, ElementLabel edgeLabel, ElementLabel endLabel, + ClauseCollection clauseCollection, EdgeDescriptor.EdgeDirection edgeDirection) { + if (!collectionMap.containsKey(startLabel)) { + collectionMap.put(startLabel, new ArrayList<>()); + } + collectionMap.get(startLabel).add(new Entry(edgeLabel, endLabel, clauseCollection, edgeDirection)); + } + + public Map<ElementLabel, StateContainer> getInputMap() { + return inputMap; + } + + public Map<ElementLabel, StateContainer> getOutputMap() { + return outputMap; + } + + public Iterator<Pair<ElementLabel, List<Entry>>> entryIterator() { + return collectionMap.entrySet().stream().map(e -> new Pair<>(e.getKey(), e.getValue())).iterator(); + } + + @Override + public Iterator<ClauseCollection> iterator() { + return collectionMap.values().stream().flatMap(e -> e.stream().map(c -> c.clauseCollection)).iterator(); + } + + @Override + public int hashCode() { + return Objects.hash(collectionMap, inputMap, outputMap); + } + + @Override + public boolean equals(Object object) { + if (this == object) { + return true; + } + if (!(object instanceof CollectionTable)) { + return false; + } + CollectionTable that = (CollectionTable) object; + return Objects.equals(this.collectionMap, that.collectionMap) && Objects.equals(this.inputMap, that.inputMap) + && Objects.equals(this.outputMap, that.outputMap); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("collection-table: \n"); + Iterator<Pair<ElementLabel, List<Entry>>> entryIterator = this.entryIterator(); + while (entryIterator.hasNext()) { + Pair<ElementLabel, List<Entry>> mapEntry = entryIterator.next(); + for (Entry entry : mapEntry.second) { + sb.append("\t").append(mapEntry.getFirst()).append(" to "); + sb.append(entry.getDestinationLabel()).append(" [ "); + sb.append(entry.getEdgeLabel()).append(" ]\n"); + } + } + return sb.toString(); + } +}
diff --git a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrite/lower/struct/StateContainer.java b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrite/lower/struct/StateContainer.java new file mode 100644 index 0000000..379bf78 --- /dev/null +++ b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrite/lower/struct/StateContainer.java
@@ -0,0 +1,66 @@ +/* + * 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.graphix.lang.rewrite.lower.struct; + +import java.util.Iterator; +import java.util.List; +import java.util.Objects; + +import org.apache.asterix.lang.common.expression.VariableExpr; + +public class StateContainer implements Iterable<VariableExpr> { + private final VariableExpr iterationVariable; + private final VariableExpr joinVariable; + + public StateContainer(VariableExpr iterationVariable, VariableExpr joinVariable) { + this.iterationVariable = iterationVariable; + this.joinVariable = joinVariable; + } + + public VariableExpr getIterationVariable() { + return iterationVariable; + } + + public VariableExpr getJoinVariable() { + return joinVariable; + } + + @Override + public int hashCode() { + return Objects.hash(iterationVariable, joinVariable); + } + + @Override + public boolean equals(Object object) { + if (this == object) { + return true; + } + if (!(object instanceof StateContainer)) { + return false; + } + StateContainer that = (StateContainer) object; + return Objects.equals(this.iterationVariable, that.iterationVariable) + && Objects.equals(this.joinVariable, that.joinVariable); + } + + @Override + public Iterator<VariableExpr> iterator() { + return List.of(iterationVariable, joinVariable).iterator(); + } +}
diff --git a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/print/GraphixASTPrintVisitor.java b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrite/print/GraphixASTPrintVisitor.java similarity index 85% rename from asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/print/GraphixASTPrintVisitor.java rename to asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrite/print/GraphixASTPrintVisitor.java index fba1762..2f70c01 100644 --- a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/print/GraphixASTPrintVisitor.java +++ b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrite/print/GraphixASTPrintVisitor.java
@@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.asterix.graphix.lang.rewrites.print; +package org.apache.asterix.graphix.lang.rewrite.print; import java.io.PrintWriter; import java.util.List; @@ -24,21 +24,19 @@ import org.apache.asterix.common.exceptions.CompilationException; import org.apache.asterix.graphix.lang.clause.FromGraphClause; -import org.apache.asterix.graphix.lang.clause.GraphSelectBlock; import org.apache.asterix.graphix.lang.clause.MatchClause; import org.apache.asterix.graphix.lang.expression.EdgePatternExpr; import org.apache.asterix.graphix.lang.expression.GraphConstructor; import org.apache.asterix.graphix.lang.expression.PathPatternExpr; import org.apache.asterix.graphix.lang.expression.VertexPatternExpr; -import org.apache.asterix.graphix.lang.rewrites.visitor.IGraphixLangVisitor; import org.apache.asterix.graphix.lang.statement.CreateGraphStatement; import org.apache.asterix.graphix.lang.statement.DeclareGraphStatement; import org.apache.asterix.graphix.lang.statement.GraphDropStatement; import org.apache.asterix.graphix.lang.statement.GraphElementDeclaration; import org.apache.asterix.graphix.lang.struct.ElementLabel; -import org.apache.asterix.lang.common.base.AbstractClause; +import org.apache.asterix.graphix.lang.visitor.base.IGraphixLangVisitor; import org.apache.asterix.lang.sqlpp.clause.AbstractBinaryCorrelateClause; -import org.apache.asterix.lang.sqlpp.clause.SelectBlock; +import org.apache.asterix.lang.sqlpp.clause.FromClause; import org.apache.asterix.lang.sqlpp.visitor.SqlppAstPrintVisitor; public class GraphixASTPrintVisitor extends SqlppAstPrintVisitor implements IGraphixLangVisitor<Void, Integer> { @@ -80,34 +78,9 @@ } @Override - public Void visit(SelectBlock selectBlock, Integer step) throws CompilationException { - return (selectBlock instanceof GraphSelectBlock) ? this.visit((GraphSelectBlock) selectBlock, step) - : super.visit(selectBlock, step); - } - - @Override - public Void visit(GraphSelectBlock graphSelectBlock, Integer step) throws CompilationException { - graphSelectBlock.getSelectClause().accept(this, step); - if (graphSelectBlock.hasFromClause()) { - graphSelectBlock.getFromClause().accept(this, step); - - } else if (graphSelectBlock.hasFromGraphClause()) { - graphSelectBlock.getFromGraphClause().accept(this, step); - } - if (graphSelectBlock.hasLetWhereClauses()) { - for (AbstractClause letWhereClause : graphSelectBlock.getLetWhereList()) { - letWhereClause.accept(this, step); - } - } - if (graphSelectBlock.hasGroupbyClause()) { - graphSelectBlock.getGroupbyClause().accept(this, step); - if (graphSelectBlock.hasLetHavingClausesAfterGroupby()) { - for (AbstractClause letHavingClause : graphSelectBlock.getLetHavingListAfterGroupby()) { - letHavingClause.accept(this, step); - } - } - } - return null; + public Void visit(FromClause fromClause, Integer step) throws CompilationException { + return (fromClause instanceof FromGraphClause) ? this.visit((FromGraphClause) fromClause, step) + : super.visit(fromClause, step); } @Override
diff --git a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/print/GraphixASTPrintVisitorFactory.java b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrite/print/GraphixASTPrintVisitorFactory.java similarity index 95% rename from asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/print/GraphixASTPrintVisitorFactory.java rename to asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrite/print/GraphixASTPrintVisitorFactory.java index ff00077..f3955e6 100644 --- a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/print/GraphixASTPrintVisitorFactory.java +++ b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrite/print/GraphixASTPrintVisitorFactory.java
@@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.asterix.graphix.lang.rewrites.print; +package org.apache.asterix.graphix.lang.rewrite.print; import java.io.PrintWriter;
diff --git a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/print/SqlppASTPrintQueryVisitor.java b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrite/print/SqlppASTPrintQueryVisitor.java similarity index 84% rename from asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/print/SqlppASTPrintQueryVisitor.java rename to asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrite/print/SqlppASTPrintQueryVisitor.java index 0a1b942..33ef30e 100644 --- a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/print/SqlppASTPrintQueryVisitor.java +++ b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrite/print/SqlppASTPrintQueryVisitor.java
@@ -14,20 +14,30 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.asterix.graphix.lang.rewrites.print; +package org.apache.asterix.graphix.lang.rewrite.print; import java.io.PrintWriter; +import java.util.ArrayList; import java.util.Collection; +import java.util.Iterator; import java.util.List; +import java.util.Spliterator; +import java.util.function.Function; import java.util.stream.Collectors; import java.util.stream.IntStream; +import java.util.stream.StreamSupport; import org.apache.asterix.common.exceptions.CompilationException; -import org.apache.asterix.graphix.lang.clause.CorrLetClause; -import org.apache.asterix.graphix.lang.clause.CorrWhereClause; +import org.apache.asterix.graphix.lang.clause.FromGraphClause; +import org.apache.asterix.graphix.lang.clause.LowerListClause; +import org.apache.asterix.graphix.lang.clause.LowerSwitchClause; +import org.apache.asterix.graphix.lang.rewrite.lower.struct.CollectionTable; +import org.apache.asterix.graphix.lang.struct.ElementLabel; +import org.apache.asterix.lang.common.base.AbstractClause; import org.apache.asterix.lang.common.base.Clause; import org.apache.asterix.lang.common.base.Expression; import org.apache.asterix.lang.common.base.ILangExpression; +import org.apache.asterix.lang.common.base.IVisitorExtension; import org.apache.asterix.lang.common.clause.GroupbyClause; import org.apache.asterix.lang.common.clause.LetClause; import org.apache.asterix.lang.common.clause.LimitClause; @@ -35,7 +45,6 @@ import org.apache.asterix.lang.common.clause.WhereClause; import org.apache.asterix.lang.common.expression.CallExpr; import org.apache.asterix.lang.common.expression.FieldAccessor; -import org.apache.asterix.lang.common.expression.GbyVariableExpressionPair; import org.apache.asterix.lang.common.expression.IfExpr; import org.apache.asterix.lang.common.expression.IndexAccessor; import org.apache.asterix.lang.common.expression.ListConstructor; @@ -47,6 +56,7 @@ import org.apache.asterix.lang.common.expression.UnaryExpr; import org.apache.asterix.lang.common.expression.VariableExpr; import org.apache.asterix.lang.common.statement.Query; +import org.apache.asterix.lang.common.struct.Identifier; import org.apache.asterix.lang.common.struct.OperatorType; import org.apache.asterix.lang.sqlpp.clause.AbstractBinaryCorrelateClause; import org.apache.asterix.lang.sqlpp.clause.FromClause; @@ -69,16 +79,11 @@ import org.apache.asterix.lang.sqlpp.visitor.base.AbstractSqlppQueryExpressionVisitor; import org.apache.asterix.om.functions.BuiltinFunctions; import org.apache.hyracks.algebricks.common.exceptions.NotImplementedException; +import org.apache.hyracks.algebricks.common.utils.Pair; import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier; /** - * Visitor class to create a _valid_ SQL++ query out of a **valid** AST. The only exception to the validity of the - * printed SQL++ is the presence of {@link CorrLetClause} and {@link CorrWhereClause}, denoted using "WITH ..." before - * (or after) JOIN / UNNEST clauses. We make the following assumptions: - * 1. The entry point is a {@link Query}, which we will print to. - * 2. All {@link GroupbyClause} nodes only have one set of {@link GbyVariableExpressionPair} (i.e. the GROUP-BY - * rewrites should have fired). - * 3. No {@link WindowExpression} nodes are included (this is on the TODO list). + * Visitor class to create a somewhat-valid SQL++ query out of a <b>valid</b> AST. */ public class SqlppASTPrintQueryVisitor extends AbstractSqlppQueryExpressionVisitor<String, Void> { private final PrintWriter printWriter; @@ -194,8 +199,58 @@ @Override public String visit(FromClause fromClause, Void arg) { - return String.format(" FROM %s ", fromClause.getFromTerms().stream().map(this::visitAndSwallowException) - .collect(Collectors.joining(", "))); + final Function<AbstractClause, String> abstractClauseFormatter = c -> { + if (c.getClauseType() == Clause.ClauseType.LET_CLAUSE) { + return " LET " + visitAndSwallowException(c); + + } else if (c.getClauseType() == Clause.ClauseType.WHERE_CLAUSE) { + return " WHERE " + visitAndSwallowException(c); + } + return visitAndSwallowException(c); + }; + final Function<LowerSwitchClause, String> fixedPointClauseFormatter = c -> { + LowerSwitchClause.ClauseOutputEnvironment outputEnvironment = c.getClauseOutputEnvironment(); + String outputString = + String.format("`%s` = { \"vertex-iteration\": `%s`, \"vertex-join\": `%s`, \"path\": `%s` }", + formatIdentifierForQuery(outputEnvironment.getOutputVariable().getVar()), + formatIdentifierForQuery(outputEnvironment.getOutputVertexIterationVariable().getVar()), + formatIdentifierForQuery(outputEnvironment.getOutputVertexJoinVariable().getVar()), + formatIdentifierForQuery(outputEnvironment.getPathVariable().getVar())); + Iterator<Pair<ElementLabel, List<CollectionTable.Entry>>> entryIterator = + c.getCollectionLookupTable().entryIterator(); + List<String> collectionKeyValuePairStrings = new ArrayList<>(); + while (entryIterator.hasNext()) { + Pair<ElementLabel, List<CollectionTable.Entry>> mapEntry = entryIterator.next(); + for (CollectionTable.Entry entry : mapEntry.second) { + collectionKeyValuePairStrings + .add(String.format("\"%s TO %s [ %s ] TO %s\": [ %s ] ", mapEntry.getFirst(), + entry.getEdgeLabel(), entry.getEdgeDirection(), entry.getDestinationLabel(), + StreamSupport.stream(entry.getClauseCollection().spliterator(), false) + .map(abstractClauseFormatter).collect(Collectors.joining(", ")))); + } + } + ElementLabel startingLabel = c.getClauseInputEnvironment().getStartingLabel(); + return String.format("%s = { \"StartLabel\": %s, %s }", outputString, startingLabel, + String.join(",", collectionKeyValuePairStrings)); + }; + + if (fromClause instanceof FromGraphClause) { + FromGraphClause fromGraphClause = (FromGraphClause) fromClause; + LowerListClause lowerClause = (LowerListClause) fromGraphClause.getLowerClause(); + Spliterator<AbstractClause> clauseCollectionIterator = lowerClause.getClauseCollection().spliterator(); + return String.format(" FROM %s ", StreamSupport.stream(clauseCollectionIterator, false).map(c -> { + if (c instanceof LowerSwitchClause) { + return " FIXED-POINT " + fixedPointClauseFormatter.apply((LowerSwitchClause) c); + + } else { + return abstractClauseFormatter.apply(c); + } + }).collect(Collectors.joining(", "))); + + } else { + return String.format(" FROM %s ", fromClause.getFromTerms().stream().map(this::visitAndSwallowException) + .collect(Collectors.joining(", "))); + } } @Override @@ -211,9 +266,6 @@ sb.append(fromTerm.getPositionalVariable().accept(this, arg)); } for (AbstractBinaryCorrelateClause correlateClause : fromTerm.getCorrelateClauses()) { - if (correlateClause instanceof CorrLetClause || correlateClause instanceof CorrWhereClause) { - sb.append(" WITH "); - } sb.append(correlateClause.accept(this, arg)); } return sb.toString(); @@ -238,11 +290,9 @@ sb.append(groupbyClause.getGroupVar().accept(this, arg)); if (groupbyClause.hasGroupFieldList()) { sb.append(" ( "); - sb.append( - groupbyClause.getGroupFieldList().stream() - .map(f -> visitAndSwallowException(f.first) + " AS " - + formatIdentifierForQuery(f.second.getValue())) - .collect(Collectors.joining(", "))); + sb.append(groupbyClause.getGroupFieldList().stream() + .map(f -> visitAndSwallowException(f.first) + " AS " + formatIdentifierForQuery(f.second)) + .collect(Collectors.joining(", "))); sb.append(" ) "); } } @@ -441,7 +491,7 @@ @Override public String visit(VariableExpr variableExpr, Void arg) { - return "`" + formatIdentifierForQuery(variableExpr.getVar().getValue()) + "`"; + return "`" + formatIdentifierForQuery(variableExpr.getVar()) + "`"; } @Override @@ -560,8 +610,8 @@ @Override public String visit(FieldAccessor fieldAccessor, Void arg) throws CompilationException { - return fieldAccessor.getExpr().accept(this, arg) + ".`" - + formatIdentifierForQuery(fieldAccessor.getIdent().getValue()) + "`"; + return fieldAccessor.getExpr().accept(this, arg) + ".`" + formatIdentifierForQuery(fieldAccessor.getIdent()) + + "`"; } @Override @@ -595,6 +645,12 @@ } @Override + public String visit(IVisitorExtension extensionClause, Void arg) throws CompilationException { + // TODO (GLENN): Push this class to AsterixDB, and create a hook. + return null; + } + + @Override public String visit(UnaryExpr unaryExpr, Void arg) throws CompilationException { StringBuilder sb = new StringBuilder(); if (unaryExpr.getExprType() != null) { @@ -665,7 +721,7 @@ @Override public String visit(WindowExpression windowExpression, Void arg) { - // TODO: Add support for WINDOW-EXPR here. + // TODO (GLENN): Add support for WINDOW-EXPR here. throw new NotImplementedException("WindowExpression not currently supported!"); } @@ -693,4 +749,8 @@ return identifier; } } + + private String formatIdentifierForQuery(Identifier identifier) { + return formatIdentifierForQuery(identifier.getValue()); + } }
diff --git a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrite/resolve/ExhaustiveSearchResolver.java b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrite/resolve/ExhaustiveSearchResolver.java new file mode 100644 index 0000000..4c52a53 --- /dev/null +++ b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrite/resolve/ExhaustiveSearchResolver.java
@@ -0,0 +1,502 @@ +/* + * 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.graphix.lang.rewrite.resolve; + +import static org.apache.asterix.graphix.lang.rewrite.util.CanonicalElementUtil.expandElementLabels; +import static org.apache.asterix.graphix.lang.rewrite.util.CanonicalElementUtil.expandFixedPathPattern; + +import java.util.ArrayDeque; +import java.util.ArrayList; +import java.util.Deque; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.ListIterator; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import java.util.function.BiConsumer; +import java.util.function.Function; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import org.apache.asterix.common.exceptions.CompilationException; +import org.apache.asterix.common.exceptions.ErrorCode; +import org.apache.asterix.graphix.lang.annotation.SubqueryVertexJoinAnnotation; +import org.apache.asterix.graphix.lang.expression.EdgePatternExpr; +import org.apache.asterix.graphix.lang.expression.VertexPatternExpr; +import org.apache.asterix.graphix.lang.rewrite.visitor.GraphixDeepCopyVisitor; +import org.apache.asterix.graphix.lang.struct.EdgeDescriptor; +import org.apache.asterix.graphix.lang.struct.EdgeDescriptor.EdgeDirection; +import org.apache.asterix.graphix.lang.struct.ElementLabel; +import org.apache.asterix.graphix.lang.struct.PatternGroup; +import org.apache.asterix.lang.common.base.AbstractExpression; +import org.apache.asterix.lang.common.expression.VariableExpr; +import org.apache.hyracks.api.exceptions.SourceLocation; + +public class ExhaustiveSearchResolver implements IPatternGroupResolver { + private final GraphixDeepCopyVisitor deepCopyVisitor; + private final SchemaKnowledgeTable schemaKnowledgeTable; + + // Vertex / edge expression map to a list of canonical vertex / edge expressions. + private final Map<VariableExpr, List<AbstractExpression>> canonicalExpressionsMap; + private final Map<VariableExpr, AbstractExpression> originalExpressionMap; + + public ExhaustiveSearchResolver(SchemaKnowledgeTable schemaKnowledgeTable) { + this.schemaKnowledgeTable = schemaKnowledgeTable; + this.deepCopyVisitor = new GraphixDeepCopyVisitor(); + this.canonicalExpressionsMap = new LinkedHashMap<>(); + this.originalExpressionMap = new LinkedHashMap<>(); + } + + private void expandVertexPattern(VertexPatternExpr unexpandedVertexPattern, Deque<PatternGroup> inputPatternGroups, + Deque<PatternGroup> outputPatternGroups) throws CompilationException { + VariableExpr vertexVariable = unexpandedVertexPattern.getVariableExpr(); + Set<ElementLabel> vertexLabelSet = unexpandedVertexPattern.getLabels(); + Set<ElementLabel> expandedLabelSet = + expandElementLabels(unexpandedVertexPattern, vertexLabelSet, schemaKnowledgeTable); + while (!inputPatternGroups.isEmpty()) { + PatternGroup unexpandedPatternGroup = inputPatternGroups.pop(); + for (ElementLabel vertexLabel : expandedLabelSet) { + PatternGroup expandedPatternGroup = new PatternGroup(); + for (VertexPatternExpr vertexPatternExpr : unexpandedPatternGroup.getVertexPatternSet()) { + VertexPatternExpr clonedVertexPattern = deepCopyVisitor.visit(vertexPatternExpr, null); + SubqueryVertexJoinAnnotation hint = vertexPatternExpr.findHint(SubqueryVertexJoinAnnotation.class); + if (vertexPatternExpr.getVariableExpr().equals(vertexVariable) + || (hint != null && hint.getSourceVertexVariable().equals(vertexVariable))) { + clonedVertexPattern.getLabels().clear(); + clonedVertexPattern.getLabels().add(vertexLabel); + } + expandedPatternGroup.getVertexPatternSet().add(clonedVertexPattern); + } + for (EdgePatternExpr unexpandedEdgePattern : unexpandedPatternGroup.getEdgePatternSet()) { + EdgePatternExpr clonedEdgePattern = deepCopyVisitor.visit(unexpandedEdgePattern, null); + VertexPatternExpr clonedLeftVertex = clonedEdgePattern.getLeftVertex(); + SubqueryVertexJoinAnnotation hint1 = clonedLeftVertex.findHint(SubqueryVertexJoinAnnotation.class); + if (clonedLeftVertex.getVariableExpr().equals(vertexVariable) + || (hint1 != null && hint1.getSourceVertexVariable().equals(vertexVariable))) { + clonedLeftVertex.getLabels().clear(); + clonedLeftVertex.getLabels().add(vertexLabel); + } + + VertexPatternExpr clonedRightVertex = clonedEdgePattern.getRightVertex(); + SubqueryVertexJoinAnnotation hint2 = clonedLeftVertex.findHint(SubqueryVertexJoinAnnotation.class); + if (clonedRightVertex.getVariableExpr().equals(vertexVariable) + || (hint2 != null && hint2.getSourceVertexVariable().equals(vertexVariable))) { + clonedRightVertex.getLabels().clear(); + clonedRightVertex.getLabels().add(vertexLabel); + } + expandedPatternGroup.getEdgePatternSet().add(clonedEdgePattern); + } + unifyVertexLabels(expandedPatternGroup); + outputPatternGroups.push(expandedPatternGroup); + } + } + } + + private void expandEdgePattern(EdgePatternExpr unexpandedEdgePattern, Deque<PatternGroup> inputPatternGroups, + Deque<PatternGroup> outputPatternGroups) throws CompilationException { + EdgeDescriptor unexpandedEdgeDescriptor = unexpandedEdgePattern.getEdgeDescriptor(); + VariableExpr edgeVariable = unexpandedEdgeDescriptor.getVariableExpr(); + Set<ElementLabel> edgeLabelSet = unexpandedEdgeDescriptor.getEdgeLabels(); + Set<ElementLabel> expandedLabelSet = + expandElementLabels(unexpandedEdgePattern, edgeLabelSet, schemaKnowledgeTable); + + // Determine the direction of our edge. + Set<EdgeDirection> edgeDirectionSet = new HashSet<>(); + if (unexpandedEdgeDescriptor.getEdgeDirection() != EdgeDirection.RIGHT_TO_LEFT) { + edgeDirectionSet.add(EdgeDirection.LEFT_TO_RIGHT); + } + if (unexpandedEdgeDescriptor.getEdgeDirection() != EdgeDirection.LEFT_TO_RIGHT) { + edgeDirectionSet.add(EdgeDirection.RIGHT_TO_LEFT); + } + + // Expand an edge pattern. + while (!inputPatternGroups.isEmpty()) { + PatternGroup unexpandedPatternGroup = inputPatternGroups.pop(); + for (ElementLabel edgeLabel : expandedLabelSet) { + for (EdgeDirection edgeDirection : edgeDirectionSet) { + PatternGroup expandedPatternGroup = new PatternGroup(); + expandedPatternGroup.getVertexPatternSet().addAll(unexpandedPatternGroup.getVertexPatternSet()); + for (EdgePatternExpr edgePatternExpr : unexpandedPatternGroup.getEdgePatternSet()) { + // Update our edge descriptor, if necessary. + EdgePatternExpr clonedEdgePattern = deepCopyVisitor.visit(edgePatternExpr, null); + if (edgeVariable.equals(edgePatternExpr.getEdgeDescriptor().getVariableExpr())) { + clonedEdgePattern.getEdgeDescriptor().getEdgeLabels().clear(); + clonedEdgePattern.getEdgeDescriptor().getEdgeLabels().add(edgeLabel); + clonedEdgePattern.getEdgeDescriptor().setEdgeDirection(edgeDirection); + } + expandedPatternGroup.getEdgePatternSet().add(clonedEdgePattern); + } + unifyVertexLabels(expandedPatternGroup); + outputPatternGroups.push(expandedPatternGroup); + } + } + } + } + + private void expandPathPattern(EdgePatternExpr unexpandedPathPattern, Deque<PatternGroup> inputPatternGroups, + Deque<PatternGroup> outputPatternGroups) throws CompilationException { + EdgeDescriptor unexpandedEdgeDescriptor = unexpandedPathPattern.getEdgeDescriptor(); + VariableExpr edgeVariable = unexpandedEdgeDescriptor.getVariableExpr(); + Set<ElementLabel> edgeLabelSet = unexpandedEdgeDescriptor.getEdgeLabels(); + Set<ElementLabel> expandedEdgeLabelSet = + expandElementLabels(unexpandedPathPattern, edgeLabelSet, schemaKnowledgeTable); + + // Determine our candidates for internal vertex labels. + Set<ElementLabel> vertexLabelSet = schemaKnowledgeTable.getVertexLabels(); + + // Determine the direction of our edges. + Set<EdgeDirection> edgeDirectionSet = new HashSet<>(); + if (unexpandedEdgeDescriptor.getEdgeDirection() != EdgeDirection.RIGHT_TO_LEFT) { + edgeDirectionSet.add(EdgeDirection.LEFT_TO_RIGHT); + } + if (unexpandedEdgeDescriptor.getEdgeDirection() != EdgeDirection.LEFT_TO_RIGHT) { + edgeDirectionSet.add(EdgeDirection.RIGHT_TO_LEFT); + } + + // Determine the length of **expanded** path. For {N,M} w/ E labels... MIN(M, (1 + 2(E-1))). + int minimumExpansionLength, expansionLength; + Integer minimumHops = unexpandedEdgeDescriptor.getMinimumHops(); + Integer maximumHops = unexpandedEdgeDescriptor.getMaximumHops(); + if (Objects.equals(minimumHops, maximumHops) && minimumHops != null && minimumHops == 1) { + // Special case: we have a path disguised as an edge. + minimumExpansionLength = 1; + expansionLength = 1; + + } else { + int maximumExpansionLength = 1 + 2 * (expandedEdgeLabelSet.size() - 1); + minimumExpansionLength = Objects.requireNonNullElse(minimumHops, 1); + expansionLength = Objects.requireNonNullElse(maximumHops, maximumExpansionLength); + if (minimumExpansionLength > expansionLength) { + minimumExpansionLength = expansionLength; + } + } + + // Generate all possible paths, from minimumExpansionLength to expansionLength. + List<List<EdgePatternExpr>> candidatePaths = new ArrayList<>(); + for (int pathLength = minimumExpansionLength; pathLength <= expansionLength; pathLength++) { + List<List<EdgePatternExpr>> expandedPathPattern = expandFixedPathPattern(pathLength, unexpandedPathPattern, + expandedEdgeLabelSet, vertexLabelSet, edgeDirectionSet, deepCopyVisitor, + () -> deepCopyVisitor.visit(unexpandedPathPattern.getInternalVertex(), null)); + candidatePaths.addAll(expandedPathPattern); + } + + // Push the labels of our pattern group to our edge. + final BiConsumer<PatternGroup, EdgePatternExpr> vertexLabelUpdater = (p, e) -> { + VariableExpr leftVariable = e.getLeftVertex().getVariableExpr(); + VariableExpr rightVariable = e.getRightVertex().getVariableExpr(); + p.getVertexPatternSet().forEach(v -> { + VariableExpr vertexVariable = v.getVariableExpr(); + if (leftVariable.equals(vertexVariable)) { + e.getLeftVertex().getLabels().clear(); + e.getLeftVertex().getLabels().addAll(v.getLabels()); + } + if (rightVariable.equals(vertexVariable)) { + e.getRightVertex().getLabels().clear(); + e.getRightVertex().getLabels().addAll(v.getLabels()); + } + }); + }; + + // Push our expansion to our pattern groups. + while (!inputPatternGroups.isEmpty()) { + PatternGroup unexpandedPatternGroup = inputPatternGroups.pop(); + for (List<EdgePatternExpr> candidatePath : candidatePaths) { + PatternGroup expandedPatternGroup = new PatternGroup(); + expandedPatternGroup.getVertexPatternSet().addAll(unexpandedPatternGroup.getVertexPatternSet()); + for (EdgePatternExpr edgePatternExpr : unexpandedPatternGroup.getEdgePatternSet()) { + if (edgePatternExpr.getEdgeDescriptor().getVariableExpr().equals(edgeVariable)) { + for (EdgePatternExpr candidateEdge : candidatePath) { + EdgePatternExpr copyEdgePattern = deepCopyVisitor.visit(candidateEdge, null); + vertexLabelUpdater.accept(expandedPatternGroup, copyEdgePattern); + expandedPatternGroup.getEdgePatternSet().add(copyEdgePattern); + } + + } else { + EdgePatternExpr copyEdgePattern = deepCopyVisitor.visit(edgePatternExpr, null); + vertexLabelUpdater.accept(expandedPatternGroup, copyEdgePattern); + expandedPatternGroup.getEdgePatternSet().add(copyEdgePattern); + } + } + outputPatternGroups.push(expandedPatternGroup); + } + } + } + + private List<PatternGroup> expandPatternGroups(PatternGroup patternGroup) throws CompilationException { + // First pass: unify our vertex labels. + unifyVertexLabels(patternGroup); + + // Second pass: collect all ambiguous graph elements. + Set<AbstractExpression> ambiguousExpressionSet = new LinkedHashSet<>(); + for (AbstractExpression originalExpr : patternGroup) { + if (originalExpr instanceof VertexPatternExpr) { + VertexPatternExpr vertexPatternExpr = (VertexPatternExpr) originalExpr; + if (vertexPatternExpr.getLabels().size() != 1 + || vertexPatternExpr.getLabels().iterator().next().isNegated()) { + ambiguousExpressionSet.add(vertexPatternExpr); + } + + } else { // originalExpr instanceof EdgePatternExpr + EdgePatternExpr edgePatternExpr = (EdgePatternExpr) originalExpr; + EdgeDescriptor edgeDescriptor = edgePatternExpr.getEdgeDescriptor(); + if (edgeDescriptor.getEdgeLabels().size() != 1 + || edgeDescriptor.getEdgeLabels().iterator().next().isNegated() + || edgeDescriptor.getPatternType() == EdgeDescriptor.PatternType.PATH + || edgeDescriptor.getEdgeDirection() == EdgeDirection.UNDIRECTED) { + ambiguousExpressionSet.add(edgePatternExpr); + } + } + } + if (ambiguousExpressionSet.isEmpty()) { + // Our list must always be mutable. + return new ArrayList<>(List.of(patternGroup)); + } + + // Third pass: expand the ambiguous expressions. + Deque<PatternGroup> redPatternGroups = new ArrayDeque<>(); + Deque<PatternGroup> blackPatternGroups = new ArrayDeque<>(); + redPatternGroups.add(patternGroup); + for (AbstractExpression ambiguousExpression : ambiguousExpressionSet) { + // Determine our read and write pattern groups. + Deque<PatternGroup> readPatternGroups, writePatternGroups; + if (redPatternGroups.isEmpty()) { + readPatternGroups = blackPatternGroups; + writePatternGroups = redPatternGroups; + + } else { + readPatternGroups = redPatternGroups; + writePatternGroups = blackPatternGroups; + } + + // Expand our vertex / edge patterns. + if (ambiguousExpression instanceof VertexPatternExpr) { + VertexPatternExpr vertexPatternExpr = (VertexPatternExpr) ambiguousExpression; + expandVertexPattern(vertexPatternExpr, readPatternGroups, writePatternGroups); + + } else { // ambiguousExpression instance EdgePatternExpr + EdgePatternExpr edgePatternExpr = (EdgePatternExpr) ambiguousExpression; + if (edgePatternExpr.getEdgeDescriptor().getPatternType() == EdgeDescriptor.PatternType.EDGE) { + expandEdgePattern(edgePatternExpr, readPatternGroups, writePatternGroups); + + } else { // edgePatternExpr.getEdgeDescriptor().getPatternType() == EdgeDescriptor.PatternType.PATH + expandPathPattern(edgePatternExpr, readPatternGroups, writePatternGroups); + } + } + } + return new ArrayList<>(redPatternGroups.isEmpty() ? blackPatternGroups : redPatternGroups); + } + + private List<PatternGroup> pruneInvalidPatternGroups(List<PatternGroup> sourceGroups) { + ListIterator<PatternGroup> sourceGroupIterator = sourceGroups.listIterator(); + while (sourceGroupIterator.hasNext()) { + PatternGroup workingPatternGroup = sourceGroupIterator.next(); + for (AbstractExpression abstractExpression : workingPatternGroup) { + if (!(abstractExpression instanceof EdgePatternExpr)) { + continue; + } + + // Prune any groups with bad edges. + EdgePatternExpr edgePatternExpr = (EdgePatternExpr) abstractExpression; + if (!schemaKnowledgeTable.isValidEdge(edgePatternExpr)) { + sourceGroupIterator.remove(); + break; + } + } + } + return sourceGroups; + } + + private void unifyVertexLabels(PatternGroup patternGroup) { + // Pass #1: Collect all vertex variables and their labels. + Map<VariableExpr, Set<ElementLabel>> vertexVariableMap = new HashMap<>(); + for (AbstractExpression abstractExpression : patternGroup) { + if (abstractExpression instanceof VertexPatternExpr) { + VertexPatternExpr vertexPatternExpr = (VertexPatternExpr) abstractExpression; + VariableExpr vertexVariable = vertexPatternExpr.getVariableExpr(); + vertexVariableMap.putIfAbsent(vertexVariable, new HashSet<>()); + vertexVariableMap.get(vertexVariable).addAll(vertexPatternExpr.getLabels()); + SubqueryVertexJoinAnnotation hint = vertexPatternExpr.findHint(SubqueryVertexJoinAnnotation.class); + if (hint != null) { + VariableExpr sourceVertexVariable = hint.getSourceVertexVariable(); + vertexVariableMap.putIfAbsent(sourceVertexVariable, new HashSet<>()); + vertexVariableMap.get(sourceVertexVariable).addAll(vertexPatternExpr.getLabels()); + } + + } else { // abstractExpression instanceof EdgePatternExpr + EdgePatternExpr edgePatternExpr = (EdgePatternExpr) abstractExpression; + VertexPatternExpr leftVertexExpr = edgePatternExpr.getLeftVertex(); + VertexPatternExpr rightVertexExpr = edgePatternExpr.getRightVertex(); + VariableExpr leftVariable = leftVertexExpr.getVariableExpr(); + VariableExpr rightVariable = rightVertexExpr.getVariableExpr(); + + // Visit the vertices of our edges and their labels as well. + vertexVariableMap.putIfAbsent(leftVariable, new HashSet<>()); + vertexVariableMap.putIfAbsent(rightVariable, new HashSet<>()); + vertexVariableMap.get(leftVariable).addAll(leftVertexExpr.getLabels()); + vertexVariableMap.get(rightVariable).addAll(rightVertexExpr.getLabels()); + SubqueryVertexJoinAnnotation leftHint = leftVertexExpr.findHint(SubqueryVertexJoinAnnotation.class); + SubqueryVertexJoinAnnotation rightHint = rightVertexExpr.findHint(SubqueryVertexJoinAnnotation.class); + if (leftHint != null) { + VariableExpr sourceVertexVariable = leftHint.getSourceVertexVariable(); + vertexVariableMap.putIfAbsent(sourceVertexVariable, new HashSet<>()); + vertexVariableMap.get(sourceVertexVariable).addAll(leftVertexExpr.getLabels()); + } + if (rightHint != null) { + VariableExpr sourceVertexVariable = rightHint.getSourceVertexVariable(); + vertexVariableMap.putIfAbsent(sourceVertexVariable, new HashSet<>()); + vertexVariableMap.get(sourceVertexVariable).addAll(rightVertexExpr.getLabels()); + } + } + } + + // Pass #2: Copy the vertex label sets to all vertices of the same variable. + for (AbstractExpression abstractExpression : patternGroup) { + if (abstractExpression instanceof VertexPatternExpr) { + VertexPatternExpr vertexPatternExpr = (VertexPatternExpr) abstractExpression; + VariableExpr vertexVariable = vertexPatternExpr.getVariableExpr(); + vertexPatternExpr.getLabels().addAll(vertexVariableMap.get(vertexVariable)); + SubqueryVertexJoinAnnotation hint = vertexPatternExpr.findHint(SubqueryVertexJoinAnnotation.class); + if (hint != null) { + vertexPatternExpr.getLabels().addAll(vertexVariableMap.get(hint.getSourceVertexVariable())); + } + + } else { // abstractExpression instanceof EdgePatternExpr + EdgePatternExpr edgePatternExpr = (EdgePatternExpr) abstractExpression; + VertexPatternExpr leftVertexExpr = edgePatternExpr.getLeftVertex(); + VertexPatternExpr rightVertexExpr = edgePatternExpr.getRightVertex(); + + // Visit the vertices of our edge as well. + VariableExpr leftVariable = leftVertexExpr.getVariableExpr(); + VariableExpr rightVariable = rightVertexExpr.getVariableExpr(); + leftVertexExpr.getLabels().addAll(vertexVariableMap.get(leftVariable)); + rightVertexExpr.getLabels().addAll(vertexVariableMap.get(rightVariable)); + SubqueryVertexJoinAnnotation leftHint = leftVertexExpr.findHint(SubqueryVertexJoinAnnotation.class); + SubqueryVertexJoinAnnotation rightHint = rightVertexExpr.findHint(SubqueryVertexJoinAnnotation.class); + if (leftHint != null) { + leftVertexExpr.getLabels().addAll(vertexVariableMap.get(leftHint.getSourceVertexVariable())); + } + if (rightHint != null) { + rightVertexExpr.getLabels().addAll(vertexVariableMap.get(rightHint.getSourceVertexVariable())); + } + } + } + + } + + @Override + public void resolve(PatternGroup patternGroup) throws CompilationException { + // Populate our vertex / edge expression map. + for (VertexPatternExpr vertexPatternExpr : patternGroup.getVertexPatternSet()) { + VariableExpr vertexVariable = vertexPatternExpr.getVariableExpr(); + canonicalExpressionsMap.putIfAbsent(vertexVariable, new ArrayList<>()); + originalExpressionMap.put(vertexVariable, vertexPatternExpr); + } + for (EdgePatternExpr edgePatternExpr : patternGroup.getEdgePatternSet()) { + EdgeDescriptor edgeDescriptor = edgePatternExpr.getEdgeDescriptor(); + VariableExpr edgeVariable = edgeDescriptor.getVariableExpr(); + canonicalExpressionsMap.putIfAbsent(edgeVariable, new ArrayList<>()); + originalExpressionMap.put(edgeVariable, edgePatternExpr); + } + + // Expand the given pattern group and then prune the invalid pattern groups. + List<PatternGroup> validPatternGroups = pruneInvalidPatternGroups(expandPatternGroups(patternGroup)); + for (PatternGroup validPatternGroup : validPatternGroups) { + for (AbstractExpression canonicalExpr : validPatternGroup) { + if (canonicalExpr instanceof VertexPatternExpr) { + VertexPatternExpr vertexPatternExpr = (VertexPatternExpr) canonicalExpr; + VariableExpr vertexVariable = vertexPatternExpr.getVariableExpr(); + canonicalExpressionsMap.get(vertexVariable).add(deepCopyVisitor.visit(vertexPatternExpr, null)); + + } else { // canonicalExpr instanceof EdgePatternExpr + EdgePatternExpr edgePatternExpr = (EdgePatternExpr) canonicalExpr; + EdgeDescriptor edgeDescriptor = edgePatternExpr.getEdgeDescriptor(); + VariableExpr edgeVariable = edgeDescriptor.getVariableExpr(); + canonicalExpressionsMap.get(edgeVariable).add(deepCopyVisitor.visit(edgePatternExpr, null)); + + // We must also visit the vertices of our edge (to find internal vertices). + VertexPatternExpr leftVertex = edgePatternExpr.getLeftVertex(); + VertexPatternExpr rightVertex = edgePatternExpr.getRightVertex(); + VariableExpr leftVariable = leftVertex.getVariableExpr(); + VariableExpr rightVariable = rightVertex.getVariableExpr(); + canonicalExpressionsMap.putIfAbsent(leftVariable, new ArrayList<>()); + canonicalExpressionsMap.putIfAbsent(rightVariable, new ArrayList<>()); + canonicalExpressionsMap.get(leftVariable).add(deepCopyVisitor.visit(leftVertex, null)); + canonicalExpressionsMap.get(rightVariable).add(deepCopyVisitor.visit(rightVertex, null)); + } + } + } + + // Propagate the facts of our expression map to the original expressions. + final Function<VariableExpr, Stream<VertexPatternExpr>> canonicalVertexStreamProvider = + v -> canonicalExpressionsMap.get(v).stream().map(t -> (VertexPatternExpr) t); + final Function<VariableExpr, Stream<EdgePatternExpr>> canonicalEdgeStreamProvider = + v -> canonicalExpressionsMap.get(v).stream().map(t -> (EdgePatternExpr) t); + for (Map.Entry<VariableExpr, AbstractExpression> mapEntry : originalExpressionMap.entrySet()) { + if (canonicalExpressionsMap.get(mapEntry.getKey()).isEmpty()) { + SourceLocation sourceLocation = mapEntry.getValue().getSourceLocation(); + throw new CompilationException(ErrorCode.COMPILATION_ERROR, sourceLocation, + "Encountered graph element that does not conform the queried graph schema!"); + } + + if (mapEntry.getValue() instanceof VertexPatternExpr) { + VertexPatternExpr vertexPatternExpr = (VertexPatternExpr) mapEntry.getValue(); + vertexPatternExpr.getLabels().clear(); + Stream<VertexPatternExpr> vertexStream = canonicalVertexStreamProvider.apply(mapEntry.getKey()); + vertexStream.forEach(v -> vertexPatternExpr.getLabels().addAll(v.getLabels())); + + } else { // originalExpr instanceof EdgePatternExpr + EdgePatternExpr edgePatternExpr = (EdgePatternExpr) mapEntry.getValue(); + EdgeDescriptor edgeDescriptor = edgePatternExpr.getEdgeDescriptor(); + Integer maximumHopLength = edgeDescriptor.getMaximumHops(); + + // Update our edge labels. + edgeDescriptor.getEdgeLabels().clear(); + Stream<EdgePatternExpr> edgeStream = canonicalEdgeStreamProvider.apply(mapEntry.getKey()); + edgeStream.forEach(e -> edgeDescriptor.getEdgeLabels().addAll(e.getEdgeDescriptor().getEdgeLabels())); + + // Update our direction, if we have resolved it. + Set<EdgeDirection> resolvedDirections = canonicalEdgeStreamProvider.apply(mapEntry.getKey()) + .map(e -> e.getEdgeDescriptor().getEdgeDirection()).collect(Collectors.toSet()); + if (resolvedDirections.size() == 1) { + edgeDescriptor.setEdgeDirection(resolvedDirections.iterator().next()); + } + + // Update the vertex references of our edge. + VariableExpr leftVariable = edgePatternExpr.getLeftVertex().getVariableExpr(); + VariableExpr rightVariable = edgePatternExpr.getRightVertex().getVariableExpr(); + edgePatternExpr.getLeftVertex().getLabels().clear(); + edgePatternExpr.getRightVertex().getLabels().clear(); + Stream<VertexPatternExpr> leftStream = canonicalVertexStreamProvider.apply(leftVariable); + Stream<VertexPatternExpr> rightStream = canonicalVertexStreamProvider.apply(rightVariable); + leftStream.forEach(v -> edgePatternExpr.getLeftVertex().getLabels().addAll(v.getLabels())); + rightStream.forEach(v -> edgePatternExpr.getRightVertex().getLabels().addAll(v.getLabels())); + if (edgeDescriptor.getPatternType() == EdgeDescriptor.PatternType.PATH && maximumHopLength != 1) { + VariableExpr internalVariable = edgePatternExpr.getInternalVertex().getVariableExpr(); + edgePatternExpr.getInternalVertex().getLabels().clear(); + Stream<VertexPatternExpr> internalStream = canonicalVertexStreamProvider.apply(internalVariable); + internalStream.forEach(v -> edgePatternExpr.getInternalVertex().getLabels().addAll(v.getLabels())); + } + } + } + } +}
diff --git a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/lower/transform/ISequenceTransformer.java b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrite/resolve/IInternalVertexSupplier.java similarity index 79% rename from asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/lower/transform/ISequenceTransformer.java rename to asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrite/resolve/IInternalVertexSupplier.java index 4509792..931f979 100644 --- a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/lower/transform/ISequenceTransformer.java +++ b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrite/resolve/IInternalVertexSupplier.java
@@ -16,11 +16,12 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.asterix.graphix.lang.rewrites.lower.transform; +package org.apache.asterix.graphix.lang.rewrite.resolve; import org.apache.asterix.common.exceptions.CompilationException; +import org.apache.asterix.graphix.lang.expression.VertexPatternExpr; @FunctionalInterface -public interface ISequenceTransformer { - void accept(CorrelatedClauseSequence clauseSequence) throws CompilationException; +public interface IInternalVertexSupplier { + VertexPatternExpr get() throws CompilationException; }
diff --git a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/lower/transform/ISequenceTransformer.java b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrite/resolve/IPatternGroupResolver.java similarity index 79% copy from asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/lower/transform/ISequenceTransformer.java copy to asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrite/resolve/IPatternGroupResolver.java index 4509792..c0129fb 100644 --- a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/lower/transform/ISequenceTransformer.java +++ b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrite/resolve/IPatternGroupResolver.java
@@ -16,11 +16,12 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.asterix.graphix.lang.rewrites.lower.transform; +package org.apache.asterix.graphix.lang.rewrite.resolve; import org.apache.asterix.common.exceptions.CompilationException; +import org.apache.asterix.graphix.lang.struct.PatternGroup; @FunctionalInterface -public interface ISequenceTransformer { - void accept(CorrelatedClauseSequence clauseSequence) throws CompilationException; +public interface IPatternGroupResolver { + void resolve(PatternGroup patternGroup) throws CompilationException; }
diff --git a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrite/resolve/SchemaKnowledgeTable.java b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrite/resolve/SchemaKnowledgeTable.java new file mode 100644 index 0000000..e0eb8ad --- /dev/null +++ b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrite/resolve/SchemaKnowledgeTable.java
@@ -0,0 +1,227 @@ +/* + * 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.graphix.lang.rewrite.resolve; + +import static org.apache.asterix.graphix.extension.GraphixMetadataExtension.getGraph; + +import java.util.HashSet; +import java.util.Iterator; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import java.util.stream.Collectors; + +import org.apache.asterix.common.exceptions.CompilationException; +import org.apache.asterix.common.exceptions.ErrorCode; +import org.apache.asterix.common.metadata.DataverseName; +import org.apache.asterix.graphix.common.metadata.EdgeIdentifier; +import org.apache.asterix.graphix.common.metadata.GraphIdentifier; +import org.apache.asterix.graphix.common.metadata.VertexIdentifier; +import org.apache.asterix.graphix.lang.clause.FromGraphClause; +import org.apache.asterix.graphix.lang.expression.EdgePatternExpr; +import org.apache.asterix.graphix.lang.expression.GraphConstructor; +import org.apache.asterix.graphix.lang.rewrite.GraphixRewritingContext; +import org.apache.asterix.graphix.lang.statement.DeclareGraphStatement; +import org.apache.asterix.graphix.lang.struct.EdgeDescriptor; +import org.apache.asterix.graphix.lang.struct.ElementLabel; +import org.apache.asterix.graphix.metadata.entity.schema.Edge; +import org.apache.asterix.graphix.metadata.entity.schema.Graph; +import org.apache.asterix.graphix.metadata.entity.schema.Vertex; +import org.apache.asterix.lang.common.struct.Identifier; +import org.apache.asterix.metadata.MetadataTransactionContext; +import org.apache.asterix.metadata.declared.MetadataProvider; +import org.apache.hyracks.algebricks.common.exceptions.AlgebricksException; + +/** + * A collection of ground truths, derived from the graph schema (either a {@link Graph} or {@link GraphConstructor}). + */ +public class SchemaKnowledgeTable implements Iterable<SchemaKnowledgeTable.KnowledgeRecord> { + private final Set<KnowledgeRecord> knowledgeRecordSet = new HashSet<>(); + private final Set<VertexIdentifier> vertexIdentifierSet = new HashSet<>(); + private final Set<EdgeIdentifier> edgeIdentifierSet = new HashSet<>(); + private final GraphIdentifier graphIdentifier; + + public SchemaKnowledgeTable(FromGraphClause fromGraphClause, GraphixRewritingContext graphixRewritingContext) + throws CompilationException { + MetadataProvider metadataProvider = graphixRewritingContext.getMetadataProvider(); + DataverseName dataverseName = (fromGraphClause.getDataverseName() == null) + ? metadataProvider.getDefaultDataverseName() : fromGraphClause.getDataverseName(); + graphIdentifier = fromGraphClause.getGraphIdentifier(metadataProvider); + + // Establish our schema knowledge. + GraphConstructor graphConstructor = fromGraphClause.getGraphConstructor(); + if (graphConstructor == null) { + Identifier graphName = fromGraphClause.getGraphName(); + + // First, try to find our graph inside our declared graph set. + Map<GraphIdentifier, DeclareGraphStatement> declaredGraphs = graphixRewritingContext.getDeclaredGraphs(); + DeclareGraphStatement declaredGraph = declaredGraphs.get(graphIdentifier); + if (declaredGraph != null) { + initializeWithGraphConstructor(declaredGraph.getGraphConstructor()); + + } else { + // Otherwise, fetch the graph from our metadata. + try { + MetadataTransactionContext metadataTxnContext = metadataProvider.getMetadataTxnContext(); + Graph graphFromMetadata = getGraph(metadataTxnContext, dataverseName, graphName.getValue()); + if (graphFromMetadata == null) { + throw new CompilationException(ErrorCode.COMPILATION_ERROR, fromGraphClause.getSourceLocation(), + "Graph " + graphName.getValue() + " does not exist."); + } + initializeWithMetadataGraph(graphFromMetadata); + + } catch (AlgebricksException e) { + throw new CompilationException(ErrorCode.COMPILATION_ERROR, fromGraphClause.getSourceLocation(), + "Graph " + graphName.getValue() + " does not exist."); + } + } + + } else { + initializeWithGraphConstructor(graphConstructor); + } + } + + private void initializeWithMetadataGraph(Graph graph) { + for (Vertex vertex : graph.getGraphSchema().getVertices()) { + VertexIdentifier vertexIdentifier = vertex.getIdentifier(); + vertexIdentifierSet.add(vertexIdentifier); + } + for (Edge edge : graph.getGraphSchema().getEdges()) { + ElementLabel sourceLabel = edge.getSourceLabel(); + ElementLabel edgeLabel = edge.getLabel(); + ElementLabel destLabel = edge.getDestinationLabel(); + + // Build our source and destination vertex identifiers. + VertexIdentifier sourceIdentifier = new VertexIdentifier(graphIdentifier, sourceLabel); + VertexIdentifier destIdentifier = new VertexIdentifier(graphIdentifier, destLabel); + vertexIdentifierSet.add(sourceIdentifier); + vertexIdentifierSet.add(destIdentifier); + edgeIdentifierSet.add(edge.getIdentifier()); + + // Update our knowledge set. + knowledgeRecordSet.add(new KnowledgeRecord(sourceLabel, edgeLabel, destLabel)); + } + } + + private void initializeWithGraphConstructor(GraphConstructor graphConstructor) { + for (GraphConstructor.VertexConstructor vertexElement : graphConstructor.getVertexElements()) { + VertexIdentifier vertexIdentifier = new VertexIdentifier(graphIdentifier, vertexElement.getLabel()); + vertexIdentifierSet.add(vertexIdentifier); + } + for (GraphConstructor.EdgeConstructor edgeElement : graphConstructor.getEdgeElements()) { + ElementLabel sourceLabel = edgeElement.getSourceLabel(); + ElementLabel edgeLabel = edgeElement.getEdgeLabel(); + ElementLabel destLabel = edgeElement.getDestinationLabel(); + + // Build our edge identifiers, and source & destination vertex identifiers. + VertexIdentifier sourceIdentifier = new VertexIdentifier(graphIdentifier, sourceLabel); + VertexIdentifier destIdentifier = new VertexIdentifier(graphIdentifier, destLabel); + EdgeIdentifier edgeIdentifier = new EdgeIdentifier(graphIdentifier, sourceLabel, edgeLabel, destLabel); + vertexIdentifierSet.add(sourceIdentifier); + vertexIdentifierSet.add(destIdentifier); + edgeIdentifierSet.add(edgeIdentifier); + + // Update our knowledge set. + knowledgeRecordSet.add(new KnowledgeRecord(sourceLabel, edgeLabel, destLabel)); + } + } + + public Set<ElementLabel> getVertexLabels() { + return vertexIdentifierSet.stream().map(VertexIdentifier::getVertexLabel).collect(Collectors.toSet()); + } + + public Set<ElementLabel> getEdgeLabels() { + return edgeIdentifierSet.stream().map(EdgeIdentifier::getEdgeLabel).collect(Collectors.toSet()); + } + + public boolean isValidEdge(EdgePatternExpr edgePatternExpr) { + for (ElementLabel leftLabel : edgePatternExpr.getLeftVertex().getLabels()) { + for (ElementLabel rightLabel : edgePatternExpr.getRightVertex().getLabels()) { + EdgeDescriptor edgeDescriptor = edgePatternExpr.getEdgeDescriptor(); + for (ElementLabel edgeLabel : edgeDescriptor.getEdgeLabels()) { + if (edgeDescriptor.getEdgeDirection() != EdgeDescriptor.EdgeDirection.RIGHT_TO_LEFT) { + EdgeIdentifier id = new EdgeIdentifier(graphIdentifier, leftLabel, edgeLabel, rightLabel); + if (!edgeIdentifierSet.contains(id)) { + return false; + } + } + if (edgeDescriptor.getEdgeDirection() != EdgeDescriptor.EdgeDirection.LEFT_TO_RIGHT) { + EdgeIdentifier id = new EdgeIdentifier(graphIdentifier, rightLabel, edgeLabel, leftLabel); + if (!edgeIdentifierSet.contains(id)) { + return false; + } + } + } + } + } + return true; + } + + @Override + public Iterator<KnowledgeRecord> iterator() { + return knowledgeRecordSet.iterator(); + } + + public static class KnowledgeRecord { + private final ElementLabel sourceElementLabel; + private final ElementLabel destElementLabel; + private final ElementLabel edgeLabel; + + public KnowledgeRecord(ElementLabel sourceElementLabel, ElementLabel edgeLabel, ElementLabel destElementLabel) { + this.sourceElementLabel = Objects.requireNonNull(sourceElementLabel); + this.destElementLabel = Objects.requireNonNull(destElementLabel); + this.edgeLabel = Objects.requireNonNull(edgeLabel); + } + + public ElementLabel getSourceVertexLabel() { + return sourceElementLabel; + } + + public ElementLabel getDestVertexLabel() { + return destElementLabel; + } + + public ElementLabel getEdgeLabel() { + return edgeLabel; + } + + @Override + public String toString() { + return String.format("(%s)-[%s]->(%s)", sourceElementLabel, edgeLabel, destElementLabel); + } + + @Override + public boolean equals(Object object) { + if (this == object) { + return true; + } + if (!(object instanceof KnowledgeRecord)) { + return false; + } + KnowledgeRecord target = (KnowledgeRecord) object; + return sourceElementLabel.equals(target.sourceElementLabel) + && destElementLabel.equals(target.destElementLabel) && edgeLabel.equals(target.edgeLabel); + } + + @Override + public int hashCode() { + return Objects.hash(sourceElementLabel, destElementLabel, edgeLabel); + } + } +}
diff --git a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrite/util/CanonicalElementUtil.java b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrite/util/CanonicalElementUtil.java new file mode 100644 index 0000000..36d1fc0 --- /dev/null +++ b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrite/util/CanonicalElementUtil.java
@@ -0,0 +1,221 @@ +/* + * 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.graphix.lang.rewrite.util; + +import static org.apache.asterix.graphix.lang.struct.EdgeDescriptor.EdgeDirection; +import static org.apache.asterix.graphix.lang.struct.EdgeDescriptor.PatternType; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.ListIterator; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; + +import org.apache.asterix.common.exceptions.CompilationException; +import org.apache.asterix.graphix.lang.expression.EdgePatternExpr; +import org.apache.asterix.graphix.lang.expression.VertexPatternExpr; +import org.apache.asterix.graphix.lang.rewrite.resolve.IInternalVertexSupplier; +import org.apache.asterix.graphix.lang.rewrite.resolve.SchemaKnowledgeTable; +import org.apache.asterix.graphix.lang.rewrite.visitor.GraphixDeepCopyVisitor; +import org.apache.asterix.graphix.lang.struct.EdgeDescriptor; +import org.apache.asterix.graphix.lang.struct.ElementLabel; +import org.apache.asterix.lang.common.base.AbstractExpression; +import org.apache.asterix.lang.common.expression.VariableExpr; +import org.paukov.combinatorics3.Generator; + +public final class CanonicalElementUtil { + public static Set<ElementLabel> expandElementLabels(AbstractExpression originalExpr, + Set<ElementLabel> elementLabels, SchemaKnowledgeTable schemaKnowledgeTable) { + Set<ElementLabel> schemaLabels = (originalExpr instanceof VertexPatternExpr) + ? schemaKnowledgeTable.getVertexLabels() : schemaKnowledgeTable.getEdgeLabels(); + if (elementLabels.isEmpty()) { + // If our set is empty, then return all the element labels associated with our schema. + return schemaLabels; + } + + // If we have any negated element labels, then we return the difference between our schema and this set. + Set<String> negatedElementLabels = elementLabels.stream().filter(ElementLabel::isNegated) + .map(ElementLabel::getLabelName).collect(Collectors.toSet()); + if (!negatedElementLabels.isEmpty()) { + return schemaLabels.stream().filter(s -> !negatedElementLabels.contains(s.getLabelName())) + .collect(Collectors.toSet()); + } + + // Otherwise, we return our input set of labels. + return elementLabels; + } + + public static Set<EdgeDirection> expandEdgeDirection(EdgeDescriptor edgeDescriptor) { + Set<EdgeDirection> edgeDirectionList = new HashSet<>(); + if (edgeDescriptor.getEdgeDirection() != EdgeDirection.RIGHT_TO_LEFT) { + edgeDirectionList.add(EdgeDirection.LEFT_TO_RIGHT); + } + if (edgeDescriptor.getEdgeDirection() != EdgeDirection.LEFT_TO_RIGHT) { + edgeDirectionList.add(EdgeDirection.RIGHT_TO_LEFT); + } + return edgeDirectionList; + } + + public static List<List<EdgePatternExpr>> expandFixedPathPattern(int pathLength, EdgePatternExpr edgePattern, + Set<ElementLabel> edgeLabels, Set<ElementLabel> vertexLabels, Set<EdgeDirection> edgeDirections, + GraphixDeepCopyVisitor deepCopyVisitor, IInternalVertexSupplier internalVertexSupplier) + throws CompilationException { + VertexPatternExpr leftVertex = edgePattern.getLeftVertex(); + VertexPatternExpr rightVertex = edgePattern.getRightVertex(); + VariableExpr edgeVariableExpr = edgePattern.getEdgeDescriptor().getVariableExpr(); + + // Generate all possible edge label K-permutations w/ repetitions. + List<List<ElementLabel>> edgeLabelPermutations = new ArrayList<>(); + Generator.combination(edgeLabels).multi(pathLength).stream() + .forEach(c -> Generator.permutation(c).simple().forEach(edgeLabelPermutations::add)); + + // Generate all possible direction K-permutations w/ repetitions. + List<List<EdgeDirection>> directionPermutations = new ArrayList<>(); + Generator.combination(edgeDirections).multi(pathLength).stream() + .forEach(c -> Generator.permutation(c).simple().forEach(directionPermutations::add)); + + // Special case: if we have a path of length one, we do not need to permute our vertices. + if (pathLength == 1) { + List<List<EdgePatternExpr>> expandedPathPatterns = new ArrayList<>(); + for (List<ElementLabel> edgeLabelPermutation : edgeLabelPermutations) { + ElementLabel edgeLabel = edgeLabelPermutation.get(0); + for (List<EdgeDirection> directionPermutation : directionPermutations) { + EdgeDirection edgeDirection = directionPermutation.get(0); + EdgeDescriptor edgeDescriptor = new EdgeDescriptor(edgeDirection, PatternType.EDGE, + Set.of(edgeLabel), null, deepCopyVisitor.visit(edgeVariableExpr, null), null, null); + expandedPathPatterns.add(List.of(new EdgePatternExpr(leftVertex, rightVertex, edgeDescriptor))); + } + } + return expandedPathPatterns; + } + + // Otherwise... generate all possible (K-1)-permutations w/ repetitions. + List<List<ElementLabel>> vertexLabelPermutations = new ArrayList<>(); + Generator.combination(vertexLabels).multi(pathLength - 1).stream() + .forEach(c -> Generator.permutation(c).simple().forEach(vertexLabelPermutations::add)); + + // ... and perform a cartesian product of all three sets above. + List<List<EdgePatternExpr>> expandedPathPatterns = new ArrayList<>(); + for (List<ElementLabel> edgeLabelPermutation : edgeLabelPermutations) { + for (List<EdgeDirection> directionPermutation : directionPermutations) { + for (List<ElementLabel> vertexLabelPermutation : vertexLabelPermutations) { + Iterator<ElementLabel> edgeLabelIterator = edgeLabelPermutation.iterator(); + Iterator<EdgeDirection> directionIterator = directionPermutation.iterator(); + Iterator<ElementLabel> vertexLabelIterator = vertexLabelPermutation.iterator(); + + // Build one path. + List<EdgePatternExpr> pathPattern = new ArrayList<>(); + VertexPatternExpr previousRightVertex = null; + for (int i = 0; edgeLabelIterator.hasNext(); i++) { + Set<ElementLabel> edgeLabelSet = Set.of(edgeLabelIterator.next()); + EdgeDirection edgeDirection = directionIterator.next(); + + // Determine our left vertex. + VertexPatternExpr workingLeftVertex; + if (i == 0) { + workingLeftVertex = deepCopyVisitor.visit(leftVertex, null); + } else { + workingLeftVertex = deepCopyVisitor.visit(previousRightVertex, null); + } + + // Determine our right vertex. + VertexPatternExpr workingRightVertex; + if (!edgeLabelIterator.hasNext()) { + workingRightVertex = deepCopyVisitor.visit(rightVertex, null); + + } else { + workingRightVertex = internalVertexSupplier.get(); + workingRightVertex.getLabels().clear(); + workingRightVertex.getLabels().add(vertexLabelIterator.next()); + } + previousRightVertex = workingRightVertex; + + // And build the edge to add to our path. + EdgeDescriptor edgeDescriptor = new EdgeDescriptor(edgeDirection, PatternType.EDGE, + edgeLabelSet, null, deepCopyVisitor.visit(edgeVariableExpr, null), null, null); + pathPattern.add(new EdgePatternExpr(workingLeftVertex, workingRightVertex, edgeDescriptor)); + } + expandedPathPatterns.add(pathPattern); + } + } + } + return expandedPathPatterns; + } + + public static List<EdgePatternExpr> deepCopyPathPattern(List<EdgePatternExpr> pathPattern, + GraphixDeepCopyVisitor deepCopyVisitor) throws CompilationException { + List<EdgePatternExpr> deepCopyEdgeList = new ArrayList<>(); + for (EdgePatternExpr edgePatternExpr : pathPattern) { + deepCopyEdgeList.add(deepCopyVisitor.visit(edgePatternExpr, null)); + } + return deepCopyEdgeList; + } + + public static void replaceVertexInIterator(Map<VertexPatternExpr, VertexPatternExpr> replaceMap, + ListIterator<VertexPatternExpr> elementIterator, GraphixDeepCopyVisitor deepCopyVisitor) + throws CompilationException { + while (elementIterator.hasNext()) { + VertexPatternExpr nextElement = elementIterator.next(); + if (replaceMap.containsKey(nextElement)) { + VertexPatternExpr replacementElement = replaceMap.get(nextElement); + elementIterator.set((VertexPatternExpr) replacementElement.accept(deepCopyVisitor, null)); + } + } + } + + public static void replaceEdgeInIterator(Map<EdgePatternExpr, EdgePatternExpr> replaceMap, + ListIterator<EdgePatternExpr> elementIterator, GraphixDeepCopyVisitor deepCopyVisitor) + throws CompilationException { + // Build a map of vertices to vertices from our replace map. + Map<VertexPatternExpr, VertexPatternExpr> replaceVertexMap = new HashMap<>(); + for (Map.Entry<EdgePatternExpr, EdgePatternExpr> mapEntry : replaceMap.entrySet()) { + VertexPatternExpr keyLeftVertex = mapEntry.getKey().getLeftVertex(); + VertexPatternExpr keyRightVertex = mapEntry.getKey().getRightVertex(); + VertexPatternExpr valueLeftVertex = mapEntry.getValue().getLeftVertex(); + VertexPatternExpr valueRightVertex = mapEntry.getValue().getRightVertex(); + replaceVertexMap.put(keyLeftVertex, valueLeftVertex); + replaceVertexMap.put(keyRightVertex, valueRightVertex); + } + + while (elementIterator.hasNext()) { + EdgePatternExpr nextElement = elementIterator.next(); + if (replaceMap.containsKey(nextElement)) { + EdgePatternExpr replacementElement = replaceMap.get(nextElement); + elementIterator.set((EdgePatternExpr) replacementElement.accept(deepCopyVisitor, null)); + + } else { + if (replaceVertexMap.containsKey(nextElement.getLeftVertex())) { + VertexPatternExpr replaceLeftVertex = replaceVertexMap.get(nextElement.getLeftVertex()); + nextElement.setLeftVertex(deepCopyVisitor.visit(replaceLeftVertex, null)); + } + if (replaceVertexMap.containsKey(nextElement.getRightVertex())) { + VertexPatternExpr replaceRightVertex = replaceVertexMap.get(nextElement.getRightVertex()); + nextElement.setRightVertex(deepCopyVisitor.visit(replaceRightVertex, null)); + } + } + } + } + + private CanonicalElementUtil() { + } +}
diff --git a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/util/LowerRewritingUtil.java b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrite/util/LowerRewritingUtil.java similarity index 90% rename from asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/util/LowerRewritingUtil.java rename to asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrite/util/LowerRewritingUtil.java index 616138e..78d7aea 100644 --- a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/util/LowerRewritingUtil.java +++ b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrite/util/LowerRewritingUtil.java
@@ -16,19 +16,19 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.asterix.graphix.lang.rewrites.util; +package org.apache.asterix.graphix.lang.rewrite.util; import java.util.ArrayList; import java.util.Collections; import java.util.List; import org.apache.asterix.common.exceptions.CompilationException; +import org.apache.asterix.graphix.lang.rewrite.visitor.GraphixDeepCopyVisitor; import org.apache.asterix.lang.common.base.Expression; import org.apache.asterix.lang.common.expression.FieldAccessor; import org.apache.asterix.lang.common.expression.OperatorExpr; import org.apache.asterix.lang.common.struct.Identifier; import org.apache.asterix.lang.common.struct.OperatorType; -import org.apache.asterix.lang.sqlpp.util.SqlppRewriteUtil; public final class LowerRewritingUtil { public static Expression buildConnectedClauses(List<Expression> clauses, OperatorType connector) { @@ -59,9 +59,10 @@ public static List<FieldAccessor> buildAccessorList(Expression startingExpr, List<List<String>> fieldNames) throws CompilationException { + GraphixDeepCopyVisitor deepCopyVisitor = new GraphixDeepCopyVisitor(); List<FieldAccessor> fieldAccessors = new ArrayList<>(); for (List<String> nestedField : fieldNames) { - Expression copiedStartingExpr = (Expression) SqlppRewriteUtil.deepCopy(startingExpr); + Expression copiedStartingExpr = (Expression) startingExpr.accept(deepCopyVisitor, null); FieldAccessor workingAccessor = new FieldAccessor(copiedStartingExpr, new Identifier(nestedField.get(0))); for (String field : nestedField.subList(1, nestedField.size())) { workingAccessor = new FieldAccessor(workingAccessor, new Identifier(field));
diff --git a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrite/visitor/AmbiguousElementVisitor.java b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrite/visitor/AmbiguousElementVisitor.java new file mode 100644 index 0000000..da9bff9 --- /dev/null +++ b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrite/visitor/AmbiguousElementVisitor.java
@@ -0,0 +1,166 @@ +/* + * 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.graphix.lang.rewrite.visitor; + +import java.util.ArrayDeque; +import java.util.Deque; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import org.apache.asterix.common.exceptions.CompilationException; +import org.apache.asterix.graphix.algebra.compiler.option.ElementEvaluationOption; +import org.apache.asterix.graphix.algebra.compiler.option.IGraphixCompilerOption; +import org.apache.asterix.graphix.lang.annotation.ElementEvaluationAnnotation; +import org.apache.asterix.graphix.lang.annotation.SubqueryVertexJoinAnnotation; +import org.apache.asterix.graphix.lang.clause.FromGraphClause; +import org.apache.asterix.graphix.lang.expression.EdgePatternExpr; +import org.apache.asterix.graphix.lang.expression.PathPatternExpr; +import org.apache.asterix.graphix.lang.expression.VertexPatternExpr; +import org.apache.asterix.graphix.lang.rewrite.GraphixRewritingContext; +import org.apache.asterix.graphix.lang.rewrite.canonical.CanonicalElementGeneratorFactory; +import org.apache.asterix.graphix.lang.rewrite.resolve.SchemaKnowledgeTable; +import org.apache.asterix.graphix.lang.struct.EdgeDescriptor; +import org.apache.asterix.graphix.lang.visitor.base.AbstractGraphixQueryVisitor; +import org.apache.asterix.lang.common.base.AbstractExpression; +import org.apache.asterix.lang.common.base.Expression; +import org.apache.asterix.lang.common.base.ILangExpression; +import org.apache.asterix.lang.common.expression.VariableExpr; +import org.apache.asterix.lang.sqlpp.clause.SelectBlock; + +/** + * @see QueryCanonicalizationVisitor + */ +public class AmbiguousElementVisitor extends AbstractGraphixQueryVisitor { + private final Map<AbstractExpression, Context> elementContextMap; + private final Deque<CanonicalElementGeneratorFactory> factoryStack; + private final Deque<SelectBlock> selectBlockStack; + + private final GraphixRewritingContext graphixRewritingContext; + private final ElementEvaluationOption defaultElementEvaluation; + + private static class Context { + final CanonicalElementGeneratorFactory generatorFactory; + final ElementEvaluationOption elementEvaluationOption; + final SelectBlock sourceSelectBlock; + + private Context(CanonicalElementGeneratorFactory generatorFactory, + ElementEvaluationOption elementEvaluationOption, SelectBlock sourceSelectBlock) { + this.generatorFactory = generatorFactory; + this.elementEvaluationOption = elementEvaluationOption; + this.sourceSelectBlock = sourceSelectBlock; + } + } + + public AmbiguousElementVisitor(GraphixRewritingContext graphixRewritingContext) throws CompilationException { + IGraphixCompilerOption setting = graphixRewritingContext.getSetting(ElementEvaluationOption.OPTION_KEY_NAME); + this.defaultElementEvaluation = (ElementEvaluationOption) setting; + this.graphixRewritingContext = graphixRewritingContext; + this.elementContextMap = new HashMap<>(); + this.selectBlockStack = new ArrayDeque<>(); + this.factoryStack = new ArrayDeque<>(); + } + + @Override + public Expression visit(SelectBlock selectBlock, ILangExpression arg) throws CompilationException { + selectBlockStack.push(selectBlock); + super.visit(selectBlock, arg); + selectBlockStack.pop(); + return null; + } + + @Override + public Expression visit(FromGraphClause fromGraphClause, ILangExpression arg) throws CompilationException { + SchemaKnowledgeTable knowledgeTable = new SchemaKnowledgeTable(fromGraphClause, graphixRewritingContext); + factoryStack.push(new CanonicalElementGeneratorFactory(graphixRewritingContext, knowledgeTable)); + super.visit(fromGraphClause, arg); + factoryStack.pop(); + return null; + } + + @Override + public Expression visit(PathPatternExpr pathPatternExpr, ILangExpression arg) throws CompilationException { + Set<VariableExpr> visitedVertices = new HashSet<>(); + for (EdgePatternExpr edgeExpression : pathPatternExpr.getEdgeExpressions()) { + edgeExpression.accept(this, arg); + visitedVertices.add(edgeExpression.getLeftVertex().getVariableExpr()); + visitedVertices.add(edgeExpression.getRightVertex().getVariableExpr()); + } + for (VertexPatternExpr vertexExpression : pathPatternExpr.getVertexExpressions()) { + if (!visitedVertices.contains(vertexExpression.getVariableExpr())) { + vertexExpression.accept(this, arg); + } + } + return pathPatternExpr; + } + + @Override + public Expression visit(EdgePatternExpr edgePatternExpr, ILangExpression arg) throws CompilationException { + VertexPatternExpr leftVertex = edgePatternExpr.getLeftVertex(); + VertexPatternExpr rightVertex = edgePatternExpr.getRightVertex(); + EdgeDescriptor edgeDescriptor = edgePatternExpr.getEdgeDescriptor(); + if ((edgeDescriptor.getEdgeLabels().size() + leftVertex.getLabels().size() + rightVertex.getLabels().size()) > 3 + || edgeDescriptor.getPatternType() == EdgeDescriptor.PatternType.PATH + || edgeDescriptor.getEdgeDirection() == EdgeDescriptor.EdgeDirection.UNDIRECTED) { + elementContextMap.put(edgePatternExpr, new Context(factoryStack.peek(), + getEvaluationFromAnnotation(edgePatternExpr), selectBlockStack.peek())); + } + return super.visit(edgePatternExpr, arg); + } + + @Override + public Expression visit(VertexPatternExpr vertexPatternExpr, ILangExpression arg) throws CompilationException { + if (vertexPatternExpr.getLabels().size() > 1 + && vertexPatternExpr.findHint(SubqueryVertexJoinAnnotation.class) == null) { + elementContextMap.put(vertexPatternExpr, new Context(factoryStack.peek(), + ElementEvaluationOption.EXPAND_AND_UNION, selectBlockStack.peek())); + } + return super.visit(vertexPatternExpr, arg); + } + + private ElementEvaluationOption getEvaluationFromAnnotation(AbstractExpression expression) { + ElementEvaluationAnnotation hint = expression.findHint(ElementEvaluationAnnotation.class); + if (hint == null) { + return defaultElementEvaluation; + + } else if (hint.getKind() == ElementEvaluationAnnotation.Kind.EXPAND_AND_UNION) { + return ElementEvaluationOption.EXPAND_AND_UNION; + + } else { // hint.getKind() == ElementEvaluationAnnotation.Kind.SWITCH_AND_CYCLE + return ElementEvaluationOption.SWITCH_AND_CYCLE; + } + } + + public Set<AbstractExpression> getAmbiguousElements() { + return elementContextMap.keySet(); + } + + public CanonicalElementGeneratorFactory getGeneratorFactory(AbstractExpression ambiguousElement) { + return elementContextMap.get(ambiguousElement).generatorFactory; + } + + public SelectBlock getSourceSelectBlock(AbstractExpression ambiguousElement) { + return elementContextMap.get(ambiguousElement).sourceSelectBlock; + } + + public ElementEvaluationOption getElementEvaluationOption(AbstractExpression ambiguousElement) { + return elementContextMap.get(ambiguousElement).elementEvaluationOption; + } +}
diff --git a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/visitor/ElementBodyAnalysisVisitor.java b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrite/visitor/ElementBodyAnalysisVisitor.java similarity index 90% rename from asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/visitor/ElementBodyAnalysisVisitor.java rename to asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrite/visitor/ElementBodyAnalysisVisitor.java index 448f698..1fca964 100644 --- a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/visitor/ElementBodyAnalysisVisitor.java +++ b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrite/visitor/ElementBodyAnalysisVisitor.java
@@ -16,10 +16,11 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.asterix.graphix.lang.rewrites.visitor; +package org.apache.asterix.graphix.lang.rewrite.visitor; import java.util.ArrayList; import java.util.List; +import java.util.stream.Collectors; import org.apache.asterix.common.exceptions.CompilationException; import org.apache.asterix.common.exceptions.ErrorCode; @@ -42,6 +43,7 @@ import org.apache.asterix.lang.sqlpp.clause.SelectClause; import org.apache.asterix.lang.sqlpp.clause.SelectRegular; import org.apache.asterix.lang.sqlpp.clause.SelectSetOperation; +import org.apache.asterix.lang.sqlpp.clause.UnnestClause; import org.apache.asterix.lang.sqlpp.expression.SelectExpression; import org.apache.asterix.lang.sqlpp.struct.SetOperationInput; import org.apache.asterix.lang.sqlpp.util.FunctionMapUtil; @@ -52,13 +54,15 @@ /** * Perform an analysis of a normalized graph element body (i.e. no Graphix AST nodes) to determine if we can inline * this graph element body with the greater SELECT-BLOCK node during lowering. - * 1. Is this a dataset CALL-EXPR? If so, we can inline this directly. - * 2. Is this expression a SELECT-EXPR containing a single FROM-TERM w/ possibly only UNNEST clauses? If so, we can - * inline this expression. - * 3. Are there are LET-WHERE expressions? We can inline these if the two questions are true. - * 4. Are there any aggregate functions (or any aggregation)? If so, we cannot inline this expression. - * 5. Are there any UNION-ALLs? If so, we cannot inline this expression. - * 6. Are there any ORDER-BY or LIMIT clauses? If so, we cannot inline this expression. + * <ol> + * <li>Is this a dataset CALL-EXPR? If so, we can inline this directly.</li> + * <li>Is this expression a SELECT-EXPR containing a single FROM-TERM w/ possibly only UNNEST clauses? If so, we can + * inline this expression.</li> + * <li>Are there are LET-WHERE expressions? We can inline these if the two questions are true.</li> + * <li>Are there any aggregate functions (or any aggregation)? If so, we cannot inline this expression.</li> + * <li>Are there any UNION-ALLs? If so, we cannot inline this expression.</li> + * <li>Are there any ORDER-BY or LIMIT clauses? If so, we cannot inline this expression.</li> + * </ol> */ public class ElementBodyAnalysisVisitor extends AbstractSqlppSimpleExpressionVisitor { private final ElementBodyAnalysisContext elementBodyAnalysisContext = new ElementBodyAnalysisContext(); @@ -225,7 +229,10 @@ @Override public Expression visit(FromTerm fromTerm, ILangExpression arg) throws CompilationException { List<AbstractBinaryCorrelateClause> correlateClauses = fromTerm.getCorrelateClauses(); - if (correlateClauses.stream().anyMatch(c -> !c.getClauseType().equals(Clause.ClauseType.UNNEST_CLAUSE))) { + List<AbstractBinaryCorrelateClause> unnestClauses = + correlateClauses.stream().filter(c -> c.getClauseType().equals(Clause.ClauseType.UNNEST_CLAUSE)) + .map(c -> (UnnestClause) c).collect(Collectors.toList()); + if (correlateClauses.size() != unnestClauses.size()) { elementBodyAnalysisContext.isExpressionInline = false; return null; @@ -233,10 +240,10 @@ // TODO (GLENN): Add support for positional variables. elementBodyAnalysisContext.isExpressionInline = false; return null; - } - if (!correlateClauses.isEmpty()) { - elementBodyAnalysisContext.unnestClauses = correlateClauses; + + if (!unnestClauses.isEmpty()) { + elementBodyAnalysisContext.unnestClauses = unnestClauses; } fromTerm.getLeftExpression().accept(this, arg); elementBodyAnalysisContext.fromTermVariable = fromTerm.getLeftVariable();
diff --git a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/visitor/ElementLookupTableVisitor.java b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrite/visitor/ElementLookupTableVisitor.java similarity index 83% rename from asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/visitor/ElementLookupTableVisitor.java rename to asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrite/visitor/ElementLookupTableVisitor.java index 6ba1d87..9bbe3f0 100644 --- a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/visitor/ElementLookupTableVisitor.java +++ b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrite/visitor/ElementLookupTableVisitor.java
@@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.asterix.graphix.lang.rewrites.visitor; +package org.apache.asterix.graphix.lang.rewrite.visitor; import static org.apache.asterix.graphix.lang.parser.GraphElementBodyParser.parse; @@ -28,8 +28,9 @@ import org.apache.asterix.common.exceptions.CompilationException; import org.apache.asterix.common.exceptions.ErrorCode; import org.apache.asterix.common.metadata.DataverseName; -import org.apache.asterix.graphix.common.metadata.GraphElementIdentifier; +import org.apache.asterix.graphix.common.metadata.EdgeIdentifier; import org.apache.asterix.graphix.common.metadata.GraphIdentifier; +import org.apache.asterix.graphix.common.metadata.VertexIdentifier; import org.apache.asterix.graphix.extension.GraphixMetadataExtension; import org.apache.asterix.graphix.lang.clause.FromGraphClause; import org.apache.asterix.graphix.lang.clause.MatchClause; @@ -37,12 +38,13 @@ import org.apache.asterix.graphix.lang.expression.GraphConstructor; import org.apache.asterix.graphix.lang.expression.VertexPatternExpr; import org.apache.asterix.graphix.lang.parser.GraphixParserFactory; -import org.apache.asterix.graphix.lang.rewrites.GraphixRewritingContext; -import org.apache.asterix.graphix.lang.rewrites.common.ElementLookupTable; +import org.apache.asterix.graphix.lang.rewrite.GraphixRewritingContext; +import org.apache.asterix.graphix.lang.rewrite.common.ElementLookupTable; import org.apache.asterix.graphix.lang.statement.DeclareGraphStatement; import org.apache.asterix.graphix.lang.statement.GraphElementDeclaration; import org.apache.asterix.graphix.lang.struct.EdgeDescriptor; import org.apache.asterix.graphix.lang.struct.ElementLabel; +import org.apache.asterix.graphix.lang.visitor.base.AbstractGraphixQueryVisitor; import org.apache.asterix.graphix.metadata.entity.schema.Edge; import org.apache.asterix.graphix.metadata.entity.schema.Graph; import org.apache.asterix.graphix.metadata.entity.schema.Vertex; @@ -55,7 +57,7 @@ /** * Populate the given graph element table, which will hold all referenced {@link GraphElementDeclaration}s. We assume - * that our graph elements are properly labeled at this point (i.e. {@link StructureResolutionVisitor} must run before + * that our graph elements are properly labeled at this point (i.e. {@link PatternGraphGroupVisitor} must run before * this). */ public class ElementLookupTableVisitor extends AbstractGraphixQueryVisitor { @@ -63,7 +65,7 @@ private final MetadataProvider metadataProvider; private final GraphixParserFactory parserFactory; - private final Set<ElementLabel> referencedVertexLabels = new HashSet<>(); + private final Set<ElementLabel> referencedElementLabels = new HashSet<>(); private final Set<ElementLabel> referencedEdgeLabels = new HashSet<>(); private final ElementLookupTable elementLookupTable; private final Map<GraphIdentifier, DeclareGraphStatement> declaredGraphs; @@ -84,14 +86,13 @@ } GraphConstructor graphConstructor = fromGraphClause.getGraphConstructor(); - GraphIdentifier graphIdentifier = null; + GraphIdentifier graphIdentifier = fromGraphClause.getGraphIdentifier(metadataProvider); if (graphConstructor == null) { DataverseName dataverseName = (fromGraphClause.getDataverseName() == null) ? metadataProvider.getDefaultDataverseName() : fromGraphClause.getDataverseName(); Identifier graphName = fromGraphClause.getGraphName(); // Our query refers to a named graph. First see if we can find this in our declared graph set. - graphIdentifier = new GraphIdentifier(dataverseName, graphName.getValue()); DeclareGraphStatement declaredGraph = declaredGraphs.get(graphIdentifier); if (declaredGraph != null) { graphConstructor = declaredGraph.getGraphConstructor(); @@ -113,7 +114,7 @@ } for (Vertex vertex : graphFromMetadata.getGraphSchema().getVertices()) { - if (referencedVertexLabels.contains(vertex.getLabel())) { + if (referencedElementLabels.contains(vertex.getLabel())) { GraphElementDeclaration vertexDecl = parse(vertex, parserFactory, warningCollector); elementLookupTable.put(vertex.getIdentifier(), vertexDecl); elementLookupTable.putVertexKey(vertex.getIdentifier(), vertex.getPrimaryKeyFieldNames()); @@ -130,24 +131,17 @@ } } if (graphConstructor != null) { - if (graphIdentifier == null) { - // We have been provided an anonymous graph. Load the referenced elements from our walk. - DataverseName defaultDataverse = metadataProvider.getDefaultDataverse().getDataverseName(); - graphIdentifier = new GraphIdentifier(defaultDataverse, graphConstructor.getInstanceID()); - } - for (GraphConstructor.VertexConstructor vertex : graphConstructor.getVertexElements()) { - if (referencedVertexLabels.contains(vertex.getLabel())) { - GraphElementIdentifier identifier = new GraphElementIdentifier(graphIdentifier, - GraphElementIdentifier.Kind.VERTEX, vertex.getLabel()); + if (referencedElementLabels.contains(vertex.getLabel())) { + VertexIdentifier identifier = new VertexIdentifier(graphIdentifier, vertex.getLabel()); elementLookupTable.put(identifier, new GraphElementDeclaration(identifier, vertex.getExpression())); elementLookupTable.putVertexKey(identifier, vertex.getPrimaryKeyFields()); } } for (GraphConstructor.EdgeConstructor edge : graphConstructor.getEdgeElements()) { if (referencedEdgeLabels.contains(edge.getEdgeLabel())) { - GraphElementIdentifier identifier = new GraphElementIdentifier(graphIdentifier, - GraphElementIdentifier.Kind.EDGE, edge.getEdgeLabel()); + EdgeIdentifier identifier = new EdgeIdentifier(graphIdentifier, edge.getSourceLabel(), + edge.getEdgeLabel(), edge.getDestinationLabel()); elementLookupTable.put(identifier, new GraphElementDeclaration(identifier, edge.getExpression())); elementLookupTable.putEdgeKeys(identifier, edge.getSourceKeyFields(), edge.getDestinationKeyFields()); @@ -164,8 +158,8 @@ "EdgePatternExpr found without labels. Elements should have been resolved earlier."); } referencedEdgeLabels.addAll(edgeDescriptor.getEdgeLabels()); - for (VertexPatternExpr internalVertex : edgeExpression.getInternalVertices()) { - internalVertex.accept(this, arg); + if (edgeExpression.getInternalVertex() != null && edgeDescriptor.getMaximumHops() != 1) { + edgeExpression.getInternalVertex().accept(this, arg); } return edgeExpression; } @@ -175,7 +169,7 @@ throw new CompilationException(ErrorCode.COMPILATION_ILLEGAL_STATE, vertexExpression.getSourceLocation(), "VertexPatternExpr found without labels. Elements should have been resolved earlier."); } - referencedVertexLabels.addAll(vertexExpression.getLabels()); + referencedElementLabels.addAll(vertexExpression.getLabels()); return vertexExpression; } }
diff --git a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrite/visitor/ElementSubstitutionVisitor.java b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrite/visitor/ElementSubstitutionVisitor.java new file mode 100644 index 0000000..16c5200 --- /dev/null +++ b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrite/visitor/ElementSubstitutionVisitor.java
@@ -0,0 +1,101 @@ +/* + * 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.graphix.lang.rewrite.visitor; + +import java.util.List; +import java.util.ListIterator; + +import org.apache.asterix.common.exceptions.CompilationException; +import org.apache.asterix.graphix.lang.clause.MatchClause; +import org.apache.asterix.graphix.lang.expression.EdgePatternExpr; +import org.apache.asterix.graphix.lang.expression.PathPatternExpr; +import org.apache.asterix.graphix.lang.expression.VertexPatternExpr; +import org.apache.asterix.graphix.lang.visitor.base.AbstractGraphixQueryVisitor; +import org.apache.asterix.lang.common.base.AbstractExpression; +import org.apache.asterix.lang.common.base.Expression; +import org.apache.asterix.lang.common.base.ILangExpression; + +/** + * Visitor to replace a {@link VertexPatternExpr}, a {@link EdgePatternExpr}, or a {@link PathPatternExpr}. + */ +public class ElementSubstitutionVisitor<T extends AbstractExpression> extends AbstractGraphixQueryVisitor { + private final T replacementElementExpression; + private final T originalElementExpression; + + public ElementSubstitutionVisitor(T replacementElementExpression, T originalElementExpression) { + this.replacementElementExpression = replacementElementExpression; + this.originalElementExpression = originalElementExpression; + if (!((replacementElementExpression instanceof VertexPatternExpr) + || (replacementElementExpression instanceof EdgePatternExpr) + || (replacementElementExpression instanceof PathPatternExpr))) { + throw new IllegalArgumentException( + "Only VertexPatternExpr, EdgePatternExpr, or PathPatternExpr " + "instances should be given."); + } + } + + @Override + public Expression visit(MatchClause matchClause, ILangExpression arg) throws CompilationException { + if (replacementElementExpression instanceof PathPatternExpr) { + ListIterator<PathPatternExpr> pathIterator = matchClause.getPathExpressions().listIterator(); + while (pathIterator.hasNext()) { + PathPatternExpr pathPatternExpr = pathIterator.next(); + if (pathPatternExpr.equals(originalElementExpression)) { + pathIterator.set((PathPatternExpr) replacementElementExpression); + break; + } + } + return null; + } + return super.visit(matchClause, arg); + } + + @Override + public Expression visit(PathPatternExpr pathPatternExpr, ILangExpression arg) throws CompilationException { + if (replacementElementExpression instanceof VertexPatternExpr) { + ListIterator<VertexPatternExpr> vertexExprIterator = pathPatternExpr.getVertexExpressions().listIterator(); + while (vertexExprIterator.hasNext()) { + VertexPatternExpr workingVertexExpression = vertexExprIterator.next(); + if (workingVertexExpression.equals(originalElementExpression)) { + vertexExprIterator.set((VertexPatternExpr) replacementElementExpression); + break; + } + } + List<EdgePatternExpr> edgeExpressions = pathPatternExpr.getEdgeExpressions(); + for (EdgePatternExpr workingEdgeExpression : edgeExpressions) { + if (workingEdgeExpression.getLeftVertex().equals(originalElementExpression)) { + workingEdgeExpression.setLeftVertex((VertexPatternExpr) replacementElementExpression); + } + if (workingEdgeExpression.getRightVertex().equals(originalElementExpression)) { + workingEdgeExpression.setRightVertex((VertexPatternExpr) replacementElementExpression); + } + } + + } else { // replacementElementExpression instanceof EdgePatternExpr + ListIterator<EdgePatternExpr> edgeExprIterator = pathPatternExpr.getEdgeExpressions().listIterator(); + while (edgeExprIterator.hasNext()) { + EdgePatternExpr workingEdgeExpression = edgeExprIterator.next(); + if (workingEdgeExpression.equals(originalElementExpression)) { + edgeExprIterator.set((EdgePatternExpr) replacementElementExpression); + break; + } + } + } + return pathPatternExpr; + } +}
diff --git a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/visitor/FunctionResolutionVisitor.java b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrite/visitor/FunctionResolutionVisitor.java similarity index 90% rename from asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/visitor/FunctionResolutionVisitor.java rename to asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrite/visitor/FunctionResolutionVisitor.java index 689f58f..961c270 100644 --- a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/visitor/FunctionResolutionVisitor.java +++ b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrite/visitor/FunctionResolutionVisitor.java
@@ -14,12 +14,13 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.asterix.graphix.lang.rewrites.visitor; +package org.apache.asterix.graphix.lang.rewrite.visitor; import org.apache.asterix.common.exceptions.CompilationException; import org.apache.asterix.common.functions.FunctionSignature; import org.apache.asterix.graphix.function.GraphixFunctionResolver; -import org.apache.asterix.graphix.lang.rewrites.GraphixRewritingContext; +import org.apache.asterix.graphix.lang.rewrite.GraphixRewritingContext; +import org.apache.asterix.graphix.lang.visitor.base.AbstractGraphixQueryVisitor; import org.apache.asterix.lang.common.base.Expression; import org.apache.asterix.lang.common.base.ILangExpression; import org.apache.asterix.lang.common.expression.CallExpr; @@ -49,8 +50,9 @@ FunctionSignature functionSignature = graphixFunctionResolver.resolve(callExpr, allowNonStoredUdfCalls); if (functionSignature != null) { callExpr.setFunctionSignature(functionSignature); + return super.visit(callExpr, arg); } - return super.visit(callExpr, arg); + return sqlppFunctionCallResolverVisitor.visit(callExpr, arg); } @Override
diff --git a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/visitor/GraphixDeepCopyVisitor.java b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrite/visitor/GraphixDeepCopyVisitor.java similarity index 66% rename from asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/visitor/GraphixDeepCopyVisitor.java rename to asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrite/visitor/GraphixDeepCopyVisitor.java index 8a4b93c..2280e75 100644 --- a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/visitor/GraphixDeepCopyVisitor.java +++ b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrite/visitor/GraphixDeepCopyVisitor.java
@@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.asterix.graphix.lang.rewrites.visitor; +package org.apache.asterix.graphix.lang.rewrite.visitor; import java.util.ArrayList; import java.util.HashSet; @@ -25,7 +25,6 @@ import org.apache.asterix.common.exceptions.CompilationException; import org.apache.asterix.graphix.lang.clause.FromGraphClause; -import org.apache.asterix.graphix.lang.clause.GraphSelectBlock; import org.apache.asterix.graphix.lang.clause.MatchClause; import org.apache.asterix.graphix.lang.expression.EdgePatternExpr; import org.apache.asterix.graphix.lang.expression.GraphConstructor; @@ -37,13 +36,16 @@ import org.apache.asterix.graphix.lang.statement.GraphElementDeclaration; import org.apache.asterix.graphix.lang.struct.EdgeDescriptor; import org.apache.asterix.graphix.lang.struct.ElementLabel; +import org.apache.asterix.graphix.lang.visitor.base.IGraphixLangVisitor; import org.apache.asterix.lang.common.base.AbstractClause; +import org.apache.asterix.lang.common.base.Expression; import org.apache.asterix.lang.common.base.ILangExpression; import org.apache.asterix.lang.common.clause.GroupbyClause; import org.apache.asterix.lang.common.clause.LetClause; import org.apache.asterix.lang.common.expression.VariableExpr; -import org.apache.asterix.lang.common.struct.VarIdentifier; import org.apache.asterix.lang.sqlpp.clause.AbstractBinaryCorrelateClause; +import org.apache.asterix.lang.sqlpp.clause.FromClause; +import org.apache.asterix.lang.sqlpp.clause.SelectBlock; import org.apache.asterix.lang.sqlpp.clause.SelectClause; import org.apache.asterix.lang.sqlpp.visitor.DeepCopyVisitor; @@ -52,23 +54,31 @@ */ public class GraphixDeepCopyVisitor extends DeepCopyVisitor implements IGraphixLangVisitor<ILangExpression, Void> { @Override - public GraphSelectBlock visit(GraphSelectBlock graphSelectBlock, Void arg) throws CompilationException { - SelectClause clonedSelectClause = this.visit(graphSelectBlock.getSelectClause(), arg); - FromGraphClause clonedFromGraphClause = this.visit(graphSelectBlock.getFromGraphClause(), arg); + public SelectBlock visit(SelectBlock selectBlock, Void arg) throws CompilationException { + SelectClause clonedSelectClause = this.visit(selectBlock.getSelectClause(), arg); + FromClause clonedFromClause = null; + if (selectBlock.hasFromClause() && selectBlock.getFromClause() instanceof FromGraphClause) { + clonedFromClause = this.visit((FromGraphClause) selectBlock.getFromClause(), arg); + + } else if (selectBlock.hasFromClause()) { + clonedFromClause = super.visit(selectBlock.getFromClause(), arg); + } GroupbyClause clonedGroupByClause = null; - if (graphSelectBlock.hasGroupbyClause()) { - clonedGroupByClause = this.visit(graphSelectBlock.getGroupbyClause(), arg); + if (selectBlock.hasGroupbyClause()) { + clonedGroupByClause = this.visit(selectBlock.getGroupbyClause(), arg); } List<AbstractClause> clonedLetWhereClauses = new ArrayList<>(); List<AbstractClause> clonedLetHavingClauses = new ArrayList<>(); - for (AbstractClause letWhereClause : graphSelectBlock.getLetWhereList()) { + for (AbstractClause letWhereClause : selectBlock.getLetWhereList()) { clonedLetWhereClauses.add((AbstractClause) letWhereClause.accept(this, arg)); } - for (AbstractClause letHavingClause : graphSelectBlock.getLetHavingListAfterGroupby()) { + for (AbstractClause letHavingClause : selectBlock.getLetHavingListAfterGroupby()) { clonedLetHavingClauses.add((AbstractClause) letHavingClause.accept(this, arg)); } - return new GraphSelectBlock(clonedSelectClause, clonedFromGraphClause, clonedLetWhereClauses, + SelectBlock clonedSelectBlock = new SelectBlock(clonedSelectClause, clonedFromClause, clonedLetWhereClauses, clonedGroupByClause, clonedLetHavingClauses); + clonedSelectBlock.setSourceLocation(selectBlock.getSourceLocation()); + return clonedSelectBlock; } @Override @@ -81,14 +91,17 @@ for (MatchClause matchClause : fromGraphClause.getMatchClauses()) { clonedMatchClauses.add(this.visit(matchClause, arg)); } + FromGraphClause clonedFromGraphClause; if (fromGraphClause.getGraphConstructor() != null) { GraphConstructor graphConstructor = fromGraphClause.getGraphConstructor(); - return new FromGraphClause(graphConstructor, clonedMatchClauses, clonedCorrelateClauses); + clonedFromGraphClause = new FromGraphClause(graphConstructor, clonedMatchClauses, clonedCorrelateClauses); } else { - return new FromGraphClause(fromGraphClause.getDataverseName(), fromGraphClause.getGraphName(), - clonedMatchClauses, clonedCorrelateClauses); + clonedFromGraphClause = new FromGraphClause(fromGraphClause.getDataverseName(), + fromGraphClause.getGraphName(), clonedMatchClauses, clonedCorrelateClauses); } + clonedFromGraphClause.setSourceLocation(fromGraphClause.getSourceLocation()); + return clonedFromGraphClause; } @Override @@ -97,7 +110,9 @@ for (PathPatternExpr pathExpression : matchClause.getPathExpressions()) { clonedPathExpression.add(this.visit(pathExpression, arg)); } - return new MatchClause(clonedPathExpression, matchClause.getMatchType()); + MatchClause clonedMatchClause = new MatchClause(clonedPathExpression, matchClause.getMatchType()); + clonedMatchClause.setSourceLocation(matchClause.getSourceLocation()); + return clonedMatchClause; } @Override @@ -110,26 +125,31 @@ } // Only visit dangling vertices in our edge. - Set<VarIdentifier> visitedVertices = new HashSet<>(); + Set<VariableExpr> visitedVertices = new HashSet<>(); for (EdgePatternExpr edgeExpression : pathPatternExpr.getEdgeExpressions()) { EdgePatternExpr clonedEdgeExpression = this.visit(edgeExpression, arg); clonedEdgeExpressions.add(clonedEdgeExpression); - clonedVertexExpressions.add(clonedEdgeExpression.getLeftVertex()); - clonedVertexExpressions.add(clonedEdgeExpression.getRightVertex()); - visitedVertices.add(clonedEdgeExpression.getRightVertex().getVariableExpr().getVar()); - visitedVertices.add(clonedEdgeExpression.getLeftVertex().getVariableExpr().getVar()); + clonedEdgeExpression.setSourceLocation(edgeExpression.getSourceLocation()); + VertexPatternExpr clonedLeftVertex = clonedEdgeExpression.getLeftVertex(); + VertexPatternExpr clonedRightVertex = clonedEdgeExpression.getRightVertex(); + clonedVertexExpressions.add(clonedLeftVertex); + clonedVertexExpressions.add(clonedRightVertex); + visitedVertices.add(clonedLeftVertex.getVariableExpr()); + visitedVertices.add(clonedRightVertex.getVariableExpr()); } for (VertexPatternExpr vertexExpression : pathPatternExpr.getVertexExpressions()) { - if (!visitedVertices.contains(vertexExpression.getVariableExpr().getVar())) { + if (!visitedVertices.contains(vertexExpression.getVariableExpr())) { VertexPatternExpr clonedVertexExpression = this.visit(vertexExpression, arg); + clonedVertexExpression.setSourceLocation(vertexExpression.getSourceLocation()); clonedVertexExpressions.add(clonedVertexExpression); - visitedVertices.add(clonedVertexExpression.getVariableExpr().getVar()); + visitedVertices.add(clonedVertexExpression.getVariableExpr()); } } // Clone our sub-path expressions. PathPatternExpr clonedPathPatternExpr = new PathPatternExpr(clonedVertexExpressions, clonedEdgeExpressions, clonedVariableExpr); + clonedPathPatternExpr.setSourceLocation(pathPatternExpr.getSourceLocation()); for (LetClause letClause : pathPatternExpr.getReboundSubPathList()) { clonedPathPatternExpr.getReboundSubPathList().add(this.visit(letClause, arg)); } @@ -141,23 +161,30 @@ // Clone our expressions. VertexPatternExpr clonedLeftVertex = this.visit(edgePatternExpr.getLeftVertex(), arg); VertexPatternExpr clonedRightVertex = this.visit(edgePatternExpr.getRightVertex(), arg); - List<VertexPatternExpr> clonedInternalVertices = new ArrayList<>(); - for (VertexPatternExpr internalVertex : edgePatternExpr.getInternalVertices()) { - clonedInternalVertices.add(this.visit(internalVertex, arg)); + VertexPatternExpr clonedInternalVertex = null; + if (edgePatternExpr.getInternalVertex() != null) { + clonedInternalVertex = this.visit(edgePatternExpr.getInternalVertex(), arg); } VariableExpr clonedVariableExpr = null; if (edgePatternExpr.getEdgeDescriptor().getVariableExpr() != null) { clonedVariableExpr = this.visit(edgePatternExpr.getEdgeDescriptor().getVariableExpr(), arg); } + clonedLeftVertex.setSourceLocation(edgePatternExpr.getLeftVertex().getSourceLocation()); + clonedRightVertex.setSourceLocation(edgePatternExpr.getRightVertex().getSourceLocation()); // Generate a cloned edge. EdgeDescriptor edgeDescriptor = edgePatternExpr.getEdgeDescriptor(); Set<ElementLabel> clonedEdgeDescriptorLabels = new HashSet<>(edgeDescriptor.getEdgeLabels()); + Expression clonedFilterExpr = null; + if (edgeDescriptor.getFilterExpr() != null) { + clonedFilterExpr = (Expression) edgeDescriptor.getFilterExpr().accept(this, arg); + } EdgeDescriptor clonedDescriptor = new EdgeDescriptor(edgeDescriptor.getEdgeDirection(), - edgeDescriptor.getPatternType(), clonedEdgeDescriptorLabels, clonedVariableExpr, + edgeDescriptor.getPatternType(), clonedEdgeDescriptorLabels, clonedFilterExpr, clonedVariableExpr, edgeDescriptor.getMinimumHops(), edgeDescriptor.getMaximumHops()); EdgePatternExpr clonedEdge = new EdgePatternExpr(clonedLeftVertex, clonedRightVertex, clonedDescriptor); - clonedEdge.replaceInternalVertices(clonedInternalVertices); + clonedEdge.setInternalVertex(clonedInternalVertex); + clonedEdge.setSourceLocation(edgePatternExpr.getSourceLocation()); return clonedEdge; } @@ -167,8 +194,15 @@ if (vertexPatternExpr.getVariableExpr() != null) { clonedVariableExpr = this.visit(vertexPatternExpr.getVariableExpr(), arg); } - Set<ElementLabel> clonedVertexLabels = new HashSet<>(vertexPatternExpr.getLabels()); - return new VertexPatternExpr(clonedVariableExpr, clonedVertexLabels); + Expression clonedFilterExpr = null; + if (vertexPatternExpr.getFilterExpr() != null) { + clonedFilterExpr = (Expression) vertexPatternExpr.getFilterExpr().accept(this, arg); + } + Set<ElementLabel> clonedElementLabels = new HashSet<>(vertexPatternExpr.getLabels()); + VertexPatternExpr clonedVertexExpr = + new VertexPatternExpr(clonedVariableExpr, clonedFilterExpr, clonedElementLabels); + clonedVertexExpr.setSourceLocation(vertexPatternExpr.getSourceLocation()); + return clonedVertexExpr; } // We do not touch our GRAPH-CONSTRUCTOR here.
diff --git a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/visitor/GraphixFunctionCallVisitor.java b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrite/visitor/GraphixFunctionCallVisitor.java similarity index 96% rename from asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/visitor/GraphixFunctionCallVisitor.java rename to asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrite/visitor/GraphixFunctionCallVisitor.java index 399cb43..de34713 100644 --- a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/visitor/GraphixFunctionCallVisitor.java +++ b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrite/visitor/GraphixFunctionCallVisitor.java
@@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.asterix.graphix.lang.rewrites.visitor; +package org.apache.asterix.graphix.lang.rewrite.visitor; import java.util.Map; @@ -27,7 +27,7 @@ import org.apache.asterix.graphix.function.GraphixFunctionMap; import org.apache.asterix.graphix.function.GraphixFunctionResolver; import org.apache.asterix.graphix.function.rewrite.IFunctionRewrite; -import org.apache.asterix.graphix.lang.rewrites.GraphixRewritingContext; +import org.apache.asterix.graphix.lang.rewrite.GraphixRewritingContext; import org.apache.asterix.lang.common.base.Expression; import org.apache.asterix.lang.common.base.ILangExpression; import org.apache.asterix.lang.common.expression.CallExpr;
diff --git a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrite/visitor/GraphixLoweringVisitor.java b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrite/visitor/GraphixLoweringVisitor.java new file mode 100644 index 0000000..088e698 --- /dev/null +++ b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrite/visitor/GraphixLoweringVisitor.java
@@ -0,0 +1,530 @@ +/* + * 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.graphix.lang.rewrite.visitor; + +import static org.apache.asterix.graphix.function.GraphixFunctionIdentifiers.isEdgeFunction; +import static org.apache.asterix.graphix.function.GraphixFunctionIdentifiers.isVertexFunction; + +import java.util.ArrayDeque; +import java.util.Deque; +import java.util.HashMap; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.ListIterator; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import java.util.function.Function; +import java.util.stream.Stream; + +import org.apache.asterix.common.exceptions.CompilationException; +import org.apache.asterix.common.exceptions.ErrorCode; +import org.apache.asterix.common.functions.FunctionSignature; +import org.apache.asterix.common.metadata.DataverseName; +import org.apache.asterix.graphix.algebra.compiler.option.IGraphixCompilerOption; +import org.apache.asterix.graphix.algebra.compiler.option.SchemaDecorateEdgeOption; +import org.apache.asterix.graphix.algebra.compiler.option.SchemaDecorateVertexOption; +import org.apache.asterix.graphix.common.metadata.EdgeIdentifier; +import org.apache.asterix.graphix.common.metadata.GraphIdentifier; +import org.apache.asterix.graphix.common.metadata.IElementIdentifier; +import org.apache.asterix.graphix.common.metadata.VertexIdentifier; +import org.apache.asterix.graphix.function.GraphixFunctionIdentifiers; +import org.apache.asterix.graphix.lang.annotation.LoweringExemptAnnotation; +import org.apache.asterix.graphix.lang.clause.FromGraphClause; +import org.apache.asterix.graphix.lang.clause.LowerListClause; +import org.apache.asterix.graphix.lang.clause.LowerSwitchClause.ClauseInputEnvironment; +import org.apache.asterix.graphix.lang.clause.LowerSwitchClause.ClauseOutputEnvironment; +import org.apache.asterix.graphix.lang.clause.MatchClause; +import org.apache.asterix.graphix.lang.expression.EdgePatternExpr; +import org.apache.asterix.graphix.lang.expression.PathPatternExpr; +import org.apache.asterix.graphix.lang.expression.VertexPatternExpr; +import org.apache.asterix.graphix.lang.optype.MatchType; +import org.apache.asterix.graphix.lang.rewrite.GraphixRewritingContext; +import org.apache.asterix.graphix.lang.rewrite.common.BranchLookupTable; +import org.apache.asterix.graphix.lang.rewrite.common.ElementLookupTable; +import org.apache.asterix.graphix.lang.rewrite.lower.AliasLookupTable; +import org.apache.asterix.graphix.lang.rewrite.lower.EnvironmentActionFactory; +import org.apache.asterix.graphix.lang.rewrite.lower.LoweringEnvironment; +import org.apache.asterix.graphix.lang.rewrite.lower.struct.ClauseCollection; +import org.apache.asterix.graphix.lang.rewrite.visitor.ElementBodyAnalysisVisitor.ElementBodyAnalysisContext; +import org.apache.asterix.graphix.lang.statement.GraphElementDeclaration; +import org.apache.asterix.graphix.lang.struct.EdgeDescriptor; +import org.apache.asterix.graphix.lang.struct.ElementLabel; +import org.apache.asterix.graphix.lang.visitor.base.AbstractGraphixQueryVisitor; +import org.apache.asterix.lang.common.base.AbstractClause; +import org.apache.asterix.lang.common.base.Expression; +import org.apache.asterix.lang.common.base.ILangExpression; +import org.apache.asterix.lang.common.clause.LetClause; +import org.apache.asterix.lang.common.expression.CallExpr; +import org.apache.asterix.lang.common.expression.VariableExpr; +import org.apache.asterix.lang.common.statement.Query; +import org.apache.asterix.lang.sqlpp.clause.SelectBlock; +import org.apache.asterix.lang.sqlpp.expression.SelectExpression; +import org.apache.asterix.lang.sqlpp.util.SqlppVariableUtil; +import org.apache.asterix.metadata.declared.MetadataProvider; +import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier; +import org.apache.hyracks.api.exceptions.SourceLocation; + +/** + * Rewrite a graph AST to utilize non-graph AST nodes (i.e. replace FROM-GRAPH-CLAUSEs with a LOWER-{LIST|BFS}-CLAUSE). + */ +public class GraphixLoweringVisitor extends AbstractGraphixQueryVisitor { + private final GraphixDeepCopyVisitor graphixDeepCopyVisitor; + private final VariableRemapCloneVisitor variableRemapCloneVisitor; + private final ElementLookupTable elementLookupTable; + private final GraphixRewritingContext graphixRewritingContext; + private SelectExpression topLevelSelectExpression; + + // Our stack corresponds to which GRAPH-SELECT-BLOCK we are currently working with. + private final Map<IElementIdentifier, ElementBodyAnalysisContext> analysisContextMap; + private final Deque<LoweringEnvironment> environmentStack; + private final AliasLookupTable aliasLookupTable; + private final BranchLookupTable branchLookupTable; + private final EnvironmentActionFactory actionFactory; + + public GraphixLoweringVisitor(GraphixRewritingContext graphixRewritingContext, + ElementLookupTable elementLookupTable, BranchLookupTable branchLookupTable) { + this.branchLookupTable = Objects.requireNonNull(branchLookupTable); + this.elementLookupTable = Objects.requireNonNull(elementLookupTable); + this.graphixRewritingContext = Objects.requireNonNull(graphixRewritingContext); + this.variableRemapCloneVisitor = new VariableRemapCloneVisitor(graphixRewritingContext); + this.graphixDeepCopyVisitor = new GraphixDeepCopyVisitor(); + this.aliasLookupTable = new AliasLookupTable(); + this.environmentStack = new ArrayDeque<>(); + this.analysisContextMap = new HashMap<>(); + + // All actions on our environment are supplied by the factory below. + this.actionFactory = new EnvironmentActionFactory(analysisContextMap, elementLookupTable, aliasLookupTable, + graphixRewritingContext); + } + + @Override + public Expression visit(Query query, ILangExpression arg) throws CompilationException { + boolean isTopLevelQuery = query.isTopLevel(); + boolean isSelectExpr = query.getBody().getKind() == Expression.Kind.SELECT_EXPRESSION; + if (isSelectExpr && (isTopLevelQuery || topLevelSelectExpression == null)) { + topLevelSelectExpression = (SelectExpression) query.getBody(); + } + return super.visit(query, arg); + } + + @Override + public Expression visit(SelectBlock selectBlock, ILangExpression arg) throws CompilationException { + SelectExpression selectExpression = (SelectExpression) arg; + if (selectBlock.hasFromClause() && selectBlock.getFromClause() instanceof FromGraphClause) { + FromGraphClause fromGraphClause = (FromGraphClause) selectBlock.getFromClause(); + MetadataProvider metadataProvider = graphixRewritingContext.getMetadataProvider(); + GraphIdentifier graphIdentifier = fromGraphClause.getGraphIdentifier(metadataProvider); + SourceLocation sourceLocation = fromGraphClause.getSourceLocation(); + + // Initialize a new lowering environment. + LoweringEnvironment newEnvironment = + new LoweringEnvironment(graphixRewritingContext, graphIdentifier, sourceLocation); + actionFactory.reset(graphIdentifier); + + // We will remove the FROM-GRAPH node and replace this with a FROM node on the child visit. + environmentStack.addLast(newEnvironment); + super.visit(selectBlock, arg); + environmentStack.removeLast(); + + // See if we need to perform a pass for schema enrichment. By default, we decorate "as-needed". + String schemaDecorateVertexKey = SchemaDecorateVertexOption.OPTION_KEY_NAME; + String schemaDecorateEdgeKey = SchemaDecorateEdgeOption.OPTION_KEY_NAME; + IGraphixCompilerOption vertexModeOption = graphixRewritingContext.getSetting(schemaDecorateVertexKey); + IGraphixCompilerOption edgeModeOption = graphixRewritingContext.getSetting(schemaDecorateEdgeKey); + SchemaDecorateVertexOption vertexMode = (SchemaDecorateVertexOption) vertexModeOption; + SchemaDecorateEdgeOption edgeMode = (SchemaDecorateEdgeOption) edgeModeOption; + + // See if there are any Graphix functions used in our query. + Set<FunctionIdentifier> graphixFunctionSet = new LinkedHashSet<>(); + topLevelSelectExpression.accept(new AbstractGraphixQueryVisitor() { + @Override + public Expression visit(CallExpr callExpr, ILangExpression arg) throws CompilationException { + FunctionSignature functionSignature = callExpr.getFunctionSignature(); + if (functionSignature.getDataverseName().equals(GraphixFunctionIdentifiers.GRAPHIX_DV)) { + FunctionIdentifier functionID = functionSignature.createFunctionIdentifier(); + if ((vertexMode == SchemaDecorateVertexOption.NEVER && isVertexFunction(functionID)) + || (edgeMode == SchemaDecorateEdgeOption.NEVER && isEdgeFunction(functionID))) { + throw new CompilationException(ErrorCode.COMPILATION_ERROR, callExpr.getSourceLocation(), + "Schema-decorate mode has been set to 'NEVER', but schema-decoration is required " + + "to realize the function" + functionSignature + "!"); + } + graphixFunctionSet.add(functionID); + } + return super.visit(callExpr, arg); + } + }, null); + + // Perform a pass for schema enrichment, if needed. + boolean isVertexModeAlways = vertexMode == SchemaDecorateVertexOption.ALWAYS; + boolean isEdgeModeAlways = edgeMode == SchemaDecorateEdgeOption.ALWAYS; + if (!graphixFunctionSet.isEmpty() || isVertexModeAlways || isEdgeModeAlways) { + SchemaEnrichmentVisitor schemaEnrichmentVisitor = new SchemaEnrichmentVisitor(vertexMode, edgeMode, + elementLookupTable, branchLookupTable, graphIdentifier, selectBlock, graphixFunctionSet); + selectExpression.accept(schemaEnrichmentVisitor, null); + if (selectExpression.hasOrderby()) { + selectExpression.getOrderbyClause().accept(schemaEnrichmentVisitor, null); + } + if (selectExpression.hasLimit()) { + selectExpression.getLimitClause().accept(schemaEnrichmentVisitor, null); + } + } + + } else { + super.visit(selectBlock, arg); + } + return null; + } + + @Override + public Expression visit(FromGraphClause fromGraphClause, ILangExpression arg) throws CompilationException { + // Perform an analysis pass over each element body. We need to determine what we can and can't inline. + for (GraphElementDeclaration graphElementDeclaration : elementLookupTable) { + ElementBodyAnalysisVisitor elementBodyAnalysisVisitor = new ElementBodyAnalysisVisitor(); + IElementIdentifier elementIdentifier = graphElementDeclaration.getIdentifier(); + graphElementDeclaration.getNormalizedBody().accept(elementBodyAnalysisVisitor, null); + analysisContextMap.put(elementIdentifier, elementBodyAnalysisVisitor.getElementBodyAnalysisContext()); + } + LoweringEnvironment workingEnvironment = environmentStack.getLast(); + + // TODO (GLENN): Perform smarter analysis to determine when a vertex / edge is inlineable w/ LEFT-MATCH. + Stream<MatchClause> matchClauseStream = fromGraphClause.getMatchClauses().stream(); + workingEnvironment.setInlineLegal(matchClauseStream.noneMatch(c -> c.getMatchType() == MatchType.LEFTOUTER)); + + // Lower our MATCH-CLAUSEs. We should be working with canonical-ized patterns. + for (MatchClause matchClause : fromGraphClause.getMatchClauses()) { + if (matchClause.getMatchType() == MatchType.LEFTOUTER) { + workingEnvironment.beginLeftMatch(); + } + for (PathPatternExpr pathPatternExpr : matchClause.getPathExpressions()) { + for (EdgePatternExpr edgePatternExpr : pathPatternExpr.getEdgeExpressions()) { + edgePatternExpr.accept(this, fromGraphClause); + } + for (VertexPatternExpr vertexPatternExpr : pathPatternExpr.getVertexExpressions()) { + VariableExpr vertexVariableExpr = vertexPatternExpr.getVariableExpr(); + if (aliasLookupTable.getIterationAlias(vertexVariableExpr) != null) { + continue; + } + workingEnvironment.acceptAction(actionFactory.buildDanglingVertexAction(vertexPatternExpr)); + } + workingEnvironment.acceptAction(actionFactory.buildPathPatternAction(pathPatternExpr)); + } + if (matchClause.getMatchType() == MatchType.LEFTOUTER) { + workingEnvironment.endLeftMatch(); + } + } + workingEnvironment.acceptAction(actionFactory.buildMatchSemanticAction(fromGraphClause)); + + // Finalize our lowering by moving our lower list to our environment. + workingEnvironment.endLowering(fromGraphClause); + + // Add our correlate clauses, if any, to our tail FROM-TERM. + if (!fromGraphClause.getCorrelateClauses().isEmpty()) { + LowerListClause lowerClause = (LowerListClause) fromGraphClause.getLowerClause(); + ClauseCollection clauseCollection = lowerClause.getClauseCollection(); + fromGraphClause.getCorrelateClauses().forEach(clauseCollection::addUserDefinedCorrelateClause); + } + return null; + } + + @Override + public Expression visit(EdgePatternExpr edgePatternExpr, ILangExpression arg) throws CompilationException { + EdgeDescriptor edgeDescriptor = edgePatternExpr.getEdgeDescriptor(); + if (edgeDescriptor.getPatternType() == EdgeDescriptor.PatternType.EDGE) { + lowerCanonicalExpandedEdge(edgePatternExpr, environmentStack.getLast()); + + } else { // edgeDescriptor.getPatternType() == EdgeDescriptor.PatternType.PATH + lowerCanonicalExpandedPath(edgePatternExpr, environmentStack.getLast()); + } + return edgePatternExpr; + } + + private void lowerCanonicalExpandedEdge(EdgePatternExpr edgePatternExpr, LoweringEnvironment environment) + throws CompilationException { + EdgeDescriptor edgeDescriptor = edgePatternExpr.getEdgeDescriptor(); + + // We should be working with a canonical edge. + GraphIdentifier graphIdentifier = environment.getGraphIdentifier(); + List<EdgeIdentifier> edgeElementIDs = edgePatternExpr.generateIdentifiers(graphIdentifier); + if (edgeElementIDs.size() != 1) { + throw new CompilationException(ErrorCode.COMPILATION_ILLEGAL_STATE, + "Encountered non-fixed-point edge pattern!"); + } + EdgeIdentifier edgeIdentifier = edgeElementIDs.get(0); + ElementBodyAnalysisContext edgeBodyAnalysisContext = analysisContextMap.get(edgeIdentifier); + DataverseName edgeDataverseName = edgeBodyAnalysisContext.getDataverseName(); + String edgeDatasetName = edgeBodyAnalysisContext.getDatasetName(); + + // Determine our source and destination vertices. + VertexPatternExpr sourceVertex, destVertex; + if (edgeDescriptor.getEdgeDirection() == EdgeDescriptor.EdgeDirection.LEFT_TO_RIGHT) { + sourceVertex = edgePatternExpr.getLeftVertex(); + destVertex = edgePatternExpr.getRightVertex(); + + } else { // edgeDescriptor.getEdgeDirection() == EdgeDescriptor.EdgeDirection.RIGHT_TO_LEFT + sourceVertex = edgePatternExpr.getRightVertex(); + destVertex = edgePatternExpr.getLeftVertex(); + } + + // Collect information about our source vertex. + VertexIdentifier sourceIdentifier = sourceVertex.generateIdentifiers(graphIdentifier).get(0); + ElementBodyAnalysisContext sourceBodyAnalysisContext = analysisContextMap.get(sourceIdentifier); + VariableExpr sourceVertexVariable = sourceVertex.getVariableExpr(); + List<List<String>> sourceVertexKey = elementLookupTable.getVertexKey(sourceIdentifier); + Function<EdgeIdentifier, List<List<String>>> sourceKey = elementLookupTable::getEdgeSourceKey; + boolean isSourceInline = sourceBodyAnalysisContext.isExpressionInline() && environment.isInlineLegal(); + boolean isSourceIntroduced = aliasLookupTable.getIterationAlias(sourceVertexVariable) != null; + + // ...and our destination vertex... + VertexIdentifier destIdentifier = destVertex.generateIdentifiers(graphIdentifier).get(0); + ElementBodyAnalysisContext destBodyAnalysisContext = analysisContextMap.get(destIdentifier); + VariableExpr destVertexVariable = destVertex.getVariableExpr(); + List<List<String>> destVertexKey = elementLookupTable.getVertexKey(destIdentifier); + Function<EdgeIdentifier, List<List<String>>> destKey = elementLookupTable::getEdgeDestKey; + boolean isDestInline = destBodyAnalysisContext.isExpressionInline() && environment.isInlineLegal(); + boolean isDestIntroduced = aliasLookupTable.getIterationAlias(destVertexVariable) != null; + + // ...and our edge. + List<List<String>> sourceEdgeKey = elementLookupTable.getEdgeSourceKey(edgeIdentifier); + List<List<String>> destEdgeKey = elementLookupTable.getEdgeDestKey(edgeIdentifier); + String sourceBodyDatasetName = sourceBodyAnalysisContext.getDatasetName(); + String destBodyDatasetName = destBodyAnalysisContext.getDatasetName(); + boolean isEdgeInline = edgeBodyAnalysisContext.isExpressionInline() && environment.isInlineLegal(); + boolean isSourceFolded = isSourceInline && sourceBodyDatasetName.equals(edgeDatasetName) + && sourceBodyAnalysisContext.getDataverseName().equals(edgeDataverseName) + && sourceVertexKey.equals(sourceEdgeKey); + boolean isDestFolded = isDestInline && destBodyDatasetName.equals(edgeDatasetName) + && destBodyAnalysisContext.getDataverseName().equals(edgeDataverseName) + && destVertexKey.equals(destEdgeKey); + + // Condition our strategy on which vertices are currently introduced. + if (isEdgeInline && isSourceFolded) { + if (!isSourceIntroduced) { + environment.acceptAction(actionFactory.buildDanglingVertexAction(sourceVertex)); + } + environment.acceptAction(actionFactory.buildFoldedEdgeAction(sourceVertex, edgePatternExpr)); + environment.acceptAction( + !isDestIntroduced ? actionFactory.buildBoundVertexAction(destVertex, edgePatternExpr, destKey) + : actionFactory.buildRawJoinVertexAction(destVertex, edgePatternExpr, destKey)); + + } else if (isEdgeInline && isDestFolded) { + if (!isDestIntroduced) { + environment.acceptAction(actionFactory.buildDanglingVertexAction(destVertex)); + } + environment.acceptAction(actionFactory.buildFoldedEdgeAction(destVertex, edgePatternExpr)); + environment.acceptAction( + !isSourceIntroduced ? actionFactory.buildBoundVertexAction(sourceVertex, edgePatternExpr, sourceKey) + : actionFactory.buildRawJoinVertexAction(sourceVertex, edgePatternExpr, sourceKey)); + + } else if (isSourceIntroduced && isDestIntroduced) { + environment.acceptAction(actionFactory.buildNonFoldedEdgeAction(sourceVertex, edgePatternExpr, sourceKey)); + environment.acceptAction(actionFactory.buildRawJoinVertexAction(destVertex, edgePatternExpr, destKey)); + + } else if (isSourceIntroduced) { // !isDestIntroduced + environment.acceptAction(actionFactory.buildNonFoldedEdgeAction(sourceVertex, edgePatternExpr, sourceKey)); + environment.acceptAction(actionFactory.buildBoundVertexAction(destVertex, edgePatternExpr, destKey)); + + } else if (isDestIntroduced) { // !isSourceIntroduced + environment.acceptAction(actionFactory.buildNonFoldedEdgeAction(destVertex, edgePatternExpr, destKey)); + environment.acceptAction(actionFactory.buildBoundVertexAction(sourceVertex, edgePatternExpr, sourceKey)); + + } else { // !isSourceIntroduced && !isDestIntroduced + // When nothing is introduced, start off from LEFT to RIGHT instead of considering our source and dest. + VertexPatternExpr leftVertex = edgePatternExpr.getLeftVertex(); + VertexPatternExpr rightVertex = edgePatternExpr.getRightVertex(); + Function<EdgeIdentifier, List<List<String>>> leftKey; + Function<EdgeIdentifier, List<List<String>>> rightKey; + if (edgeDescriptor.getEdgeDirection() == EdgeDescriptor.EdgeDirection.LEFT_TO_RIGHT) { + leftKey = sourceKey; + rightKey = destKey; + + } else { // edgeDescriptor.getEdgeDirection() == EdgeDescriptor.EdgeDirection.RIGHT_TO_LEFT + leftKey = destKey; + rightKey = sourceKey; + } + environment.acceptAction(actionFactory.buildDanglingVertexAction(leftVertex)); + environment.acceptAction(actionFactory.buildNonFoldedEdgeAction(leftVertex, edgePatternExpr, leftKey)); + environment.acceptAction(actionFactory.buildBoundVertexAction(rightVertex, edgePatternExpr, rightKey)); + } + } + + private void lowerCanonicalExpandedPath(EdgePatternExpr edgePatternExpr, LoweringEnvironment environment) + throws CompilationException { + // Determine the starting vertex of our path. + VariableExpr leftVertexVariable = edgePatternExpr.getLeftVertex().getVariableExpr(); + VariableExpr rightVertexVariable = edgePatternExpr.getRightVertex().getVariableExpr(); + boolean isLeftVertexIntroduced = aliasLookupTable.getIterationAlias(leftVertexVariable) != null; + boolean isRightVertexIntroduced = aliasLookupTable.getIterationAlias(rightVertexVariable) != null; + boolean isJoiningLeftToRight = isLeftVertexIntroduced || !isRightVertexIntroduced; + VertexPatternExpr inputVertex, outputVertex; + if (isJoiningLeftToRight) { + inputVertex = edgePatternExpr.getLeftVertex(); + outputVertex = edgePatternExpr.getRightVertex(); + + } else { + inputVertex = edgePatternExpr.getRightVertex(); + outputVertex = edgePatternExpr.getLeftVertex(); + } + VariableExpr inputVertexVariable = inputVertex.getVariableExpr(); + VariableExpr outputVertexVariable = outputVertex.getVariableExpr(); + + // If we need to, introduce our left vertex (only occurs if nothing has been introduced). + if (!isLeftVertexIntroduced && !isRightVertexIntroduced) { + environment.acceptAction(actionFactory.buildDanglingVertexAction(edgePatternExpr.getLeftVertex())); + } + + // Our input vertex must be introduced eagerly to be given to our graph clause as input. + VariableExpr inputClauseVariable = graphixRewritingContext.getGraphixVariableCopy(inputVertexVariable); + environment.acceptTransformer(lowerList -> { + // Find the representative vertex associated with our input. + Expression representativeVertexExpr = null; + for (LetClause vertexBinding : lowerList.getRepresentativeVertexBindings()) { + if (vertexBinding.getVarExpr().equals(inputVertex.getVariableExpr())) { + representativeVertexExpr = vertexBinding.getBindingExpr(); + break; + } + } + if (representativeVertexExpr == null) { + throw new CompilationException(ErrorCode.COMPILATION_ILLEGAL_STATE, "Input vertex not found!"); + } + + // Once all variables of our representative LET-CLAUSE have been introduced, introduce our input binding. + Set<VariableExpr> usedVariables = SqlppVariableUtil.getFreeVariables(representativeVertexExpr); + ListIterator<AbstractClause> nonRepresentativeClauseIterator = + lowerList.getNonRepresentativeClauses().listIterator(); + while (nonRepresentativeClauseIterator.hasNext()) { + AbstractClause workingClause = nonRepresentativeClauseIterator.next(); + List<VariableExpr> bindingVariables = SqlppVariableUtil.getBindingVariables(workingClause); + bindingVariables.forEach(usedVariables::remove); + if (usedVariables.isEmpty()) { + VariableExpr inputClauseVariableCopy = graphixDeepCopyVisitor.visit(inputClauseVariable, null); + LetClause clauseInputBinding = new LetClause(inputClauseVariableCopy, representativeVertexExpr); + nonRepresentativeClauseIterator.add(clauseInputBinding); + lowerList.addEagerVertexBinding(inputVertex.getVariableExpr(), clauseInputBinding); + break; + } + } + }); + + // Lower each of our branches. + environment.beginBranches(); + Map<ElementLabel, VariableExpr> labelInputVariableMap = new HashMap<>(); + Map<ElementLabel, VariableExpr> labelOutputVariableMap = new HashMap<>(); + for (EdgePatternExpr branch : branchLookupTable.getBranches(edgePatternExpr)) { + VertexPatternExpr inputBranchVertex, outputBranchVertex; + if (isJoiningLeftToRight) { + inputBranchVertex = branch.getLeftVertex(); + outputBranchVertex = branch.getRightVertex(); + + } else { + inputBranchVertex = branch.getRightVertex(); + outputBranchVertex = branch.getLeftVertex(); + } + + // Introduce the alias-- but do not lower our source vertex. + environment.beginTempLowerList(); + environment.acceptAction(actionFactory.buildDanglingVertexAction(inputBranchVertex)); + environment.endTempLowerList(); + synchronizeVariables(environment, inputBranchVertex, labelInputVariableMap); + inputBranchVertex.addHint(LoweringExemptAnnotation.INSTANCE); + + // Lower our branch, having introduced our source vertex. + lowerCanonicalExpandedEdge(branch, environment); + synchronizeVariables(environment, outputBranchVertex, labelOutputVariableMap); + environment.flushBranch(branch, isJoiningLeftToRight); + } + + // Our output vertex will be aliased by the following: + VariableExpr outputVertexIterationAlias = graphixRewritingContext.getGraphixVariableCopy(outputVertexVariable); + VariableExpr outputVertexJoinAlias = graphixRewritingContext.getGraphixVariableCopy(outputVertexVariable); + aliasLookupTable.addIterationAlias(outputVertexVariable, outputVertexIterationAlias); + aliasLookupTable.addJoinAlias(outputVertexVariable, outputVertexJoinAlias); + + // Introduce a SWITCH node, finalizing our path lowering. + VariableExpr pathVariable = edgePatternExpr.getEdgeDescriptor().getVariableExpr(); + VariableExpr outputClauseVariable = graphixRewritingContext.getGraphixVariableCopy(outputVertexVariable); + ClauseOutputEnvironment outputEnvironment = new ClauseOutputEnvironment(outputClauseVariable, + aliasLookupTable.getIterationAlias(outputVertexVariable), + aliasLookupTable.getJoinAlias(outputVertexVariable), pathVariable, + outputVertex.getLabels().iterator().next()); + ClauseInputEnvironment inputEnvironment = new ClauseInputEnvironment( + graphixDeepCopyVisitor.visit(inputClauseVariable, null), inputVertex.getLabels().iterator().next()); + environment.endBranches(outputEnvironment, inputEnvironment, labelInputVariableMap, labelOutputVariableMap, + aliasLookupTable, edgePatternExpr.getSourceLocation()); + + // Expose our output vertex and path to the remainder of our query. + environment.acceptTransformer(lowerList -> { + VariableExpr iterationVariableCopy1 = graphixDeepCopyVisitor.visit(outputVertexIterationAlias, null); + VariableExpr iterationVariableCopy2 = graphixDeepCopyVisitor.visit(outputVertexIterationAlias, null); + VariableExpr joinVariableCopy = graphixDeepCopyVisitor.visit(outputVertexJoinAlias, null); + Expression iterationVariableAccess = outputEnvironment.buildIterationVariableAccess(); + Expression joinVariableAccess = outputEnvironment.buildJoinVariableAccess(); + lowerList.addNonRepresentativeClause(new LetClause(iterationVariableCopy1, iterationVariableAccess)); + lowerList.addNonRepresentativeClause(new LetClause(joinVariableCopy, joinVariableAccess)); + lowerList.addVertexBinding(outputVertexVariable, iterationVariableCopy2); + lowerList.addPathBinding(pathVariable, outputEnvironment.buildPathVariableAccess()); + }); + } + + private void synchronizeVariables(LoweringEnvironment workingEnvironment, VertexPatternExpr branchVertex, + Map<ElementLabel, VariableExpr> labelVariableMap) throws CompilationException { + VariableExpr vertexVariable = branchVertex.getVariableExpr(); + VariableExpr iterationAlias = aliasLookupTable.getIterationAlias(vertexVariable); + VariableExpr joinAlias = aliasLookupTable.getJoinAlias(vertexVariable); + ElementLabel elementLabel = branchVertex.getLabels().iterator().next(); + if (labelVariableMap.containsKey(elementLabel)) { + VariableExpr targetVertexVariable = labelVariableMap.get(elementLabel); + VariableExpr targetIterationAlias = aliasLookupTable.getIterationAlias(targetVertexVariable); + VariableExpr targetJoinAlias = aliasLookupTable.getJoinAlias(targetVertexVariable); + variableRemapCloneVisitor.resetSubstitutions(); + variableRemapCloneVisitor.addSubstitution(iterationAlias, targetIterationAlias); + variableRemapCloneVisitor.addSubstitution(joinAlias, targetJoinAlias); + variableRemapCloneVisitor.addSubstitution(vertexVariable, targetVertexVariable); + + workingEnvironment.acceptTransformer(lowerList -> { + // Transform our non-representative clauses. + ListIterator<AbstractClause> nonRepresentativeIterator = + lowerList.getNonRepresentativeClauses().listIterator(); + while (nonRepresentativeIterator.hasNext()) { + AbstractClause workingClause = nonRepresentativeIterator.next(); + nonRepresentativeIterator.set((AbstractClause) variableRemapCloneVisitor.substitute(workingClause)); + } + + // Transform our vertex bindings. + ListIterator<LetClause> vertexBindingIterator = + lowerList.getRepresentativeVertexBindings().listIterator(); + while (vertexBindingIterator.hasNext()) { + LetClause vertexBinding = vertexBindingIterator.next(); + vertexBindingIterator.set((LetClause) variableRemapCloneVisitor.substitute(vertexBinding)); + } + + // Transform our edge bindings. + ListIterator<LetClause> edgeBindingIterator = lowerList.getRepresentativeEdgeBindings().listIterator(); + while (edgeBindingIterator.hasNext()) { + LetClause edgeBinding = edgeBindingIterator.next(); + edgeBindingIterator.set((LetClause) variableRemapCloneVisitor.substitute(edgeBinding)); + } + }); + branchVertex.setVariableExpr(targetVertexVariable); + + } else { + labelVariableMap.put(elementLabel, vertexVariable); + } + } +}
diff --git a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrite/visitor/PatternGraphGroupVisitor.java b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrite/visitor/PatternGraphGroupVisitor.java new file mode 100644 index 0000000..98842d1 --- /dev/null +++ b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrite/visitor/PatternGraphGroupVisitor.java
@@ -0,0 +1,87 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.asterix.graphix.lang.rewrite.visitor; + +import java.util.ArrayDeque; +import java.util.Deque; +import java.util.Map; + +import org.apache.asterix.common.exceptions.CompilationException; +import org.apache.asterix.graphix.common.metadata.GraphIdentifier; +import org.apache.asterix.graphix.lang.clause.FromGraphClause; +import org.apache.asterix.graphix.lang.clause.MatchClause; +import org.apache.asterix.graphix.lang.expression.EdgePatternExpr; +import org.apache.asterix.graphix.lang.expression.VertexPatternExpr; +import org.apache.asterix.graphix.lang.rewrite.GraphixRewritingContext; +import org.apache.asterix.graphix.lang.struct.PatternGroup; +import org.apache.asterix.graphix.lang.visitor.base.AbstractGraphixQueryVisitor; +import org.apache.asterix.lang.common.base.Expression; +import org.apache.asterix.lang.common.base.ILangExpression; +import org.apache.asterix.lang.sqlpp.clause.AbstractBinaryCorrelateClause; +import org.apache.asterix.metadata.declared.MetadataProvider; + +/** + * Aggregate all query patterns, grouped by the graph being queried. + * + * @see PatternGroup + */ +public class PatternGraphGroupVisitor extends AbstractGraphixQueryVisitor { + private final Map<GraphIdentifier, PatternGroup> patternGroupMap; + private final Deque<GraphIdentifier> graphIdentifierStack; + protected final MetadataProvider metadataProvider; + + public PatternGraphGroupVisitor(Map<GraphIdentifier, PatternGroup> patternGroupMap, + GraphixRewritingContext graphixRewritingContext) { + this.metadataProvider = graphixRewritingContext.getMetadataProvider(); + this.graphIdentifierStack = new ArrayDeque<>(); + this.patternGroupMap = patternGroupMap; + } + + @Override + public Expression visit(FromGraphClause fromGraphClause, ILangExpression arg) throws CompilationException { + // Collect the vertices and edges in this FROM-GRAPH-CLAUSE. + graphIdentifierStack.push(fromGraphClause.getGraphIdentifier(metadataProvider)); + for (MatchClause matchClause : fromGraphClause.getMatchClauses()) { + matchClause.accept(this, arg); + } + for (AbstractBinaryCorrelateClause correlateClause : fromGraphClause.getCorrelateClauses()) { + correlateClause.accept(this, arg); + } + graphIdentifierStack.pop(); + return null; + } + + @Override + public Expression visit(VertexPatternExpr vertexPatternExpr, ILangExpression arg) throws CompilationException { + patternGroupMap.putIfAbsent(graphIdentifierStack.peek(), new PatternGroup()); + patternGroupMap.get(graphIdentifierStack.peek()).getVertexPatternSet().add(vertexPatternExpr); + return super.visit(vertexPatternExpr, arg); + } + + // We do not visit our internal vertices here. + @Override + public Expression visit(EdgePatternExpr edgePatternExpr, ILangExpression arg) throws CompilationException { + patternGroupMap.putIfAbsent(graphIdentifierStack.peek(), new PatternGroup()); + patternGroupMap.get(graphIdentifierStack.peek()).getEdgePatternSet().add(edgePatternExpr); + if (edgePatternExpr.getEdgeDescriptor().getFilterExpr() != null) { + edgePatternExpr.getEdgeDescriptor().getFilterExpr().accept(this, arg); + } + return edgePatternExpr; + } +}
diff --git a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrite/visitor/PopulateUnknownsVisitor.java b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrite/visitor/PopulateUnknownsVisitor.java new file mode 100644 index 0000000..37f341f --- /dev/null +++ b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrite/visitor/PopulateUnknownsVisitor.java
@@ -0,0 +1,148 @@ +/* + * 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.graphix.lang.rewrite.visitor; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.function.Supplier; + +import org.apache.asterix.common.exceptions.CompilationException; +import org.apache.asterix.graphix.lang.clause.FromGraphClause; +import org.apache.asterix.graphix.lang.clause.MatchClause; +import org.apache.asterix.graphix.lang.expression.EdgePatternExpr; +import org.apache.asterix.graphix.lang.expression.PathPatternExpr; +import org.apache.asterix.graphix.lang.expression.VertexPatternExpr; +import org.apache.asterix.graphix.lang.rewrite.GraphixRewritingContext; +import org.apache.asterix.graphix.lang.struct.EdgeDescriptor; +import org.apache.asterix.graphix.lang.visitor.base.AbstractGraphixQueryVisitor; +import org.apache.asterix.lang.common.base.AbstractClause; +import org.apache.asterix.lang.common.base.Clause; +import org.apache.asterix.lang.common.base.Expression; +import org.apache.asterix.lang.common.base.ILangExpression; +import org.apache.asterix.lang.common.clause.LetClause; +import org.apache.asterix.lang.common.expression.VariableExpr; +import org.apache.asterix.lang.common.struct.Identifier; +import org.apache.asterix.lang.sqlpp.clause.AbstractBinaryCorrelateClause; +import org.apache.asterix.lang.sqlpp.clause.FromClause; +import org.apache.asterix.lang.sqlpp.clause.FromTerm; +import org.apache.asterix.lang.sqlpp.clause.SelectBlock; +import org.apache.asterix.lang.sqlpp.expression.SelectExpression; +import org.apache.asterix.lang.sqlpp.rewrites.visitor.GenerateColumnNameVisitor; +import org.apache.asterix.lang.sqlpp.util.SqlppVariableUtil; +import org.apache.hyracks.algebricks.common.utils.Pair; + +/** + * A pre-Graphix transformation pass to populate a number of unknowns in our Graphix AST. + * <ol> + * <li>Populate all unknown graph elements (vertices and edges).</li> + * <li>Populate all unknown column names in SELECT-CLAUSEs.</li> + * <li>Populate all unknown GROUP-BY keys.</li> + * <li>Fill in all GROUP-BY fields.</li> + * </ol> + */ +public class PopulateUnknownsVisitor extends AbstractGraphixQueryVisitor { + private final GenerateColumnNameVisitor generateColumnNameVisitor; + private final Supplier<VariableExpr> newVariableSupplier; + + public PopulateUnknownsVisitor(GraphixRewritingContext graphixRewritingContext) { + generateColumnNameVisitor = new GenerateColumnNameVisitor(graphixRewritingContext); + newVariableSupplier = () -> new VariableExpr(graphixRewritingContext.newVariable()); + } + + @Override + public Expression visit(SelectExpression selectExpression, ILangExpression arg) throws CompilationException { + selectExpression.accept(generateColumnNameVisitor, arg); + return super.visit(selectExpression, arg); + } + + @Override + public Expression visit(SelectBlock selectBlock, ILangExpression arg) throws CompilationException { + super.visit(selectBlock, arg); + + if (selectBlock.hasGroupbyClause()) { + // Collect all variables that should belong in the GROUP-BY field list. + Set<VariableExpr> userLiveVariables = new HashSet<>(); + if (selectBlock.hasFromClause() && selectBlock.getFromClause() instanceof FromGraphClause) { + FromGraphClause fromGraphClause = (FromGraphClause) selectBlock.getFromClause(); + for (MatchClause matchClause : fromGraphClause.getMatchClauses()) { + for (PathPatternExpr pathExpression : matchClause.getPathExpressions()) { + if (pathExpression.getVariableExpr() != null) { + userLiveVariables.add(pathExpression.getVariableExpr()); + } + for (VertexPatternExpr vertexExpression : pathExpression.getVertexExpressions()) { + userLiveVariables.add(vertexExpression.getVariableExpr()); + } + for (EdgePatternExpr edgeExpression : pathExpression.getEdgeExpressions()) { + userLiveVariables.add(edgeExpression.getEdgeDescriptor().getVariableExpr()); + } + } + } + if (!fromGraphClause.getCorrelateClauses().isEmpty()) { + List<AbstractBinaryCorrelateClause> correlateClauses = fromGraphClause.getCorrelateClauses(); + for (AbstractBinaryCorrelateClause correlateClause : correlateClauses) { + userLiveVariables.add(correlateClause.getRightVariable()); + } + } + + } else if (selectBlock.hasFromClause()) { + FromClause fromClause = selectBlock.getFromClause(); + for (FromTerm fromTerm : fromClause.getFromTerms()) { + userLiveVariables.add(fromTerm.getLeftVariable()); + for (AbstractBinaryCorrelateClause correlateClause : fromTerm.getCorrelateClauses()) { + userLiveVariables.add(correlateClause.getRightVariable()); + } + } + } + if (selectBlock.hasLetWhereClauses()) { + for (AbstractClause abstractClause : selectBlock.getLetWhereList()) { + if (abstractClause.getClauseType() == Clause.ClauseType.LET_CLAUSE) { + LetClause letClause = (LetClause) abstractClause; + userLiveVariables.add(letClause.getVarExpr()); + } + } + } + + // Add the live variables to our GROUP-BY field list. + List<Pair<Expression, Identifier>> newGroupFieldList = new ArrayList<>(); + for (VariableExpr userLiveVariable : userLiveVariables) { + String variableName = SqlppVariableUtil.toUserDefinedName(userLiveVariable.getVar().getValue()); + newGroupFieldList.add(new Pair<>(userLiveVariable, new Identifier(variableName))); + } + selectBlock.getGroupbyClause().setGroupFieldList(newGroupFieldList); + } + return null; + } + + @Override + public Expression visit(VertexPatternExpr vertexExpression, ILangExpression arg) throws CompilationException { + if (vertexExpression.getVariableExpr() == null) { + vertexExpression.setVariableExpr(newVariableSupplier.get()); + } + return super.visit(vertexExpression, arg); + } + + @Override + public Expression visit(EdgePatternExpr edgeExpression, ILangExpression arg) throws CompilationException { + EdgeDescriptor edgeDescriptor = edgeExpression.getEdgeDescriptor(); + if (edgeDescriptor.getVariableExpr() == null) { + edgeDescriptor.setVariableExpr(newVariableSupplier.get()); + } + return super.visit(edgeExpression, arg); + } +}
diff --git a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/visitor/PostCanonicalizationVisitor.java b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrite/visitor/PostCanonicalExpansionVisitor.java similarity index 67% rename from asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/visitor/PostCanonicalizationVisitor.java rename to asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrite/visitor/PostCanonicalExpansionVisitor.java index bf6cb97..1f0d85b 100644 --- a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/visitor/PostCanonicalizationVisitor.java +++ b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrite/visitor/PostCanonicalExpansionVisitor.java
@@ -16,15 +16,15 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.asterix.graphix.lang.rewrites.visitor; +package org.apache.asterix.graphix.lang.rewrite.visitor; import java.util.ArrayList; +import java.util.Collection; import java.util.List; -import java.util.Set; import org.apache.asterix.common.exceptions.CompilationException; -import org.apache.asterix.graphix.lang.clause.GraphSelectBlock; -import org.apache.asterix.graphix.lang.rewrites.GraphixRewritingContext; +import org.apache.asterix.graphix.lang.rewrite.GraphixRewritingContext; +import org.apache.asterix.graphix.lang.visitor.base.AbstractGraphixQueryVisitor; import org.apache.asterix.lang.common.base.AbstractClause; import org.apache.asterix.lang.common.base.Expression; import org.apache.asterix.lang.common.base.ILangExpression; @@ -50,42 +50,48 @@ /** * Rewrite a SELECT-EXPR and its SET-OP inputs to perform the following: - * 1. Expose all user-defined variables from each SET-OP by modifying their SELECT-CLAUSE. - * 2. Qualify the source SELECT-CLAUSE (before expansion) with the nesting variable. - * 3. Qualify our output modifiers (ORDER-BY, LIMIT) with the nesting variable. - * 4. Qualify our GROUP-BY / GROUP-AS (the grouping list) / HAVING / LET (after GROUP-BY) clauses with the nesting - * variable. + * <ol> + * <li>Expose all user-defined variables from each SET-OP by modifying their SELECT-CLAUSE.</li> + * <li>Qualify the source SELECT-CLAUSE (before expansion) with the nesting variable.</li> + * <li>Qualify our output modifiers (ORDER-BY, LIMIT) with the nesting variable.</li> + * <li>Qualify our GROUP-BY / GROUP-AS (the grouping list) / HAVING / LET (after GROUP-BY) clauses with the nesting + * variable.</li> + * </ol> */ -public class PostCanonicalizationVisitor extends AbstractGraphixQueryVisitor { +public class PostCanonicalExpansionVisitor extends AbstractGraphixQueryVisitor { + private final GraphixDeepCopyVisitor deepCopyVisitor; private final GraphixRewritingContext graphixRewritingContext; private final QualifyingVisitor qualifyingVisitor; // We require the following from our canonicalization pass. - private final Set<SetOperationInput> generatedSetOpInputs; - private final GraphSelectBlock selectBlockExpansionSource; - private final List<VarIdentifier> userLiveVariables; + private final Collection<SelectBlock> generatedSelectBlocks; + private final Collection<VariableExpr> sourceSelectLiveVariables; + private final SelectBlock selectBlockExpansionSource; - public PostCanonicalizationVisitor(GraphixRewritingContext graphixRewritingContext, - GraphSelectBlock selectBlockExpansionSource, Set<SetOperationInput> generatedSetOpInputs, - List<VarIdentifier> userLiveVariables) { + public PostCanonicalExpansionVisitor(GraphixRewritingContext graphixRewritingContext, + SelectBlock selectBlockExpansionSource, Collection<SelectBlock> generatedSelectBlocks, + Collection<VariableExpr> sourceSelectLiveVariables) { + this.deepCopyVisitor = new GraphixDeepCopyVisitor(); this.graphixRewritingContext = graphixRewritingContext; this.selectBlockExpansionSource = selectBlockExpansionSource; - this.generatedSetOpInputs = generatedSetOpInputs; - this.userLiveVariables = userLiveVariables; + this.generatedSelectBlocks = generatedSelectBlocks; + this.sourceSelectLiveVariables = sourceSelectLiveVariables; this.qualifyingVisitor = new QualifyingVisitor(); } @Override public Expression visit(SelectExpression selectExpression, ILangExpression arg) throws CompilationException { - VariableExpr iterationVariableExpr = new VariableExpr(graphixRewritingContext.getNewGraphixVariable()); + VariableExpr iterationVariable = graphixRewritingContext.getGraphixVariableCopy("_Containing"); // Modify the involved SELECT-CLAUSEs to output our user-live variables and remove any GROUP-BY clauses. selectExpression.getSelectSetOperation().accept(this, arg); - FromTerm fromTerm = new FromTerm(selectExpression, iterationVariableExpr, null, null); + FromTerm fromTerm = new FromTerm(selectExpression, iterationVariable, null, null); FromClause fromClause = new FromClause(List.of(fromTerm)); + fromTerm.setSourceLocation(selectExpression.getSourceLocation()); + fromClause.setSourceLocation(selectExpression.getSourceLocation()); // Qualify the SELECT-CLAUSE given to us by our caller. - qualifyingVisitor.qualifyingVar = iterationVariableExpr.getVar(); + qualifyingVisitor.qualifyingVar = deepCopyVisitor.visit(iterationVariable, null); SelectClause selectClause = selectBlockExpansionSource.getSelectClause(); selectClause.accept(qualifyingVisitor, null); @@ -117,6 +123,8 @@ Expression newExpression = expressionIdentifierPair.first.accept(qualifyingVisitor, null); newGroupFieldList.add(new Pair<>(newExpression, expressionIdentifierPair.second)); } + VariableExpr iterationVariableCopy = deepCopyVisitor.visit(iterationVariable, null); + newGroupFieldList.add(new Pair<>(iterationVariableCopy, iterationVariable.getVar())); groupbyClause.setGroupFieldList(newGroupFieldList); } if (selectBlockExpansionSource.hasLetHavingClausesAfterGroupby()) { @@ -128,21 +136,26 @@ // Finalize our post-canonicalization: attach our SELECT-CLAUSE, GROUP-BY, output modifiers... SelectBlock selectBlock = new SelectBlock(selectClause, fromClause, null, groupbyClause, null); selectBlock.getLetHavingListAfterGroupby().addAll(letHavingClausesAfterGby); + selectBlock.setSourceLocation(selectBlockExpansionSource.getSourceLocation()); SetOperationInput setOperationInput = new SetOperationInput(selectBlock, null); SelectSetOperation selectSetOperation = new SelectSetOperation(setOperationInput, null); - return new SelectExpression(null, selectSetOperation, orderByClause, limitClause, isSubquery); + selectSetOperation.setSourceLocation(selectBlockExpansionSource.getSourceLocation()); + SelectExpression newSelectExpression = + new SelectExpression(null, selectSetOperation, orderByClause, limitClause, isSubquery); + newSelectExpression.setSourceLocation(selectExpression.getSourceLocation()); + return newSelectExpression; } @Override public Expression visit(SelectSetOperation selectSetOperation, ILangExpression arg) throws CompilationException { // Only visit SET-OP-INPUTs if they were involved in our canonicalization. SetOperationInput leftInput = selectSetOperation.getLeftInput(); - if (generatedSetOpInputs.contains(leftInput)) { + if (leftInput.selectBlock() && generatedSelectBlocks.contains(leftInput.getSelectBlock())) { leftInput.getSelectBlock().accept(this, arg); } for (SetOperationRight setOperationRight : selectSetOperation.getRightInputs()) { SetOperationInput rightInput = setOperationRight.getSetOperationRightInput(); - if (generatedSetOpInputs.contains(rightInput)) { + if (rightInput.selectBlock() && generatedSelectBlocks.contains(rightInput.getSelectBlock())) { rightInput.getSelectBlock().accept(this, arg); } } @@ -150,11 +163,6 @@ } @Override - public Expression visit(GraphSelectBlock graphSelectBlock, ILangExpression arg) throws CompilationException { - return visit((SelectBlock) graphSelectBlock, arg); - } - - @Override public Expression visit(SelectBlock selectBlock, ILangExpression arg) throws CompilationException { if (selectBlock.hasGroupbyClause()) { selectBlock.setGroupbyClause(null); @@ -170,10 +178,10 @@ public Expression visit(SelectClause selectClause, ILangExpression arg) throws CompilationException { // We are going to throw away this SELECT-CLAUSE and return all user-live variables instead. List<Projection> newProjectionList = new ArrayList<>(); - for (VarIdentifier userLiveVariable : userLiveVariables) { - String name = SqlppVariableUtil.toUserDefinedName(userLiveVariable.getValue()); - VariableExpr newVariableExpr = new VariableExpr(userLiveVariable); - newProjectionList.add(new Projection(Projection.Kind.NAMED_EXPR, newVariableExpr, name)); + for (VariableExpr userLiveVariable : sourceSelectLiveVariables) { + String name = SqlppVariableUtil.toUserDefinedName(userLiveVariable.getVar().getValue()); + VariableExpr userLiveVariableCopy = deepCopyVisitor.visit(userLiveVariable, null); + newProjectionList.add(new Projection(Projection.Kind.NAMED_EXPR, userLiveVariableCopy, name)); } selectClause.setSelectElement(null); selectClause.setSelectRegular(new SelectRegular(newProjectionList)); @@ -181,13 +189,16 @@ } private class QualifyingVisitor extends AbstractGraphixQueryVisitor { - private VarIdentifier qualifyingVar; + private VariableExpr qualifyingVar; @Override public Expression visit(VariableExpr variableExpr, ILangExpression arg) throws CompilationException { - if (userLiveVariables.contains(variableExpr.getVar())) { + if (sourceSelectLiveVariables.contains(variableExpr)) { VarIdentifier fieldAccessVar = SqlppVariableUtil.toUserDefinedVariableName(variableExpr.getVar()); - return new FieldAccessor(new VariableExpr(qualifyingVar), fieldAccessVar); + VariableExpr qualifyingVariableCopy = deepCopyVisitor.visit(qualifyingVar, null); + FieldAccessor fieldAccessor = new FieldAccessor(qualifyingVariableCopy, fieldAccessVar); + fieldAccessor.setSourceLocation(variableExpr.getSourceLocation()); + return fieldAccessor; } return super.visit(variableExpr, arg); } @@ -195,17 +206,22 @@ @Override public Expression visit(FieldAccessor fieldAccessor, ILangExpression arg) throws CompilationException { Expression fieldAccessorExpr = fieldAccessor.getExpr(); + Identifier fieldAccessIdent = fieldAccessor.getIdent(); if (fieldAccessorExpr.getKind() == Expression.Kind.FIELD_ACCESSOR_EXPRESSION) { FieldAccessor innerFieldAccessExpr = (FieldAccessor) fieldAccessorExpr.accept(this, arg); - return new FieldAccessor(innerFieldAccessExpr, fieldAccessor.getIdent()); + FieldAccessor outerFieldAccessExpr = new FieldAccessor(innerFieldAccessExpr, fieldAccessIdent); + outerFieldAccessExpr.setSourceLocation(fieldAccessor.getSourceLocation()); + return outerFieldAccessExpr; } else if (fieldAccessorExpr.getKind() == Expression.Kind.VARIABLE_EXPRESSION) { VariableExpr fieldAccessVarExpr = (VariableExpr) fieldAccessorExpr; VarIdentifier fieldAccessVar = SqlppVariableUtil.toUserDefinedVariableName(fieldAccessVarExpr.getVar()); - if (userLiveVariables.contains(fieldAccessVarExpr.getVar())) { - VariableExpr qualifyingVarExpr = new VariableExpr(qualifyingVar); - FieldAccessor innerFieldAccessExpr = new FieldAccessor(qualifyingVarExpr, fieldAccessVar); - return new FieldAccessor(innerFieldAccessExpr, fieldAccessor.getIdent()); + if (sourceSelectLiveVariables.contains(fieldAccessVarExpr)) { + VariableExpr qualifyingVariableCopy = deepCopyVisitor.visit(qualifyingVar, null); + FieldAccessor innerFieldAccessExpr = new FieldAccessor(qualifyingVariableCopy, fieldAccessVar); + FieldAccessor outerFieldAccessExpr = new FieldAccessor(innerFieldAccessExpr, fieldAccessIdent); + outerFieldAccessExpr.setSourceLocation(fieldAccessor.getSourceLocation()); + return outerFieldAccessExpr; } } return super.visit(fieldAccessor, arg);
diff --git a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/visitor/PostRewriteCheckVisitor.java b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrite/visitor/PostRewriteCheckVisitor.java similarity index 89% rename from asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/visitor/PostRewriteCheckVisitor.java rename to asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrite/visitor/PostRewriteCheckVisitor.java index a1ae83d..bc102aa 100644 --- a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/visitor/PostRewriteCheckVisitor.java +++ b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrite/visitor/PostRewriteCheckVisitor.java
@@ -16,12 +16,11 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.asterix.graphix.lang.rewrites.visitor; +package org.apache.asterix.graphix.lang.rewrite.visitor; import org.apache.asterix.common.exceptions.CompilationException; import org.apache.asterix.common.exceptions.ErrorCode; import org.apache.asterix.graphix.lang.clause.FromGraphClause; -import org.apache.asterix.graphix.lang.clause.GraphSelectBlock; import org.apache.asterix.graphix.lang.clause.MatchClause; import org.apache.asterix.graphix.lang.expression.EdgePatternExpr; import org.apache.asterix.graphix.lang.expression.GraphConstructor; @@ -31,9 +30,11 @@ import org.apache.asterix.graphix.lang.statement.DeclareGraphStatement; import org.apache.asterix.graphix.lang.statement.GraphDropStatement; import org.apache.asterix.graphix.lang.statement.GraphElementDeclaration; +import org.apache.asterix.graphix.lang.visitor.base.IGraphixLangVisitor; import org.apache.asterix.lang.common.base.Expression; import org.apache.asterix.lang.common.base.ILangExpression; import org.apache.asterix.lang.common.clause.LetClause; +import org.apache.asterix.lang.sqlpp.clause.FromClause; import org.apache.asterix.lang.sqlpp.clause.FromTerm; import org.apache.asterix.lang.sqlpp.clause.JoinClause; import org.apache.asterix.lang.sqlpp.clause.NestClause; @@ -42,7 +43,7 @@ import org.apache.asterix.lang.sqlpp.visitor.base.AbstractSqlppSimpleExpressionVisitor; /** - * Throw an error if a graph AST node (that isn't a special instance of {@link FromGraphClause}) is ever encountered. + * Throw an error if we encounter a Graphix AST node that isn't a {@link FromGraphClause} that has been lowered. */ public class PostRewriteCheckVisitor extends AbstractSqlppSimpleExpressionVisitor implements IGraphixLangVisitor<Expression, ILangExpression> { @@ -82,20 +83,23 @@ } @Override - public Expression visit(GraphSelectBlock gsb, ILangExpression arg) throws CompilationException { - // The only Graph AST node that should survive is the GRAPH-SELECT-BLOCK, which should functionally act the same - // as its parent class SELECT-BLOCK. - if (!gsb.hasFromClause() || gsb.hasFromGraphClause()) { - return throwException(gsb); + public Expression visit(FromClause fc, ILangExpression arg) throws CompilationException { + if (fc instanceof FromGraphClause) { + return visit((FromGraphClause) fc, arg); } else { - return null; + return super.visit(fc, arg); } } @Override public Expression visit(FromGraphClause fgc, ILangExpression arg) throws CompilationException { - return throwException(fgc); + if (fgc.getLowerClause() == null) { + return throwException(fgc); + + } else { + return null; + } } @Override
diff --git a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/visitor/PreRewriteCheckVisitor.java b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrite/visitor/PreRewriteCheckVisitor.java similarity index 68% rename from asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/visitor/PreRewriteCheckVisitor.java rename to asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrite/visitor/PreRewriteCheckVisitor.java index be82df7..b58463b 100644 --- a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/visitor/PreRewriteCheckVisitor.java +++ b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrite/visitor/PreRewriteCheckVisitor.java
@@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.asterix.graphix.lang.rewrites.visitor; +package org.apache.asterix.graphix.lang.rewrite.visitor; import static org.apache.asterix.graphix.extension.GraphixMetadataExtension.getGraph; @@ -34,29 +34,42 @@ import org.apache.asterix.graphix.lang.expression.EdgePatternExpr; import org.apache.asterix.graphix.lang.expression.GraphConstructor; import org.apache.asterix.graphix.lang.expression.VertexPatternExpr; -import org.apache.asterix.graphix.lang.rewrites.GraphixRewritingContext; +import org.apache.asterix.graphix.lang.rewrite.GraphixRewritingContext; import org.apache.asterix.graphix.lang.statement.DeclareGraphStatement; import org.apache.asterix.graphix.lang.struct.EdgeDescriptor; import org.apache.asterix.graphix.lang.struct.ElementLabel; +import org.apache.asterix.graphix.lang.visitor.base.AbstractGraphixQueryVisitor; import org.apache.asterix.graphix.metadata.entity.schema.Graph; import org.apache.asterix.graphix.metadata.entity.schema.Schema; import org.apache.asterix.graphix.metadata.entity.schema.Vertex; import org.apache.asterix.lang.common.base.Expression; import org.apache.asterix.lang.common.base.ILangExpression; +import org.apache.asterix.lang.common.expression.VariableExpr; import org.apache.asterix.lang.common.struct.Identifier; import org.apache.asterix.lang.sqlpp.clause.AbstractBinaryCorrelateClause; import org.apache.asterix.metadata.declared.MetadataProvider; import org.apache.hyracks.algebricks.common.exceptions.AlgebricksException; +import org.apache.hyracks.api.exceptions.SourceLocation; /** - * A pre-rewrite pass to validate our user query. We validate the following: - * 1. An edge variable is not defined more than once. (e.g. (u)-[e]-(v), (w)-[e]-(y)) - * 2. A vertex variable is not defined more than once with labels. (e.g. (u:User)-[e]-(v), (u:User)-[]-(:Review)) - * 3. An edge label exists in the context of the edge's {@link FromGraphClause}. - * 4. A vertex label exists in the context of the vertex's {@link FromGraphClause}. - * 5. The minimum hops and maximum hops of a sub-path is not equal to zero. - * 6. The maximum hops of a sub-path is greater than or equal to the minimum hops of the same sub-path. - * 7. An anonymous / declared graph passes the same validation that a named graph does. + * A pre-rewrite pass to validate / raise warnings about our user query. + * <p> + * We validate the following: + * <ul> + * <li>An edge variable is not defined more than once. (e.g. (u)-[e]-(v), (w)-[e]-(y))</li> + * <li>A vertex variable is not defined more than once with labels. (e.g. (u:User)-[e]-(v), (u:User)-[]-(:Review))</li> + * <li>An edge label exists in the context of the edge's {@link FromGraphClause}.</li> + * <li>A vertex label exists in the context of the vertex's {@link FromGraphClause}.</li> + * <li>The minimum hops and maximum hops of a sub-path is not equal to zero.</li> + * <li>The maximum hops of a sub-path is greater than or equal to the minimum hops of the same sub-path.</li> + * <li>An anonymous / declared graph passes the same validation that a named graph does.</li> + * <li>That variables in an element filter expression do not reference other previously defined graph-elements.</li> + * </ul> + * <p> + * We raise warnings about the following: + * <ul> + * <li>We encounter a disconnected pattern. TODO (GLENN): Implement this.</li> + * </ul> */ public class PreRewriteCheckVisitor extends AbstractGraphixQueryVisitor { private final Map<GraphIdentifier, DeclareGraphStatement> declaredGraphs; @@ -64,10 +77,11 @@ // Build new environments on each FROM-GRAPH-CLAUSE visit. private static class PreRewriteCheckEnvironment { - private final Set<ElementLabel> vertexLabels = new HashSet<>(); + private final Set<ElementLabel> elementLabels = new HashSet<>(); private final Set<ElementLabel> edgeLabels = new HashSet<>(); private final Set<Identifier> vertexVariablesWithLabels = new HashSet<>(); private final Set<Identifier> edgeVariables = new HashSet<>(); + private final Set<Identifier> allElementVariables = new HashSet<>(); } private final Map<ILangExpression, PreRewriteCheckEnvironment> environmentMap = new HashMap<>(); @@ -79,8 +93,7 @@ @Override public Expression visit(GraphConstructor graphConstructor, ILangExpression arg) throws CompilationException { - DataverseName dataverseName = metadataProvider.getDefaultDataverseName(); - GraphIdentifier graphIdentifier = new GraphIdentifier(dataverseName, graphConstructor.getInstanceID()); + GraphIdentifier graphIdentifier = ((FromGraphClause) arg).getGraphIdentifier(metadataProvider); Schema.Builder schemaBuilder = new Schema.Builder(graphIdentifier); // Perform the same validation we do for named graphs-- but don't build the schema object. @@ -136,7 +149,7 @@ Identifier graphName = fromGraphClause.getGraphName(); // First, see if we can fetch the graph constructor from our declared graphs. - GraphIdentifier graphIdentifier = new GraphIdentifier(dataverseName, graphName.getValue()); + GraphIdentifier graphIdentifier = fromGraphClause.getGraphIdentifier(metadataProvider); DeclareGraphStatement declaredGraph = declaredGraphs.get(graphIdentifier); if (declaredGraph != null) { graphConstructor = declaredGraph.getGraphConstructor(); @@ -152,10 +165,10 @@ } else { graphFromMetadata.getGraphSchema().getVertices().stream().map(Vertex::getLabel) - .forEach(environmentMap.get(fromGraphClause).vertexLabels::add); + .forEach(environmentMap.get(fromGraphClause).elementLabels::add); graphFromMetadata.getGraphSchema().getEdges().forEach(e -> { - environmentMap.get(fromGraphClause).vertexLabels.add(e.getSourceLabel()); - environmentMap.get(fromGraphClause).vertexLabels.add(e.getDestinationLabel()); + environmentMap.get(fromGraphClause).elementLabels.add(e.getSourceLabel()); + environmentMap.get(fromGraphClause).elementLabels.add(e.getDestinationLabel()); environmentMap.get(fromGraphClause).edgeLabels.add(e.getLabel()); }); } @@ -168,13 +181,13 @@ } if (graphConstructor != null) { graphConstructor.getVertexElements().stream().map(GraphConstructor.VertexConstructor::getLabel) - .forEach(environmentMap.get(fromGraphClause).vertexLabels::add); + .forEach(environmentMap.get(fromGraphClause).elementLabels::add); graphConstructor.getEdgeElements().forEach(e -> { - environmentMap.get(fromGraphClause).vertexLabels.add(e.getSourceLabel()); - environmentMap.get(fromGraphClause).vertexLabels.add(e.getDestinationLabel()); + environmentMap.get(fromGraphClause).elementLabels.add(e.getSourceLabel()); + environmentMap.get(fromGraphClause).elementLabels.add(e.getDestinationLabel()); environmentMap.get(fromGraphClause).edgeLabels.add(e.getEdgeLabel()); }); - graphConstructor.accept(this, arg); + graphConstructor.accept(this, fromGraphClause); } // We need to pass our FROM-GRAPH-CLAUSE to our MATCH-CLAUSE. @@ -189,54 +202,88 @@ @Override public Expression visit(VertexPatternExpr vertexExpression, ILangExpression arg) throws CompilationException { - for (ElementLabel vertexLabel : vertexExpression.getLabels()) { - if (!environmentMap.get(arg).vertexLabels.contains(vertexLabel)) { + PreRewriteCheckEnvironment workingEnvironment = environmentMap.get(arg); + Set<ElementLabel> environmentLabels = workingEnvironment.elementLabels; + for (ElementLabel elementLabel : vertexExpression.getLabels()) { + if (environmentLabels.stream().noneMatch(e -> e.getLabelName().equals(elementLabel.getLabelName()))) { throw new CompilationException(ErrorCode.COMPILATION_ERROR, vertexExpression.getSourceLocation(), - "Vertex label " + vertexLabel + " does not exist in the given graph schema."); + "Vertex label " + elementLabel + " does not exist in the given graph schema."); } } + if (vertexExpression.getFilterExpr() != null) { + vertexExpression.getFilterExpr().accept(new AbstractGraphixQueryVisitor() { + @Override + public Expression visit(VariableExpr varExpr, ILangExpression arg) throws CompilationException { + if (workingEnvironment.allElementVariables.contains(varExpr.getVar())) { + SourceLocation sourceLocation = vertexExpression.getFilterExpr().getSourceLocation(); + throw new CompilationException(ErrorCode.COMPILATION_ERROR, sourceLocation, + "Cannot reference other graph elements in a filter expression! Consider putting your " + + "condition in a WHERE clause."); + } + return super.visit(varExpr, arg); + } + }, null); + } if (vertexExpression.getVariableExpr() != null && !vertexExpression.getLabels().isEmpty()) { Identifier vertexIdentifier = vertexExpression.getVariableExpr().getVar(); - if (environmentMap.get(arg).vertexVariablesWithLabels.contains(vertexIdentifier)) { + if (workingEnvironment.vertexVariablesWithLabels.contains(vertexIdentifier)) { throw new CompilationException(ErrorCode.COMPILATION_ERROR, vertexExpression.getSourceLocation(), "Vertex " + vertexIdentifier + " defined with a label more than once. Labels can only be " + "bound to vertices once."); } - environmentMap.get(arg).vertexVariablesWithLabels.add(vertexIdentifier); + workingEnvironment.vertexVariablesWithLabels.add(vertexIdentifier); + workingEnvironment.allElementVariables.add(vertexIdentifier); } return vertexExpression; } @Override public Expression visit(EdgePatternExpr edgeExpression, ILangExpression arg) throws CompilationException { + PreRewriteCheckEnvironment workingEnvironment = environmentMap.get(arg); EdgeDescriptor edgeDescriptor = edgeExpression.getEdgeDescriptor(); - if (environmentMap.get(arg).edgeLabels.isEmpty()) { + Set<ElementLabel> environmentLabels = workingEnvironment.edgeLabels; + if (environmentLabels.isEmpty()) { throw new CompilationException(ErrorCode.COMPILATION_ERROR, edgeExpression.getSourceLocation(), "Query edge given, but no edge is defined in the schema."); } for (ElementLabel edgeLabel : edgeDescriptor.getEdgeLabels()) { - if (!environmentMap.get(arg).edgeLabels.contains(edgeLabel)) { + if (environmentLabels.stream().noneMatch(e -> e.getLabelName().equals(edgeLabel.getLabelName()))) { throw new CompilationException(ErrorCode.COMPILATION_ERROR, edgeExpression.getSourceLocation(), "Edge label " + edgeLabel + " does not exist in the given graph schema."); } } + if (edgeDescriptor.getFilterExpr() != null) { + edgeDescriptor.getFilterExpr().accept(new AbstractGraphixQueryVisitor() { + @Override + public Expression visit(VariableExpr varExpr, ILangExpression arg) throws CompilationException { + if (workingEnvironment.allElementVariables.contains(varExpr.getVar())) { + SourceLocation sourceLocation = edgeDescriptor.getFilterExpr().getSourceLocation(); + throw new CompilationException(ErrorCode.COMPILATION_ERROR, sourceLocation, + "Cannot reference other graph elements in a filter expression! Consider putting your " + + "condition in a WHERE clause."); + } + return super.visit(varExpr, arg); + } + }, null); + } if (edgeDescriptor.getVariableExpr() != null) { Identifier edgeIdentifier = edgeDescriptor.getVariableExpr().getVar(); - if (environmentMap.get(arg).edgeVariables.contains(edgeIdentifier)) { + if (workingEnvironment.edgeVariables.contains(edgeIdentifier)) { throw new CompilationException(ErrorCode.COMPILATION_ERROR, edgeExpression.getSourceLocation(), "Edge " + edgeIdentifier + " defined more than once. Edges can only connect two vertices."); } - environmentMap.get(arg).edgeVariables.add(edgeIdentifier); + workingEnvironment.edgeVariables.add(edgeIdentifier); + workingEnvironment.allElementVariables.add(edgeIdentifier); } if (edgeDescriptor.getPatternType() == EdgeDescriptor.PatternType.PATH) { Integer minimumHops = edgeDescriptor.getMinimumHops(); Integer maximumHops = edgeDescriptor.getMaximumHops(); - if (maximumHops == 0 || (minimumHops != null && minimumHops == 0)) { + if ((maximumHops != null && maximumHops == 0) || (minimumHops != null && minimumHops == 0)) { throw new CompilationException(ErrorCode.COMPILATION_ERROR, edgeExpression.getSourceLocation(), "Sub-path edges cannot have a hop length less than 1."); - } else if (minimumHops != null && (maximumHops < minimumHops)) { + } else if (minimumHops != null && maximumHops != null && maximumHops < minimumHops) { throw new CompilationException(ErrorCode.COMPILATION_ERROR, edgeExpression.getSourceLocation(), "Sub-path edges cannot have a maximum hop length (" + maximumHops + ") less than the minimum hop length (" + minimumHops + ").");
diff --git a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrite/visitor/QueryCanonicalizationVisitor.java b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrite/visitor/QueryCanonicalizationVisitor.java new file mode 100644 index 0000000..caad9ef --- /dev/null +++ b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrite/visitor/QueryCanonicalizationVisitor.java
@@ -0,0 +1,209 @@ +/* + * 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.graphix.lang.rewrite.visitor; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.apache.asterix.common.exceptions.CompilationException; +import org.apache.asterix.graphix.algebra.compiler.option.ElementEvaluationOption; +import org.apache.asterix.graphix.lang.clause.FromGraphClause; +import org.apache.asterix.graphix.lang.clause.MatchClause; +import org.apache.asterix.graphix.lang.expression.EdgePatternExpr; +import org.apache.asterix.graphix.lang.expression.PathPatternExpr; +import org.apache.asterix.graphix.lang.expression.VertexPatternExpr; +import org.apache.asterix.graphix.lang.rewrite.GraphixRewritingContext; +import org.apache.asterix.graphix.lang.rewrite.canonical.CanonicalElementBranchConsumer; +import org.apache.asterix.graphix.lang.rewrite.canonical.CanonicalElementExpansionConsumer; +import org.apache.asterix.graphix.lang.rewrite.canonical.CanonicalElementGeneratorFactory; +import org.apache.asterix.graphix.lang.rewrite.common.BranchLookupTable; +import org.apache.asterix.graphix.lang.struct.EdgeDescriptor; +import org.apache.asterix.graphix.lang.visitor.base.AbstractGraphixQueryVisitor; +import org.apache.asterix.lang.common.base.AbstractClause; +import org.apache.asterix.lang.common.base.AbstractExpression; +import org.apache.asterix.lang.common.base.Clause; +import org.apache.asterix.lang.common.base.Expression; +import org.apache.asterix.lang.common.base.ILangExpression; +import org.apache.asterix.lang.common.clause.LetClause; +import org.apache.asterix.lang.common.expression.VariableExpr; +import org.apache.asterix.lang.sqlpp.clause.AbstractBinaryCorrelateClause; +import org.apache.asterix.lang.sqlpp.clause.SelectBlock; +import org.apache.asterix.lang.sqlpp.clause.SelectSetOperation; +import org.apache.asterix.lang.sqlpp.expression.SelectExpression; +import org.apache.asterix.lang.sqlpp.visitor.CheckSql92AggregateVisitor; + +/** + * Perform a canonicalization pass, which aims to list out unambiguous <i>navigational complex graph patterns</i>. + * <p> + * For each SELECT-EXPR, we perform the following: + * <ol> + * <li>Collect all ambiguous graph elements in each SELECT-SET-OP.</li> + * <li>Enumerate each canonical form of each ambiguous graph element.</li> + * <li>Invoke one of two canonical element consumers for all ambiguous elements: + * <ol> + * <li>An expansion consumer, which will generate N UNION-ALL branches for N canonical forms of some element.</li> + * <li>A branch consumer, which will populate a separate {@link BranchLookupTable} that holds all canonical forms of + * some element.</li> + * </ol></li> + * <li>If our expansion consumer has fired, then we must ensure that the generated UNION-ALL does not affect any + * grouping or sorts. Call {@link PostCanonicalExpansionVisitor}.</li> + * </ol> + */ +public class QueryCanonicalizationVisitor extends AbstractGraphixQueryVisitor { + private final CheckSql92AggregateVisitor checkSql92AggregateVisitor; + private final GraphixDeepCopyVisitor graphixDeepCopyVisitor; + private final GraphixRewritingContext graphixRewritingContext; + private final BranchLookupTable branchLookupTable; + + public QueryCanonicalizationVisitor(BranchLookupTable branchLookupTable, + GraphixRewritingContext graphixRewritingContext) { + this.checkSql92AggregateVisitor = new CheckSql92AggregateVisitor(); + this.graphixDeepCopyVisitor = new GraphixDeepCopyVisitor(); + this.graphixRewritingContext = graphixRewritingContext; + this.branchLookupTable = branchLookupTable; + } + + @Override + public Expression visit(SelectExpression selectExpression, ILangExpression arg) throws CompilationException { + // Visit our SELECT-EXPR-level LET-CLAUSEs. + for (LetClause letClause : selectExpression.getLetList()) { + letClause.accept(this, selectExpression); + } + + // First pass: collect all ambiguous graph elements. + AmbiguousElementVisitor ambiguousElementVisitor = new AmbiguousElementVisitor(graphixRewritingContext); + SelectSetOperation selectSetOperation = selectExpression.getSelectSetOperation(); + selectSetOperation.accept(ambiguousElementVisitor, selectExpression); + if (ambiguousElementVisitor.getAmbiguousElements().isEmpty()) { + // We have no ambiguous elements. Our [sub]query is in canonical form, and we can exit here. + return selectExpression; + } + + // Second pass: enumerate all canonical forms of each ambiguous element. + Map<AbstractExpression, List<? extends AbstractExpression>> canonicalElementMap = new HashMap<>(); + for (AbstractExpression element : ambiguousElementVisitor.getAmbiguousElements()) { + CanonicalElementGeneratorFactory factory = ambiguousElementVisitor.getGeneratorFactory(element); + if (element instanceof VertexPatternExpr) { + VertexPatternExpr vertexPatternExpr = (VertexPatternExpr) element; + List<VertexPatternExpr> canonicalVertices = factory.generateCanonicalVertices(vertexPatternExpr); + canonicalElementMap.put(element, canonicalVertices); + + } else { // ambiguousElement instanceof EdgePatternExpr + EdgePatternExpr edgePatternExpr = (EdgePatternExpr) element; + EdgeDescriptor edgeDescriptor = edgePatternExpr.getEdgeDescriptor(); + if (edgeDescriptor.getPatternType() == EdgeDescriptor.PatternType.EDGE) { + List<EdgePatternExpr> canonicalEdges = factory.generateCanonicalEdges(edgePatternExpr); + canonicalElementMap.put(element, canonicalEdges); + + } else { // edgeDescriptor.getPatternType() == EdgeDescriptor.PatternType.PATH + ElementEvaluationOption option = ambiguousElementVisitor.getElementEvaluationOption(element); + List<PathPatternExpr> canonicalPaths = factory.generateCanonicalPaths(edgePatternExpr, + option == ElementEvaluationOption.SWITCH_AND_CYCLE); + canonicalElementMap.put(element, canonicalPaths); + } + } + } + + // Third pass: invoke the appropriate consumer for each canonical element. + SelectExpression selectExpressionCopy = graphixDeepCopyVisitor.visit(selectExpression, null); + CanonicalElementExpansionConsumer expansionConsumer = + new CanonicalElementExpansionConsumer(selectExpressionCopy, graphixRewritingContext); + CanonicalElementBranchConsumer branchConsumer = new CanonicalElementBranchConsumer(branchLookupTable); + Map<SelectBlock, List<AbstractExpression>> groupedElements = new HashMap<>(); + ambiguousElementVisitor.getAmbiguousElements().forEach(e -> { + SelectBlock sourceSelectBlock = ambiguousElementVisitor.getSourceSelectBlock(e); + groupedElements.putIfAbsent(sourceSelectBlock, new ArrayList<>()); + groupedElements.get(sourceSelectBlock).add(e); + }); + for (Map.Entry<SelectBlock, List<AbstractExpression>> entry : groupedElements.entrySet()) { + expansionConsumer.resetSelectBlock(entry.getKey()); + for (AbstractExpression ambiguousElement : entry.getValue()) { + ElementEvaluationOption option = ambiguousElementVisitor.getElementEvaluationOption(ambiguousElement); + if (option == ElementEvaluationOption.EXPAND_AND_UNION) { + expansionConsumer.accept(ambiguousElement, canonicalElementMap.get(ambiguousElement)); + + } else { // option == ElementEvaluationOption.SWITCH_AND_CYCLE + branchConsumer.accept(ambiguousElement, canonicalElementMap.get(ambiguousElement)); + } + } + } + + // Check if we have any output-modifiers / grouping, that we have no SET-OPs, and if expansion has occurred. + boolean hasRightInputs = selectSetOperation.hasRightInputs(); + boolean hasOutputModifiers = selectExpression.hasLimit() || selectExpression.hasOrderby(); + boolean hasGroupBy = false, hasAggregation = false; + SelectBlock originalLeftSelectBlock = selectSetOperation.getLeftInput().getSelectBlock(); + if (selectSetOperation.getLeftInput().selectBlock()) { + hasGroupBy = originalLeftSelectBlock.hasGroupbyClause(); + hasAggregation = checkSql92AggregateVisitor.visit(originalLeftSelectBlock, null); + hasAggregation |= originalLeftSelectBlock.getSelectClause().distinct(); + } + + // Finalize our expansion consumer. + Set<SelectBlock> generatedSelectBlocks = new HashSet<>(); + expansionConsumer.finalize(selectExpression, generatedSelectBlocks::add); + + // Perform a post-canonical expansion pass if necessary. + boolean hasExpansionOccurred = !generatedSelectBlocks.isEmpty(); + if (hasExpansionOccurred && !hasRightInputs && (hasOutputModifiers | hasGroupBy | hasAggregation)) { + Set<VariableExpr> liveVariables = new HashSet<>(); + FromGraphClause fromGraphClause = (FromGraphClause) originalLeftSelectBlock.getFromClause(); + for (MatchClause matchClause : fromGraphClause.getMatchClauses()) { + for (PathPatternExpr pathExpression : matchClause.getPathExpressions()) { + if (pathExpression.getVariableExpr() != null) { + liveVariables.add(pathExpression.getVariableExpr()); + } + for (VertexPatternExpr vertexExpression : pathExpression.getVertexExpressions()) { + VariableExpr vertexVariable = vertexExpression.getVariableExpr(); + liveVariables.add(vertexVariable); + } + for (EdgePatternExpr edgeExpression : pathExpression.getEdgeExpressions()) { + VariableExpr edgeVariable = edgeExpression.getEdgeDescriptor().getVariableExpr(); + liveVariables.add(edgeVariable); + } + } + } + if (!fromGraphClause.getCorrelateClauses().isEmpty()) { + List<AbstractBinaryCorrelateClause> correlateClauses = fromGraphClause.getCorrelateClauses(); + for (AbstractBinaryCorrelateClause correlateClause : correlateClauses) { + VariableExpr bindingVariable = correlateClause.getRightVariable(); + liveVariables.add(bindingVariable); + } + } + if (originalLeftSelectBlock.hasLetWhereClauses()) { + for (AbstractClause abstractClause : originalLeftSelectBlock.getLetWhereList()) { + if (abstractClause.getClauseType() == Clause.ClauseType.LET_CLAUSE) { + LetClause letClause = (LetClause) abstractClause; + VariableExpr bindingVariable = letClause.getVarExpr(); + liveVariables.add(bindingVariable); + } + } + } + return new PostCanonicalExpansionVisitor(graphixRewritingContext, originalLeftSelectBlock, + generatedSelectBlocks, liveVariables).visit(selectExpression, arg); + } + + // Return our current SELECT-EXPR. + return selectExpression; + } +}
diff --git a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrite/visitor/SchemaEnrichmentVisitor.java b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrite/visitor/SchemaEnrichmentVisitor.java new file mode 100644 index 0000000..ca64259 --- /dev/null +++ b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrite/visitor/SchemaEnrichmentVisitor.java
@@ -0,0 +1,177 @@ +/* + * 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.graphix.lang.rewrite.visitor; + +import static org.apache.asterix.graphix.function.GraphixFunctionIdentifiers.isEdgeFunction; +import static org.apache.asterix.graphix.function.GraphixFunctionIdentifiers.isVertexFunction; +import static org.apache.asterix.graphix.function.GraphixFunctionMap.getFunctionPrepare; + +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +import org.apache.asterix.common.exceptions.CompilationException; +import org.apache.asterix.graphix.algebra.compiler.option.SchemaDecorateEdgeOption; +import org.apache.asterix.graphix.algebra.compiler.option.SchemaDecorateVertexOption; +import org.apache.asterix.graphix.common.metadata.GraphIdentifier; +import org.apache.asterix.graphix.function.GraphixFunctionIdentifiers; +import org.apache.asterix.graphix.function.prepare.IFunctionPrepare; +import org.apache.asterix.graphix.lang.clause.FromGraphClause; +import org.apache.asterix.graphix.lang.clause.LowerListClause; +import org.apache.asterix.graphix.lang.clause.LowerSwitchClause; +import org.apache.asterix.graphix.lang.clause.MatchClause; +import org.apache.asterix.graphix.lang.expression.EdgePatternExpr; +import org.apache.asterix.graphix.lang.expression.VertexPatternExpr; +import org.apache.asterix.graphix.lang.rewrite.common.BranchLookupTable; +import org.apache.asterix.graphix.lang.rewrite.common.ElementLookupTable; +import org.apache.asterix.graphix.lang.rewrite.lower.struct.ClauseCollection; +import org.apache.asterix.graphix.lang.struct.EdgeDescriptor; +import org.apache.asterix.graphix.lang.visitor.base.AbstractGraphixQueryVisitor; +import org.apache.asterix.lang.common.base.AbstractClause; +import org.apache.asterix.lang.common.base.Clause; +import org.apache.asterix.lang.common.base.Expression; +import org.apache.asterix.lang.common.base.ILangExpression; +import org.apache.asterix.lang.common.clause.LetClause; +import org.apache.asterix.lang.common.expression.VariableExpr; +import org.apache.asterix.lang.sqlpp.clause.SelectBlock; +import org.apache.hyracks.algebricks.common.utils.Pair; +import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier; + +/** + * Perform a pass to enrich any {@link LetClause} and {@link LowerSwitchClause} nodes with necessary schema + * information. Note that this process may overestimate the amount of enrichment actually required (to minimize this + * difference requires some form of equivalence classes @ the rewriter level). + */ +public class SchemaEnrichmentVisitor extends AbstractGraphixQueryVisitor { + private final ElementLookupTable elementLookupTable; + private final Set<FunctionIdentifier> functionIdentifiers; + private final Map<VariableExpr, Expression> expressionMap; + private final SelectBlock workingSelectBlock; + private final GraphIdentifier graphIdentifier; + private final BranchLookupTable branchLookupTable; + + public SchemaEnrichmentVisitor(SchemaDecorateVertexOption vertexMode, SchemaDecorateEdgeOption edgeMode, + ElementLookupTable elementLookupTable, BranchLookupTable branchLookupTable, GraphIdentifier graphIdentifier, + SelectBlock workingSelectBlock, Set<FunctionIdentifier> functionIDs) { + this.graphIdentifier = graphIdentifier; + this.elementLookupTable = elementLookupTable; + this.workingSelectBlock = workingSelectBlock; + this.branchLookupTable = branchLookupTable; + this.expressionMap = new HashMap<>(); + + // If we always need to perform schema-enrichment, then we add all of our schema functions to this set. + this.functionIdentifiers = functionIDs; + if (vertexMode == SchemaDecorateVertexOption.ALWAYS) { + this.functionIdentifiers.add(GraphixFunctionIdentifiers.ELEMENT_LABEL); + this.functionIdentifiers.add(GraphixFunctionIdentifiers.VERTEX_DETAIL); + } + if (edgeMode == SchemaDecorateEdgeOption.ALWAYS) { + this.functionIdentifiers.add(GraphixFunctionIdentifiers.ELEMENT_LABEL); + this.functionIdentifiers.add(GraphixFunctionIdentifiers.EDGE_DETAIL); + this.functionIdentifiers.add(GraphixFunctionIdentifiers.EDGE_DIRECTION); + this.functionIdentifiers.add(GraphixFunctionIdentifiers.EDGE_SOURCE_VERTEX); + this.functionIdentifiers.add(GraphixFunctionIdentifiers.EDGE_DEST_VERTEX); + } + } + + private void visitClauseCollection(ClauseCollection clauseCollection) throws CompilationException { + for (FunctionIdentifier functionIdentifier : functionIdentifiers) { + IFunctionPrepare functionPrepare = getFunctionPrepare(functionIdentifier); + for (LetClause representativeEdgeBinding : clauseCollection.getRepresentativeEdgeBindings()) { + VariableExpr rightVar = representativeEdgeBinding.getVarExpr(); + Expression graphExpr = expressionMap.get(rightVar); + if (isEdgeFunction(functionIdentifier)) { + Expression outputExpr = functionPrepare.prepare(representativeEdgeBinding.getBindingExpr(), + graphExpr, graphIdentifier, elementLookupTable); + representativeEdgeBinding.setBindingExpr(outputExpr); + } + } + for (LetClause representativeVertexBinding : clauseCollection.getRepresentativeVertexBindings()) { + VariableExpr rightVar = representativeVertexBinding.getVarExpr(); + Expression graphExpr = expressionMap.get(rightVar); + if (isVertexFunction(functionIdentifier)) { + Expression outputExpr = functionPrepare.prepare(representativeVertexBinding.getBindingExpr(), + graphExpr, graphIdentifier, elementLookupTable); + representativeVertexBinding.setBindingExpr(outputExpr); + } + } + for (Pair<VariableExpr, LetClause> eagerVertexBinding : clauseCollection.getEagerVertexBindings()) { + Expression graphExpr = expressionMap.get(eagerVertexBinding.first); + if (isVertexFunction(functionIdentifier)) { + Expression outputExpr = functionPrepare.prepare(eagerVertexBinding.second.getBindingExpr(), + graphExpr, graphIdentifier, elementLookupTable); + eagerVertexBinding.second.setBindingExpr(outputExpr); + } + } + for (AbstractClause nonRepresentativeClause : clauseCollection.getNonRepresentativeClauses()) { + if (nonRepresentativeClause.getClauseType() == Clause.ClauseType.EXTENSION) { + LowerSwitchClause lowerSwitchClause = (LowerSwitchClause) nonRepresentativeClause; + for (ClauseCollection innerClauseCollection : lowerSwitchClause.getCollectionLookupTable()) { + visitClauseCollection(innerClauseCollection); + } + } + } + } + } + + @Override + public Expression visit(SelectBlock selectBlock, ILangExpression arg) throws CompilationException { + // Visit our immediate MATCH AST nodes (do not visit lower levels). + FromGraphClause fromGraphClause = (FromGraphClause) selectBlock.getFromClause(); + for (MatchClause matchClause : fromGraphClause.getMatchClauses()) { + matchClause.accept(this, arg); + } + + // We are going to enrich these LET-CLAUSEs & FIXED-POINT-CLAUSEs w/ the necessary schema. + if (workingSelectBlock.equals(selectBlock)) { + LowerListClause lowerClause = (LowerListClause) fromGraphClause.getLowerClause(); + ClauseCollection clauseCollection = lowerClause.getClauseCollection(); + visitClauseCollection(clauseCollection); + } + return null; + } + + @Override + public Expression visit(VertexPatternExpr vertexPatternExpr, ILangExpression arg) throws CompilationException { + VariableExpr variableExpr = vertexPatternExpr.getVariableExpr(); + if (variableExpr != null) { + expressionMap.put(variableExpr, vertexPatternExpr); + } + return super.visit(vertexPatternExpr, arg); + } + + @Override + public Expression visit(EdgePatternExpr edgePatternExpr, ILangExpression arg) throws CompilationException { + EdgeDescriptor edgeDescriptor = edgePatternExpr.getEdgeDescriptor(); + VariableExpr variableExpr = edgeDescriptor.getVariableExpr(); + if (variableExpr != null) { + expressionMap.put(variableExpr, edgePatternExpr); + } + if (branchLookupTable.getBranches(edgePatternExpr) != null) { + for (EdgePatternExpr branch : branchLookupTable.getBranches(edgePatternExpr)) { + branch.getLeftVertex().accept(this, arg); + branch.getRightVertex().accept(this, arg); + branch.accept(this, arg); + } + } + edgePatternExpr.getLeftVertex().accept(this, arg); + edgePatternExpr.getRightVertex().accept(this, arg); + return edgePatternExpr; + } +}
diff --git a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrite/visitor/SubqueryVertexJoinVisitor.java b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrite/visitor/SubqueryVertexJoinVisitor.java new file mode 100644 index 0000000..95993d5 --- /dev/null +++ b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrite/visitor/SubqueryVertexJoinVisitor.java
@@ -0,0 +1,147 @@ +/* + * 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.graphix.lang.rewrite.visitor; + +import java.util.ArrayDeque; +import java.util.Deque; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.apache.asterix.common.exceptions.CompilationException; +import org.apache.asterix.graphix.common.metadata.GraphIdentifier; +import org.apache.asterix.graphix.lang.annotation.SubqueryVertexJoinAnnotation; +import org.apache.asterix.graphix.lang.clause.FromGraphClause; +import org.apache.asterix.graphix.lang.clause.MatchClause; +import org.apache.asterix.graphix.lang.expression.EdgePatternExpr; +import org.apache.asterix.graphix.lang.expression.PathPatternExpr; +import org.apache.asterix.graphix.lang.expression.VertexPatternExpr; +import org.apache.asterix.graphix.lang.rewrite.GraphixRewritingContext; +import org.apache.asterix.graphix.lang.visitor.base.AbstractGraphixQueryVisitor; +import org.apache.asterix.lang.common.base.Expression; +import org.apache.asterix.lang.common.base.ILangExpression; +import org.apache.asterix.lang.common.clause.WhereClause; +import org.apache.asterix.lang.common.context.Scope; +import org.apache.asterix.lang.common.expression.OperatorExpr; +import org.apache.asterix.lang.common.expression.VariableExpr; +import org.apache.asterix.lang.common.parser.ScopeChecker; +import org.apache.asterix.lang.common.struct.OperatorType; +import org.apache.asterix.lang.common.struct.VarIdentifier; +import org.apache.asterix.lang.sqlpp.clause.SelectBlock; +import org.apache.asterix.metadata.declared.MetadataProvider; + +/** + * Search for Graphix sub-queries that have 'free' vertices, and see if we can explicitly JOIN them with a vertex in + * a parent query that refers to the same graph. + */ +public class SubqueryVertexJoinVisitor extends AbstractGraphixQueryVisitor { + private final GraphixDeepCopyVisitor graphixDeepCopyVisitor; + private final GraphixRewritingContext graphixRewritingContext; + private final Map<GraphIdentifier, ScopeChecker> vertexScopeMap; + private final Deque<GraphIdentifier> graphIdentifierStack; + + public SubqueryVertexJoinVisitor(GraphixRewritingContext graphixRewritingContext) { + this.graphixDeepCopyVisitor = new GraphixDeepCopyVisitor(); + this.graphixRewritingContext = graphixRewritingContext; + this.graphIdentifierStack = new ArrayDeque<>(); + this.vertexScopeMap = new HashMap<>(); + } + + @Override + public Expression visit(SelectBlock selectBlock, ILangExpression arg) throws CompilationException { + if (selectBlock.hasFromClause() && selectBlock.getFromClause() instanceof FromGraphClause) { + FromGraphClause fromGraphClause = (FromGraphClause) selectBlock.getFromClause(); + MetadataProvider metadataProvider = graphixRewritingContext.getMetadataProvider(); + GraphIdentifier graphIdentifier = fromGraphClause.getGraphIdentifier(metadataProvider); + vertexScopeMap.putIfAbsent(graphIdentifier, new ScopeChecker()); + graphIdentifierStack.push(graphIdentifier); + vertexScopeMap.get(graphIdentifier).createNewScope(); + + // We want to provide our SELECT-BLOCK to our MATCH-CLAUSE. + super.visit(selectBlock, selectBlock); + graphIdentifierStack.pop(); + vertexScopeMap.get(graphIdentifier).removeCurrentScope(); + + } else { + super.visit(selectBlock, arg); + } + return null; + } + + @Override + public Expression visit(MatchClause matchClause, ILangExpression arg) throws CompilationException { + GraphIdentifier workingGraphIdentifier = graphIdentifierStack.peek(); + ScopeChecker workingScopeChecker = vertexScopeMap.get(workingGraphIdentifier); + Scope precedingScope = workingScopeChecker.getPrecedingScope(); + Scope currentScope = workingScopeChecker.getCurrentScope(); + + Map<VariableExpr, VariableExpr> remappedVariables = new HashMap<>(); + for (PathPatternExpr pathExpression : matchClause.getPathExpressions()) { + for (VertexPatternExpr vertexExpression : pathExpression.getVertexExpressions()) { + VariableExpr vertexVariable = vertexExpression.getVariableExpr(); + VarIdentifier vertexName = vertexVariable.getVar(); + if (precedingScope == null || precedingScope.getParentScope() == null) { + // We are in a top-level FROM-GRAPH-CLAUSE. Add this variable to our current scope. + currentScope.addSymbolToScope(vertexName); + + } else if (currentScope.findLocalSymbol(vertexName.getValue()) == null + && currentScope.findSymbol(vertexName.getValue()) != null) { + // We have a nested Graphix query, and we have found a vertex that references an outer vertex. + SelectBlock parentSelectBlock = (SelectBlock) arg; + + // Replace the current vertex variable with a copy. + VariableExpr newVariable = graphixRewritingContext.getGraphixVariableCopy(vertexVariable); + vertexExpression.setVariableExpr(newVariable); + vertexExpression.getVariableExpr().setSourceLocation(vertexVariable.getSourceLocation()); + vertexExpression.addHint(new SubqueryVertexJoinAnnotation(vertexVariable)); + remappedVariables.put(vertexVariable, newVariable); + + // JOIN our copy with the parent vertex variable. + List<Expression> joinArgs = List.of(vertexVariable, newVariable); + OperatorExpr joinExpr = new OperatorExpr(joinArgs, List.of(OperatorType.EQ), false); + parentSelectBlock.getLetWhereList().add(new WhereClause(joinExpr)); + + } else if (currentScope.findLocalSymbol(vertexName.getValue()) == null) { + // We have a nested Graphix query that does not correlate with an outer query. + currentScope.addSymbolToScope(vertexName); + + } else if (remappedVariables.containsKey(vertexVariable)) { + // We have replaced this variable already. Update the reference. + VariableExpr remappedVertexVariable = remappedVariables.get(vertexVariable); + vertexExpression.setVariableExpr(graphixDeepCopyVisitor.visit(remappedVertexVariable, null)); + vertexExpression.getVariableExpr().setSourceLocation(vertexVariable.getSourceLocation()); + vertexExpression.addHint(new SubqueryVertexJoinAnnotation(vertexVariable)); + } + } + for (EdgePatternExpr edgeExpression : pathExpression.getEdgeExpressions()) { + VertexPatternExpr leftVertex = edgeExpression.getLeftVertex(); + VertexPatternExpr rightVertex = edgeExpression.getRightVertex(); + if (remappedVariables.containsKey(leftVertex.getVariableExpr())) { + VariableExpr leftVariableRemap = remappedVariables.get(leftVertex.getVariableExpr()); + leftVertex.setVariableExpr(graphixDeepCopyVisitor.visit(leftVariableRemap, null)); + leftVertex.addHint(new SubqueryVertexJoinAnnotation(leftVariableRemap)); + } + if (remappedVariables.containsKey(rightVertex.getVariableExpr())) { + VariableExpr rightVariableRemap = remappedVariables.get(rightVertex.getVariableExpr()); + rightVertex.setVariableExpr(graphixDeepCopyVisitor.visit(rightVariableRemap, null)); + rightVertex.addHint(new SubqueryVertexJoinAnnotation(rightVariableRemap)); + } + } + } + return null; + } +}
diff --git a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrite/visitor/VariableRemapCloneVisitor.java b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrite/visitor/VariableRemapCloneVisitor.java new file mode 100644 index 0000000..4b9ba96 --- /dev/null +++ b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrite/visitor/VariableRemapCloneVisitor.java
@@ -0,0 +1,148 @@ +/* + * 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.graphix.lang.rewrite.visitor; + +import java.util.HashMap; +import java.util.Map; + +import org.apache.asterix.common.exceptions.CompilationException; +import org.apache.asterix.graphix.lang.rewrite.GraphixRewritingContext; +import org.apache.asterix.lang.common.base.Expression; +import org.apache.asterix.lang.common.base.ILangExpression; +import org.apache.asterix.lang.common.clause.LetClause; +import org.apache.asterix.lang.common.expression.VariableExpr; +import org.apache.asterix.lang.common.rewrites.VariableSubstitutionEnvironment; +import org.apache.asterix.lang.sqlpp.clause.FromTerm; +import org.apache.asterix.lang.sqlpp.clause.JoinClause; +import org.apache.asterix.lang.sqlpp.clause.UnnestClause; +import org.apache.asterix.lang.sqlpp.visitor.SqlppCloneAndSubstituteVariablesVisitor; +import org.apache.hyracks.algebricks.common.utils.Pair; + +/** + * An extension of {@link SqlppCloneAndSubstituteVariablesVisitor} that also remaps binding variables. + */ +public class VariableRemapCloneVisitor extends SqlppCloneAndSubstituteVariablesVisitor { + private final Map<VariableExpr, Expression> variableToExpressionMap = new HashMap<>(); + + public VariableRemapCloneVisitor(GraphixRewritingContext graphixRewritingContext) { + super(graphixRewritingContext); + } + + public void addSubstitution(VariableExpr variable, Expression newExpr) { + variableToExpressionMap.put(variable, newExpr); + } + + public void resetSubstitutions() { + variableToExpressionMap.clear(); + } + + public ILangExpression substitute(ILangExpression langExpression) throws CompilationException { + return langExpression.accept(this, createKeepSubstitutionEnvironment()).first; + } + + @Override + public Pair<ILangExpression, VariableSubstitutionEnvironment> visit(FromTerm fromTerm, + VariableSubstitutionEnvironment env) throws CompilationException { + Pair<ILangExpression, VariableSubstitutionEnvironment> pair = super.visit(fromTerm, env); + if (env.constainsOldVar(fromTerm.getLeftVariable()) + || (fromTerm.hasPositionalVariable() && env.constainsOldVar(fromTerm.getPositionalVariable()))) { + FromTerm fromTermAfterVisit = (FromTerm) pair.first; + VariableExpr remapLeftVariable = (VariableExpr) rewriteVariableExpr(fromTerm.getLeftVariable(), env); + VariableExpr remapPositionalVariable = fromTerm.hasPositionalVariable() + ? (VariableExpr) rewriteVariableExpr(fromTerm.getPositionalVariable(), env) : null; + FromTerm fromTermWithRemapVariable = new FromTerm(fromTermAfterVisit.getLeftExpression(), remapLeftVariable, + remapPositionalVariable, fromTermAfterVisit.getCorrelateClauses()); + fromTermWithRemapVariable.setSourceLocation(fromTermAfterVisit.getSourceLocation()); + return new Pair<>(fromTermWithRemapVariable, pair.second); + + } else { + return pair; + } + } + + @Override + public Pair<ILangExpression, VariableSubstitutionEnvironment> visit(JoinClause joinClause, + VariableSubstitutionEnvironment env) throws CompilationException { + Pair<ILangExpression, VariableSubstitutionEnvironment> pair = super.visit(joinClause, env); + if (env.constainsOldVar(joinClause.getRightVariable()) + || (joinClause.hasPositionalVariable() && env.constainsOldVar(joinClause.getPositionalVariable()))) { + JoinClause joinClauseAfterVisit = (JoinClause) pair.first; + VariableExpr remapRightVariable = (VariableExpr) rewriteVariableExpr(joinClause.getRightVariable(), env); + VariableExpr remapPositionalVariable = joinClause.hasPositionalVariable() + ? (VariableExpr) rewriteVariableExpr(joinClause.getPositionalVariable(), env) : null; + + // A new environment gets created for our condition expression in our parent, so we need to revisit here. + ILangExpression remapConditionExpression = joinClause.getConditionExpression().accept(this, env).first; + JoinClause joinClauseWithRemapVariable = new JoinClause(joinClauseAfterVisit.getJoinType(), + joinClauseAfterVisit.getRightExpression(), remapRightVariable, remapPositionalVariable, + (Expression) remapConditionExpression, joinClauseAfterVisit.getOuterJoinMissingValueType()); + joinClauseWithRemapVariable.setSourceLocation(joinClauseAfterVisit.getSourceLocation()); + return new Pair<>(joinClauseWithRemapVariable, pair.second); + + } else { + return pair; + } + } + + @Override + public Pair<ILangExpression, VariableSubstitutionEnvironment> visit(UnnestClause unnestClause, + VariableSubstitutionEnvironment env) throws CompilationException { + Pair<ILangExpression, VariableSubstitutionEnvironment> pair = super.visit(unnestClause, env); + if (env.constainsOldVar(unnestClause.getRightVariable()) || (unnestClause.hasPositionalVariable() + && env.constainsOldVar(unnestClause.getPositionalVariable()))) { + UnnestClause unnestClauseAfterVisit = (UnnestClause) pair.first; + VariableExpr remapRightVariable = (VariableExpr) rewriteVariableExpr(unnestClause.getRightVariable(), env); + VariableExpr remapPositionalVariable = unnestClause.hasPositionalVariable() + ? (VariableExpr) rewriteVariableExpr(unnestClause.getPositionalVariable(), env) : null; + UnnestClause unnestClauseWithRemapVariable = new UnnestClause(unnestClauseAfterVisit.getUnnestType(), + unnestClauseAfterVisit.getRightExpression(), remapRightVariable, remapPositionalVariable, + unnestClauseAfterVisit.getOuterUnnestMissingValueType()); + unnestClauseWithRemapVariable.setSourceLocation(unnestClauseAfterVisit.getSourceLocation()); + return new Pair<>(unnestClauseWithRemapVariable, pair.second); + + } else { + return pair; + } + } + + @Override + public Pair<ILangExpression, VariableSubstitutionEnvironment> visit(LetClause letClause, + VariableSubstitutionEnvironment env) throws CompilationException { + Pair<ILangExpression, VariableSubstitutionEnvironment> pair = super.visit(letClause, env); + if (env.constainsOldVar(letClause.getVarExpr())) { + LetClause letClauseAfterVisit = (LetClause) pair.first; + VariableExpr remapVariable = (VariableExpr) rewriteVariableExpr(letClause.getVarExpr(), env); + LetClause letClauseWithRemapVariable = new LetClause(remapVariable, letClauseAfterVisit.getBindingExpr()); + letClauseWithRemapVariable.setSourceLocation(letClauseAfterVisit.getSourceLocation()); + return new Pair<>(letClauseWithRemapVariable, pair.second); + + } else { + return pair; + } + } + + private VariableSubstitutionEnvironment createKeepSubstitutionEnvironment() { + return new VariableSubstitutionEnvironment(variableToExpressionMap) { + @Override + public void removeSubstitution(VariableExpr oldVar) { + // We don't want to remove our substitution here. + } + }; + } +}
diff --git a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/visitor/ScopingCheckVisitor.java b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrite/visitor/VariableScopingCheckVisitor.java similarity index 74% rename from asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/visitor/ScopingCheckVisitor.java rename to asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrite/visitor/VariableScopingCheckVisitor.java index 2b01f9c..6d28200 100644 --- a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/visitor/ScopingCheckVisitor.java +++ b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrite/visitor/VariableScopingCheckVisitor.java
@@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.asterix.graphix.lang.rewrites.visitor; +package org.apache.asterix.graphix.lang.rewrite.visitor; import java.util.ArrayDeque; import java.util.Deque; @@ -25,30 +25,31 @@ import org.apache.asterix.common.exceptions.CompilationException; import org.apache.asterix.common.exceptions.ErrorCode; import org.apache.asterix.graphix.lang.clause.FromGraphClause; -import org.apache.asterix.graphix.lang.clause.GraphSelectBlock; import org.apache.asterix.graphix.lang.clause.MatchClause; import org.apache.asterix.graphix.lang.expression.EdgePatternExpr; import org.apache.asterix.graphix.lang.expression.GraphConstructor; import org.apache.asterix.graphix.lang.expression.PathPatternExpr; import org.apache.asterix.graphix.lang.expression.VertexPatternExpr; -import org.apache.asterix.graphix.lang.rewrites.GraphixRewritingContext; +import org.apache.asterix.graphix.lang.rewrite.GraphixRewritingContext; import org.apache.asterix.graphix.lang.statement.CreateGraphStatement; import org.apache.asterix.graphix.lang.statement.DeclareGraphStatement; import org.apache.asterix.graphix.lang.statement.GraphDropStatement; import org.apache.asterix.graphix.lang.statement.GraphElementDeclaration; import org.apache.asterix.graphix.lang.struct.EdgeDescriptor; -import org.apache.asterix.lang.common.base.AbstractClause; +import org.apache.asterix.graphix.lang.visitor.base.IGraphixLangVisitor; import org.apache.asterix.lang.common.base.Expression; import org.apache.asterix.lang.common.base.ILangExpression; +import org.apache.asterix.lang.common.expression.CallExpr; import org.apache.asterix.lang.common.expression.VariableExpr; import org.apache.asterix.lang.common.struct.Identifier; import org.apache.asterix.lang.sqlpp.clause.AbstractBinaryCorrelateClause; +import org.apache.asterix.lang.sqlpp.clause.FromClause; import org.apache.asterix.lang.sqlpp.clause.Projection; -import org.apache.asterix.lang.sqlpp.clause.SelectBlock; import org.apache.asterix.lang.sqlpp.clause.SelectClause; import org.apache.asterix.lang.sqlpp.clause.SelectRegular; import org.apache.asterix.lang.sqlpp.expression.SelectExpression; import org.apache.asterix.lang.sqlpp.util.SqlppVariableUtil; +import org.apache.asterix.lang.sqlpp.visitor.CheckSql92AggregateVisitor; import org.apache.asterix.lang.sqlpp.visitor.base.AbstractSqlppExpressionScopingVisitor; import org.apache.commons.lang3.mutable.Mutable; import org.apache.commons.lang3.mutable.MutableObject; @@ -56,70 +57,52 @@ /** * Ensure that any subtree whose immediate parent node includes a {@link FromGraphClause} follow Graphix-specific * variable resolution rules (i.e. do not rely on context variables). - * - {@link PathPatternExpr} may introduce a variable. Uniqueness is enforced here. - * - {@link EdgePatternExpr} may introduce a variable. Uniqueness is enforced w/ {@link PreRewriteCheckVisitor}. - * - {@link VertexPatternExpr} may introduce a variable. Uniqueness is not required. - * - {@link org.apache.asterix.lang.sqlpp.clause.JoinClause} may introduce a variable (handled in parent). - * - {@link org.apache.asterix.lang.sqlpp.clause.NestClause} may introduce a variable (handled in parent). - * - {@link org.apache.asterix.lang.sqlpp.clause.UnnestClause} may introduce a variable (handled in parent). - * - {@link org.apache.asterix.lang.common.clause.GroupbyClause} may introduce a variable (handled in parent). - * - {@link org.apache.asterix.lang.common.clause.LetClause} may introduce a variable (handled in parent). + * <ul> + * <li>{@link PathPatternExpr} may introduce a variable. Uniqueness is enforced here.</li> + * <li>{@link EdgePatternExpr} may introduce a variable. Uniqueness is enforced w/ {@link PreRewriteCheckVisitor}.</li> + * <li>{@link VertexPatternExpr} may introduce a variable. Uniqueness is not required.</li> + * <li>{@link org.apache.asterix.lang.sqlpp.clause.JoinClause} may introduce a variable (handled in parent).</li> + * <li>{@link org.apache.asterix.lang.sqlpp.clause.NestClause} may introduce a variable (handled in parent).</li> + * <li>{@link org.apache.asterix.lang.sqlpp.clause.UnnestClause} may introduce a variable (handled in parent).</li> + * <li>{@link org.apache.asterix.lang.common.clause.GroupbyClause} may introduce a variable (handled in parent).</li> + * <li>{@link org.apache.asterix.lang.common.clause.LetClause} may introduce a variable (handled in parent).</li> + * </ul> */ -public class ScopingCheckVisitor extends AbstractSqlppExpressionScopingVisitor +public class VariableScopingCheckVisitor extends AbstractSqlppExpressionScopingVisitor implements IGraphixLangVisitor<Expression, ILangExpression> { - private final Deque<Mutable<Boolean>> graphixVisitStack = new ArrayDeque<>(); + private final CheckSql92AggregateVisitor checkSql92AggregateVisitor = new CheckSql92AggregateVisitor(); + private final Deque<Mutable<Boolean>> fromGraphClauseVisitStack = new ArrayDeque<>(); - public ScopingCheckVisitor(GraphixRewritingContext graphixRewritingContext) { + public VariableScopingCheckVisitor(GraphixRewritingContext graphixRewritingContext) { super(graphixRewritingContext); // We start with an element of false in our stack. - graphixVisitStack.addLast(new MutableObject<>(false)); + fromGraphClauseVisitStack.addLast(new MutableObject<>(false)); } @Override public Expression visit(SelectExpression selectExpression, ILangExpression arg) throws CompilationException { - graphixVisitStack.addLast(new MutableObject<>(false)); + fromGraphClauseVisitStack.addLast(new MutableObject<>(false)); super.visit(selectExpression, arg); - graphixVisitStack.removeLast(); + fromGraphClauseVisitStack.removeLast(); return selectExpression; } @Override - public Expression visit(SelectBlock selectBlock, ILangExpression arg) throws CompilationException { - return (selectBlock instanceof GraphSelectBlock) ? this.visit((GraphSelectBlock) selectBlock, arg) - : super.visit(selectBlock, arg); - } + public Expression visit(FromClause fromClause, ILangExpression arg) throws CompilationException { + if (fromClause instanceof FromGraphClause) { + return visit((FromGraphClause) fromClause, arg); - @Override - public Expression visit(GraphSelectBlock graphSelectBlock, ILangExpression arg) throws CompilationException { - graphixVisitStack.getLast().setValue(true); - if (graphSelectBlock.hasFromGraphClause()) { - graphSelectBlock.getFromGraphClause().accept(this, arg); - - } else if (graphSelectBlock.hasFromClause()) { - graphSelectBlock.getFromClause().accept(this, arg); + } else { + return super.visit(fromClause, arg); } - if (graphSelectBlock.hasLetWhereClauses()) { - for (AbstractClause clause : graphSelectBlock.getLetWhereList()) { - clause.accept(this, arg); - } - } - if (graphSelectBlock.hasGroupbyClause()) { - graphSelectBlock.getGroupbyClause().accept(this, arg); - } - if (graphSelectBlock.hasLetHavingClausesAfterGroupby()) { - for (AbstractClause clause : graphSelectBlock.getLetHavingListAfterGroupby()) { - clause.accept(this, arg); - } - } - graphSelectBlock.getSelectClause().accept(this, arg); - return null; } @Override public Expression visit(FromGraphClause fromGraphClause, ILangExpression arg) throws CompilationException { // We are now working with a new scope. scopeChecker.createNewScope(); + fromGraphClauseVisitStack.getLast().setValue(true); for (MatchClause matchClause : fromGraphClause.getMatchClauses()) { matchClause.accept(this, arg); } @@ -167,6 +150,9 @@ if (edgeDescriptor.getVariableExpr() != null) { scopeChecker.getCurrentScope().addNewVarSymbolToScope(edgeDescriptor.getVariableExpr().getVar()); } + if (edgeDescriptor.getFilterExpr() != null) { + edgeDescriptor.getFilterExpr().accept(this, arg); + } return edgePatternExpr; } @@ -175,9 +161,18 @@ if (vertexPatternExpr.getVariableExpr() != null) { scopeChecker.getCurrentScope().addNewVarSymbolToScope(vertexPatternExpr.getVariableExpr().getVar()); } + if (vertexPatternExpr.getFilterExpr() != null) { + vertexPatternExpr.getFilterExpr().accept(this, arg); + } return vertexPatternExpr; } + // We will defer the scope of SQL-92 aggregates to our AggregationSugarVisitor. + @Override + public Expression visit(CallExpr callExpr, ILangExpression arg) throws CompilationException { + return (checkSql92AggregateVisitor.visit(callExpr, arg)) ? callExpr : super.visit(callExpr, arg); + } + // We aren't going to inline our column aliases (yet), so add our select variables to our scope. @Override public Expression visit(SelectClause selectClause, ILangExpression arg) throws CompilationException { @@ -194,7 +189,8 @@ @Override public Expression visit(VariableExpr varExpr, ILangExpression arg) throws CompilationException { - boolean hasVisitedGraphixNode = !graphixVisitStack.isEmpty() && graphixVisitStack.getLast().getValue(); + boolean hasVisitedGraphixNode = + !fromGraphClauseVisitStack.isEmpty() && fromGraphClauseVisitStack.getLast().getValue(); String varSymbol = varExpr.getVar().getValue(); // We will only throw an unresolved error if we first encounter a Graphix AST node.
diff --git a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/GraphixQueryRewriter.java b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/GraphixQueryRewriter.java deleted file mode 100644 index 86fc0ce..0000000 --- a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/GraphixQueryRewriter.java +++ /dev/null
@@ -1,287 +0,0 @@ -/* - * 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.graphix.lang.rewrites; - -import java.io.PrintWriter; -import java.io.StringWriter; -import java.util.Collection; -import java.util.List; - -import org.apache.asterix.common.exceptions.CompilationException; -import org.apache.asterix.common.exceptions.ErrorCode; -import org.apache.asterix.common.metadata.DataverseName; -import org.apache.asterix.graphix.algebra.compiler.provider.GraphixCompilationProvider; -import org.apache.asterix.graphix.lang.parser.GraphixParserFactory; -import org.apache.asterix.graphix.lang.rewrites.common.ElementLookupTable; -import org.apache.asterix.graphix.lang.rewrites.print.SqlppASTPrintQueryVisitor; -import org.apache.asterix.graphix.lang.rewrites.visitor.CanonicalExpansionVisitor; -import org.apache.asterix.graphix.lang.rewrites.visitor.ElementLookupTableVisitor; -import org.apache.asterix.graphix.lang.rewrites.visitor.FunctionResolutionVisitor; -import org.apache.asterix.graphix.lang.rewrites.visitor.GraphixFunctionCallVisitor; -import org.apache.asterix.graphix.lang.rewrites.visitor.GraphixLoweringVisitor; -import org.apache.asterix.graphix.lang.rewrites.visitor.GroupByAggSugarVisitor; -import org.apache.asterix.graphix.lang.rewrites.visitor.PopulateUnknownsVisitor; -import org.apache.asterix.graphix.lang.rewrites.visitor.PostRewriteCheckVisitor; -import org.apache.asterix.graphix.lang.rewrites.visitor.PostRewriteVariableVisitor; -import org.apache.asterix.graphix.lang.rewrites.visitor.PreRewriteCheckVisitor; -import org.apache.asterix.graphix.lang.rewrites.visitor.ScopingCheckVisitor; -import org.apache.asterix.graphix.lang.rewrites.visitor.StructureAnalysisVisitor; -import org.apache.asterix.graphix.lang.rewrites.visitor.StructureResolutionVisitor; -import org.apache.asterix.graphix.lang.statement.GraphElementDeclaration; -import org.apache.asterix.lang.common.base.Expression; -import org.apache.asterix.lang.common.base.IParserFactory; -import org.apache.asterix.lang.common.base.IReturningStatement; -import org.apache.asterix.lang.common.rewrites.LangRewritingContext; -import org.apache.asterix.lang.common.statement.Query; -import org.apache.asterix.lang.common.struct.VarIdentifier; -import org.apache.asterix.lang.common.util.ExpressionUtils; -import org.apache.asterix.lang.sqlpp.rewrites.SqlppFunctionBodyRewriter; -import org.apache.asterix.lang.sqlpp.rewrites.SqlppQueryRewriter; -import org.apache.asterix.metadata.declared.MetadataProvider; -import org.apache.asterix.metadata.entities.Dataverse; -import org.apache.hyracks.algebricks.common.exceptions.AlgebricksException; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - -/** - * Rewriter for Graphix queries, which will lower all graph AST nodes into SQL++ AST nodes. We perform the following: - * 1. Perform an error-checking on the fresh AST (immediately after parsing). - * 2. Populate the unknowns in our AST (e.g. vertex / edge variables, projections, GROUP-BY keys). - * 3. Perform a variable-scoping pass to identify illegal variables (either duplicate or out-of-scope). - * 4. Resolve all of our function calls (Graphix, SQL++, and user-defined). - * 5. Perform resolution of unlabeled vertices / edges, as well as edge directions. - * 6. Using the labels of the vertices / edges in our AST, fetch the relevant graph elements from our metadata. - * 7. Perform a canonical Graphix lowering pass to remove ambiguities (e.g. undirected edges). - * 8. Perform a lowering pass to transform Graphix AST nodes to SQL++ AST nodes. - * 9. Perform another lowering pass to transform Graphix CALL-EXPR nodes to SQL++ AST nodes. - * 10. Perform all SQL++ rewrites on our newly lowered AST. - */ -public class GraphixQueryRewriter extends SqlppQueryRewriter { - private static final Logger LOGGER = LogManager.getLogger(GraphixQueryRewriter.class); - - private final GraphixParserFactory parserFactory; - private final SqlppQueryRewriter bodyRewriter; - - public GraphixQueryRewriter(IParserFactory parserFactory) { - super(parserFactory); - this.parserFactory = (GraphixParserFactory) parserFactory; - this.bodyRewriter = getFunctionAndViewBodyRewriter(); - } - - @Override - public void rewrite(LangRewritingContext langRewritingContext, IReturningStatement topStatement, - boolean allowNonStoredUDFCalls, boolean inlineUdfsAndViews, Collection<VarIdentifier> externalVars) - throws CompilationException { - LOGGER.debug("Starting Graphix AST rewrites."); - - // Perform an initial error-checking pass to validate our user query. - LOGGER.trace("Performing pre-Graphix-rewrite check (user query validation)."); - GraphixRewritingContext graphixRewritingContext = (GraphixRewritingContext) langRewritingContext; - PreRewriteCheckVisitor preRewriteCheckVisitor = new PreRewriteCheckVisitor(graphixRewritingContext); - topStatement.getBody().accept(preRewriteCheckVisitor, null); - - // Perform the Graphix rewrites. - rewriteGraphixASTNodes(graphixRewritingContext, topStatement, allowNonStoredUDFCalls); - - // Sanity check: ensure that no graph AST nodes exist after this point. - LOGGER.trace("Performing post-Graphix-rewrite check (making sure no graph AST nodes exist)."); - PostRewriteCheckVisitor postRewriteCheckVisitor = new PostRewriteCheckVisitor(); - topStatement.getBody().accept(postRewriteCheckVisitor, null); - - // Perform the remainder of the SQL++ rewrites. - LOGGER.debug("Ending Graphix AST rewrites. Now starting SQL++ AST rewrites."); - rewriteSQLPPASTNodes(langRewritingContext, topStatement, allowNonStoredUDFCalls, inlineUdfsAndViews, - externalVars); - - // If desired, log the SQL++ query equivalent (i.e. turn the AST back into a query and log this). - MetadataProvider metadataProvider = graphixRewritingContext.getMetadataProvider(); - String printRewriteMetadataKeyName = GraphixCompilationProvider.PRINT_REWRITE_METADATA_CONFIG; - String printRewriteOption = (String) metadataProvider.getConfig().get(printRewriteMetadataKeyName); - if ((printRewriteOption != null && printRewriteOption.equalsIgnoreCase("true")) || LOGGER.isTraceEnabled()) { - StringWriter stringWriter = new StringWriter(); - PrintWriter printWriter = new PrintWriter(stringWriter); - new SqlppASTPrintQueryVisitor(printWriter).visit((Query) topStatement, null); - LOGGER.log(LOGGER.getLevel(), "Rewritten Graphix query: " + stringWriter); - } - - // Update the variable counter on our context. - topStatement.setVarCounter(graphixRewritingContext.getVarCounter().get()); - LOGGER.debug("Ending SQL++ AST rewrites."); - } - - public void loadNormalizedGraphElement(GraphixRewritingContext graphixRewritingContext, - GraphElementDeclaration graphElementDeclaration) throws CompilationException { - if (graphElementDeclaration.getNormalizedBody() == null) { - Dataverse defaultDataverse = graphixRewritingContext.getMetadataProvider().getDefaultDataverse(); - Dataverse targetDataverse; - - // We might need to change our dataverse, if the element definition requires a different one. - DataverseName elementName = graphElementDeclaration.getIdentifier().getGraphIdentifier().getDataverseName(); - if (elementName.equals(defaultDataverse.getDataverseName())) { - targetDataverse = defaultDataverse; - - } else { - try { - targetDataverse = graphixRewritingContext.getMetadataProvider().findDataverse(elementName); - - } catch (AlgebricksException e) { - throw new CompilationException(ErrorCode.UNKNOWN_DATAVERSE, e, - graphElementDeclaration.getSourceLocation(), elementName); - } - } - graphixRewritingContext.getMetadataProvider().setDefaultDataverse(targetDataverse); - - // Get the body of the rewritten query. - Expression rawBody = graphElementDeclaration.getRawBody(); - try { - Query wrappedQuery = - ExpressionUtils.createWrappedQuery(rawBody, graphElementDeclaration.getSourceLocation()); - bodyRewriter.rewrite(graphixRewritingContext, wrappedQuery, false, false, List.of()); - graphElementDeclaration.setNormalizedBody(wrappedQuery.getBody()); - - } catch (CompilationException e) { - throw new CompilationException(ErrorCode.COMPILATION_ERROR, rawBody.getSourceLocation(), - "Bad definition for a graph element: " + e.getMessage()); - - } finally { - // Switch back to the working dataverse. - graphixRewritingContext.getMetadataProvider().setDefaultDataverse(defaultDataverse); - } - } - } - - public void rewriteGraphixASTNodes(GraphixRewritingContext graphixRewritingContext, - IReturningStatement topStatement, boolean allowNonStoredUDFCalls) throws CompilationException { - // Generate names for unnamed graph elements, projections in our SELECT CLAUSE, and keys in our GROUP BY. - LOGGER.trace("Populating unknowns (both graph and non-graph) in our AST."); - PopulateUnknownsVisitor populateUnknownsVisitor = new PopulateUnknownsVisitor(graphixRewritingContext); - topStatement.getBody().accept(populateUnknownsVisitor, null); - - // Verify that variables are properly within scope. - LOGGER.trace("Verifying that variables are unique and are properly scoped."); - ScopingCheckVisitor scopingCheckVisitor = new ScopingCheckVisitor(graphixRewritingContext); - topStatement.getBody().accept(scopingCheckVisitor, null); - - // Resolve all of our (Graphix, SQL++, and user-defined) function calls. - LOGGER.trace("Resolving Graphix, SQL++, and user-defined function calls."); - FunctionResolutionVisitor functionResolutionVisitor = - new FunctionResolutionVisitor(graphixRewritingContext, allowNonStoredUDFCalls); - topStatement.getBody().accept(functionResolutionVisitor, null); - - // Attempt to resolve our vertex labels, edge labels, and edge directions. - LOGGER.trace("Performing label and edge direction resolution."); - StructureResolutionVisitor structureResolutionVisitor = new StructureResolutionVisitor(graphixRewritingContext); - topStatement.getBody().accept(structureResolutionVisitor, null); - - // Fetch all relevant graph element declarations, using the element labels. - LOGGER.trace("Fetching relevant edge and vertex bodies from our graph schema."); - ElementLookupTable elementLookupTable = new ElementLookupTable(); - ElementLookupTableVisitor elementLookupTableVisitor = - new ElementLookupTableVisitor(graphixRewritingContext, elementLookupTable, parserFactory); - topStatement.getBody().accept(elementLookupTableVisitor, null); - for (GraphElementDeclaration graphElementDeclaration : elementLookupTable) { - loadNormalizedGraphElement(graphixRewritingContext, graphElementDeclaration); - } - - // Expand vertex and edge patterns to remove all ambiguities (into a canonical form). - LOGGER.trace("Expanding vertex and edge patterns to a canonical form."); - CanonicalExpansionVisitor canonicalExpansionVisitor = new CanonicalExpansionVisitor(graphixRewritingContext); - topStatement.setBody(topStatement.getBody().accept(canonicalExpansionVisitor, null)); - - // Perform an analysis pass to get our edge dependencies, dangling vertices, and FROM-GRAPH-CLAUSE variables. - LOGGER.trace("Collecting edge dependencies, dangling vertices, and FROM-GRAPH-CLAUSE specific variables."); - StructureAnalysisVisitor structureAnalysisVisitor = new StructureAnalysisVisitor(graphixRewritingContext); - topStatement.getBody().accept(structureAnalysisVisitor, null); - - // Transform all graph AST nodes (i.e. perform the representation lowering). - LOGGER.trace("Lowering the Graphix AST-specific nodes representation to a pure SQL++ representation."); - GraphixLoweringVisitor graphixLoweringVisitor = new GraphixLoweringVisitor(graphixRewritingContext, - elementLookupTable, structureAnalysisVisitor.getFromGraphClauseContextMap()); - topStatement.setBody(topStatement.getBody().accept(graphixLoweringVisitor, null)); - - // Lower all of our Graphix function calls (and perform schema-enrichment). - LOGGER.trace("Lowering the Graphix CALL-EXPR nodes to a pure SQL++ representation."); - GraphixFunctionCallVisitor graphixFunctionCallVisitor = new GraphixFunctionCallVisitor(graphixRewritingContext); - topStatement.getBody().accept(graphixFunctionCallVisitor, null); - } - - public void rewriteSQLPPASTNodes(LangRewritingContext langRewritingContext, IReturningStatement topStatement, - boolean allowNonStoredUDFCalls, boolean inlineUdfsAndViews, Collection<VarIdentifier> externalVars) - throws CompilationException { - super.setup(langRewritingContext, topStatement, externalVars, allowNonStoredUDFCalls, inlineUdfsAndViews); - super.substituteGroupbyKeyExpression(); - super.rewriteGroupBys(); - super.inlineColumnAlias(); - super.rewriteWindowExpressions(); - super.rewriteGroupingSets(); - - // We need to override the default behavior of our variable check + rewrite visitor... - PostRewriteVariableVisitor postRewriteVariableVisitor = new PostRewriteVariableVisitor(langRewritingContext, - langRewritingContext.getMetadataProvider(), externalVars); - topStatement.accept(postRewriteVariableVisitor, null); - super.extractAggregatesFromCaseExpressions(); - - // ...and our GROUP-BY rewrite visitor. - GroupByAggSugarVisitor groupByAggSugarVisitor = new GroupByAggSugarVisitor(langRewritingContext, externalVars); - topStatement.accept(groupByAggSugarVisitor, null); - - // The remainder of our rewrites are the same. - super.rewriteWindowAggregationSugar(); - super.rewriteOperatorExpression(); - super.rewriteCaseExpressions(); - super.rewriteListInputFunctions(); - super.rewriteRightJoins(); - super.loadAndInlineUdfsAndViews(); - super.rewriteSpecialFunctionNames(); - super.inlineWithExpressions(); - } - - @Override - protected SqlppFunctionBodyRewriter getFunctionAndViewBodyRewriter() { - return new SqlppFunctionBodyRewriter(parserFactory) { - @Override - public void rewrite(LangRewritingContext langRewritingContext, IReturningStatement topStatement, - boolean allowNonStoredUDFCalls, boolean inlineUdfsAndViews, Collection<VarIdentifier> externalVars) - throws CompilationException { - // Perform an initial error-checking pass to validate our body. - LOGGER.trace("Performing pre-Graphix-rewrite check (user query validation)."); - GraphixRewritingContext graphixRewritingContext = (GraphixRewritingContext) langRewritingContext; - PreRewriteCheckVisitor preRewriteCheckVisitor = new PreRewriteCheckVisitor(graphixRewritingContext); - topStatement.getBody().accept(preRewriteCheckVisitor, null); - - // Perform the Graphix rewrites. - rewriteGraphixASTNodes(graphixRewritingContext, topStatement, allowNonStoredUDFCalls); - - // Sanity check: ensure that no graph AST nodes exist after this point. - LOGGER.trace("Performing post-Graphix-rewrite check (making sure no graph AST nodes exist)."); - PostRewriteCheckVisitor postRewriteCheckVisitor = new PostRewriteCheckVisitor(); - topStatement.getBody().accept(postRewriteCheckVisitor, null); - - // Perform the remainder of the SQL++ rewrites. - LOGGER.debug("Ending Graphix AST rewrites. Now starting SQL++ AST rewrites."); - rewriteSQLPPASTNodes(langRewritingContext, topStatement, allowNonStoredUDFCalls, inlineUdfsAndViews, - externalVars); - LOGGER.debug("Ending SQL++ AST rewrites."); - - // Update the variable counter in our context. - topStatement.setVarCounter(langRewritingContext.getVarCounter().get()); - } - }; - } -}
diff --git a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/GraphixRewritingContext.java b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/GraphixRewritingContext.java deleted file mode 100644 index 7e8bda5..0000000 --- a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/GraphixRewritingContext.java +++ /dev/null
@@ -1,62 +0,0 @@ -/* - * 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.graphix.lang.rewrites; - -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import org.apache.asterix.graphix.common.metadata.GraphIdentifier; -import org.apache.asterix.graphix.lang.statement.DeclareGraphStatement; -import org.apache.asterix.lang.common.rewrites.LangRewritingContext; -import org.apache.asterix.lang.common.statement.FunctionDecl; -import org.apache.asterix.lang.common.statement.ViewDecl; -import org.apache.asterix.lang.common.struct.VarIdentifier; -import org.apache.asterix.metadata.declared.MetadataProvider; -import org.apache.hyracks.api.exceptions.IWarningCollector; - -/** - * Wrapper class for {@link LangRewritingContext} and for Graphix specific rewriting. - */ -public class GraphixRewritingContext extends LangRewritingContext { - private final Map<GraphIdentifier, DeclareGraphStatement> declaredGraphs = new HashMap<>(); - - public GraphixRewritingContext(MetadataProvider metadataProvider, List<FunctionDecl> declaredFunctions, - List<ViewDecl> declaredViews, Set<DeclareGraphStatement> declareGraphStatements, - IWarningCollector warningCollector, int varCounter) { - super(metadataProvider, declaredFunctions, declaredViews, warningCollector, varCounter); - declareGraphStatements.forEach(d -> { - GraphIdentifier graphIdentifier = new GraphIdentifier(d.getDataverseName(), d.getGraphName()); - this.declaredGraphs.put(graphIdentifier, d); - }); - } - - public Map<GraphIdentifier, DeclareGraphStatement> getDeclaredGraphs() { - return declaredGraphs; - } - - public VarIdentifier getNewGraphixVariable() { - VarIdentifier langRewriteGeneratedVar = newVariable(); - String graphixGeneratedName = "#GG_" + langRewriteGeneratedVar.getValue().substring(1); - return new VarIdentifier(graphixGeneratedName, langRewriteGeneratedVar.getId()); - } - - public static boolean isGraphixVariable(VarIdentifier varIdentifier) { - return varIdentifier.getValue().startsWith("#GG_"); - } -}
diff --git a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/canonical/DanglingVertexExpander.java b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/canonical/DanglingVertexExpander.java deleted file mode 100644 index 89a1d98..0000000 --- a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/canonical/DanglingVertexExpander.java +++ /dev/null
@@ -1,93 +0,0 @@ -/* - * 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.graphix.lang.rewrites.canonical; - -import java.util.ArrayList; -import java.util.List; -import java.util.ListIterator; -import java.util.Set; - -import org.apache.asterix.common.exceptions.CompilationException; -import org.apache.asterix.graphix.lang.clause.GraphSelectBlock; -import org.apache.asterix.graphix.lang.clause.MatchClause; -import org.apache.asterix.graphix.lang.expression.EdgePatternExpr; -import org.apache.asterix.graphix.lang.expression.PathPatternExpr; -import org.apache.asterix.graphix.lang.expression.VertexPatternExpr; -import org.apache.asterix.graphix.lang.rewrites.visitor.GraphixDeepCopyVisitor; -import org.apache.asterix.graphix.lang.struct.ElementLabel; -import org.apache.asterix.lang.common.expression.VariableExpr; - -/** - * Expand a disconnected vertex into a set of canonical disconnected vertices. For dangling vertices, this just - * includes ensuring that each vertex has only one label. - */ -public class DanglingVertexExpander implements ICanonicalExpander<VertexPatternExpr> { - private final GraphixDeepCopyVisitor deepCopyVisitor = new GraphixDeepCopyVisitor(); - - @Override - public void apply(VertexPatternExpr vertexPatternExpr, List<GraphSelectBlock> inputSelectBlocks) - throws CompilationException { - if (vertexPatternExpr.getLabels().size() == 1) { - // No expansion is necessary. Our vertex is in canonical form. - return; - } - VariableExpr originalVariableExpr = vertexPatternExpr.getVariableExpr(); - - // We want to end up with |GSBs| * |labels| number of generated GSBs. - List<GraphSelectBlock> generatedGraphSelectBlocks = new ArrayList<>(); - for (GraphSelectBlock oldGraphSelectBlock : inputSelectBlocks) { - for (ElementLabel vertexLabel : vertexPatternExpr.getLabels()) { - GraphSelectBlock clonedSelectBlock = deepCopyVisitor.visit(oldGraphSelectBlock, null); - for (MatchClause matchClause : clonedSelectBlock.getFromGraphClause().getMatchClauses()) { - for (PathPatternExpr pathExpression : matchClause.getPathExpressions()) { - VariableExpr newVertexVariableExpr = deepCopyVisitor.visit(originalVariableExpr, null); - VertexPatternExpr newVertexPatternExpr = - new VertexPatternExpr(newVertexVariableExpr, Set.of(vertexLabel)); - replaceVertexExpression(pathExpression.getVertexExpressions(), - pathExpression.getEdgeExpressions(), vertexPatternExpr, newVertexPatternExpr); - } - } - generatedGraphSelectBlocks.add(clonedSelectBlock); - } - } - inputSelectBlocks.clear(); - inputSelectBlocks.addAll(generatedGraphSelectBlocks); - } - - private static void replaceVertexExpression(List<VertexPatternExpr> vertexExpressions, - List<EdgePatternExpr> edgeExpressions, VertexPatternExpr oldVertexExpression, - VertexPatternExpr newVertexExpression) { - ListIterator<VertexPatternExpr> vertexExpressionIterator = vertexExpressions.listIterator(); - while (vertexExpressionIterator.hasNext()) { - VertexPatternExpr workingVertexExpression = vertexExpressionIterator.next(); - if (workingVertexExpression.equals(oldVertexExpression)) { - vertexExpressionIterator.set(newVertexExpression); - break; - } - } - for (EdgePatternExpr workingEdgeExpression : edgeExpressions) { - if (workingEdgeExpression.getLeftVertex().equals(oldVertexExpression)) { - workingEdgeExpression.setLeftVertex(newVertexExpression); - - } else if (workingEdgeExpression.getRightVertex().equals(oldVertexExpression)) { - workingEdgeExpression.setRightVertex(newVertexExpression); - } - } - } -}
diff --git a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/canonical/EdgeSubPathExpander.java b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/canonical/EdgeSubPathExpander.java deleted file mode 100644 index 3210919..0000000 --- a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/canonical/EdgeSubPathExpander.java +++ /dev/null
@@ -1,304 +0,0 @@ -/* - * 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.graphix.lang.rewrites.canonical; - -import static org.apache.asterix.graphix.lang.struct.EdgeDescriptor.EdgeDirection; -import static org.apache.asterix.graphix.lang.struct.EdgeDescriptor.PatternType; - -import java.util.ArrayDeque; -import java.util.ArrayList; -import java.util.Deque; -import java.util.List; -import java.util.Set; -import java.util.function.Supplier; - -import org.apache.asterix.common.exceptions.CompilationException; -import org.apache.asterix.common.exceptions.ErrorCode; -import org.apache.asterix.graphix.lang.clause.GraphSelectBlock; -import org.apache.asterix.graphix.lang.clause.MatchClause; -import org.apache.asterix.graphix.lang.expression.EdgePatternExpr; -import org.apache.asterix.graphix.lang.expression.PathPatternExpr; -import org.apache.asterix.graphix.lang.expression.VertexPatternExpr; -import org.apache.asterix.graphix.lang.rewrites.lower.action.PathPatternAction; -import org.apache.asterix.graphix.lang.rewrites.resolve.SchemaKnowledgeTable; -import org.apache.asterix.graphix.lang.rewrites.visitor.GraphixDeepCopyVisitor; -import org.apache.asterix.graphix.lang.struct.EdgeDescriptor; -import org.apache.asterix.graphix.lang.struct.ElementLabel; -import org.apache.asterix.lang.common.clause.LetClause; -import org.apache.asterix.lang.common.expression.RecordConstructor; -import org.apache.asterix.lang.common.expression.VariableExpr; -import org.apache.asterix.lang.common.struct.VarIdentifier; -import org.apache.hyracks.algebricks.common.utils.Pair; - -/** - * Expand an edge and its connected vertices into an edge in canonical form. - * 1. An edge's left vertex must only have one label. - * 2. An edge's right vertex must only have one label. - * 3. An edge must only have one label. - * 4. An edge must be directed. - * 5. An edge must not be a sub-path. - */ -public class EdgeSubPathExpander implements ICanonicalExpander<EdgePatternExpr> { - private final Supplier<VariableExpr> newVariableSupplier; - private final GraphixDeepCopyVisitor deepCopyVisitor; - - // To avoid "over-expanding", we need to keep knowledge about our FROM-GRAPH-CLAUSE. - private SchemaKnowledgeTable schemaKnowledgeTable; - - public EdgeSubPathExpander(Supplier<VarIdentifier> getNewVariable) { - this.deepCopyVisitor = new GraphixDeepCopyVisitor(); - this.newVariableSupplier = () -> new VariableExpr(getNewVariable.get()); - } - - public void resetSchema(SchemaKnowledgeTable schemaKnowledgeTable) { - this.schemaKnowledgeTable = schemaKnowledgeTable; - } - - private static class CanonicalEdgeContext { - private final VariableExpr leftVertexVar; - private final VariableExpr rightVertexVar; - private final VariableExpr edgeVar; - - private final ElementLabel leftVertexLabel; - private final ElementLabel rightVertexLabel; - private final ElementLabel edgeLabel; - - private final EdgeDirection edgeDirection; - - private CanonicalEdgeContext(VariableExpr leftVertexVar, VariableExpr rightVertexVar, VariableExpr edgeVar, - ElementLabel leftVertexLabel, ElementLabel rightVertexLabel, ElementLabel edgeLabel, - EdgeDirection edgeDirection) { - this.leftVertexVar = leftVertexVar; - this.rightVertexVar = rightVertexVar; - this.leftVertexLabel = leftVertexLabel; - this.rightVertexLabel = rightVertexLabel; - this.edgeDirection = edgeDirection; - this.edgeVar = edgeVar; - this.edgeLabel = edgeLabel; - } - } - - @Override - public void apply(EdgePatternExpr edgePatternExpr, List<GraphSelectBlock> inputSelectBlocks) - throws CompilationException { - boolean doesLeftVertexRequireExpansion = edgePatternExpr.getLeftVertex().getLabels().size() > 1; - boolean doesRightVertexRequireExpansion = edgePatternExpr.getRightVertex().getLabels().size() > 1; - boolean doesEdgeRequireExpansion = edgePatternExpr.getEdgeDescriptor().getEdgeLabels().size() > 1 - || edgePatternExpr.getEdgeDescriptor().getPatternType() != PatternType.EDGE - || edgePatternExpr.getEdgeDescriptor().getEdgeDirection() == EdgeDirection.UNDIRECTED; - if (!doesLeftVertexRequireExpansion && !doesRightVertexRequireExpansion && !doesEdgeRequireExpansion) { - // Our edge and both of our vertices are in canonical form. - return; - } - - // Expand all possibilities that our edge pattern could represent. - EdgeDescriptor edgeDescriptor = edgePatternExpr.getEdgeDescriptor(); - VertexPatternExpr workingLeftVertex = edgePatternExpr.getLeftVertex(); - VertexPatternExpr workingRightVertex = edgePatternExpr.getRightVertex(); - List<List<CanonicalEdgeContext>> pathCandidates = new ArrayList<>(); - if (edgeDescriptor.getPatternType() == PatternType.EDGE) { - expandEdgePattern(workingLeftVertex, workingRightVertex, edgeDescriptor, edgeDescriptor::getVariableExpr) - .forEach(e -> pathCandidates.add(List.of(e))); - - } else { // edgeDescriptor.getPatternType() == EdgeDescriptor.PatternType.PATH - Deque<List<CanonicalEdgeContext>> pathCandidateStack = new ArrayDeque<>(List.of(List.of())); - for (int i = 0; i < edgeDescriptor.getMaximumHops(); i++) { - VertexPatternExpr nextInternalVertex = (i != edgeDescriptor.getMaximumHops() - 1) - ? edgePatternExpr.getInternalVertices().get(i) : edgePatternExpr.getRightVertex(); - - // We will only yield a path if we have generated the minimum number of hops. - Deque<List<CanonicalEdgeContext>> generatedCandidateStack = new ArrayDeque<>(); - boolean isYieldPath = i >= edgeDescriptor.getMinimumHops() - 1; - while (!pathCandidateStack.isEmpty()) { - List<CanonicalEdgeContext> pathCandidate = pathCandidateStack.removeLast(); - if (isYieldPath) { - VertexPatternExpr rightVertex = edgePatternExpr.getRightVertex(); - expandEdgePattern(workingLeftVertex, rightVertex, edgeDescriptor, newVariableSupplier) - .forEach(e -> { - List<CanonicalEdgeContext> pathCopy = new ArrayList<>(pathCandidate); - pathCopy.add(e); - pathCandidates.add(pathCopy); - }); - } - expandEdgePattern(workingLeftVertex, nextInternalVertex, edgeDescriptor, newVariableSupplier) - .forEach(e -> { - List<CanonicalEdgeContext> pathCopy = new ArrayList<>(pathCandidate); - pathCopy.add(e); - generatedCandidateStack.addLast(pathCopy); - }); - } - pathCandidateStack.addAll(generatedCandidateStack); - - // Move our vertex cursor. - workingLeftVertex = nextInternalVertex; - } - } - - // We must now prune all paths that contain an edge that does not adhere to our schema. - pathCandidates.removeIf(this::isPathCandidateInvalid); - - // Perform the expansion into GRAPH-SELECT-BLOCKs. - List<GraphSelectBlock> selectBlockList = expandSelectBlocks(edgePatternExpr, inputSelectBlocks, pathCandidates); - inputSelectBlocks.clear(); - inputSelectBlocks.addAll(selectBlockList); - } - - private List<GraphSelectBlock> expandSelectBlocks(EdgePatternExpr edgePatternExpr, - List<GraphSelectBlock> inputSelectBlocks, List<List<CanonicalEdgeContext>> pathCandidates) - throws CompilationException { - List<GraphSelectBlock> generatedGraphSelectBlocks = new ArrayList<>(); - for (GraphSelectBlock oldGraphSelectBlock : inputSelectBlocks) { - for (List<CanonicalEdgeContext> pathCandidate : pathCandidates) { - GraphSelectBlock clonedSelectBlock = deepCopyVisitor.visit(oldGraphSelectBlock, null); - for (MatchClause matchClause : clonedSelectBlock.getFromGraphClause().getMatchClauses()) { - Pair<PathPatternExpr, PathPatternExpr> pathPatternReplacePair = null; - - // We are only interested in the path that contains our already expanded edge. - for (PathPatternExpr pathPatternExpr : matchClause.getPathExpressions()) { - List<EdgePatternExpr> edgeExpressions = pathPatternExpr.getEdgeExpressions(); - int edgeExprIndex = edgeExpressions.indexOf(edgePatternExpr); - if (edgeExprIndex < 0) { - continue; - } - - // Note: in this case, we do not need to worry about dangling vertices. - List<LetClause> reboundSubPathList = new ArrayList<>(pathPatternExpr.getReboundSubPathList()); - List<EdgePatternExpr> newEdgePatternExprList = new ArrayList<>(); - List<VertexPatternExpr> newVertexPatternExprList = new ArrayList<>(); - for (int i = 0; i < edgeExpressions.size(); i++) { - EdgePatternExpr existingEdgeExpression = edgeExpressions.get(i); - if (i != edgeExprIndex) { - newEdgePatternExprList.add(existingEdgeExpression); - newVertexPatternExprList.add(existingEdgeExpression.getLeftVertex()); - newVertexPatternExprList.add(existingEdgeExpression.getRightVertex()); - continue; - } - - // Note: this will produce duplicate vertex expressions. - List<VertexPatternExpr> subPathVertexList = new ArrayList<>(); - List<EdgePatternExpr> subPathEdgeList = new ArrayList<>(); - for (CanonicalEdgeContext edgeContext : pathCandidate) { - VariableExpr newEdgeVar = deepCopyVisitor.visit(edgeContext.edgeVar, null); - VariableExpr newLeftVar = deepCopyVisitor.visit(edgeContext.leftVertexVar, null); - VariableExpr newRightVar = deepCopyVisitor.visit(edgeContext.rightVertexVar, null); - EdgeDescriptor newDescriptor = new EdgeDescriptor(edgeContext.edgeDirection, - PatternType.EDGE, Set.of(edgeContext.edgeLabel), newEdgeVar, 1, 1); - Set<ElementLabel> leftLabelSingleton = Set.of(edgeContext.leftVertexLabel); - Set<ElementLabel> rightLabelSingleton = Set.of(edgeContext.rightVertexLabel); - VertexPatternExpr leftVertex = new VertexPatternExpr(newLeftVar, leftLabelSingleton); - VertexPatternExpr rightVertex = new VertexPatternExpr(newRightVar, rightLabelSingleton); - EdgePatternExpr newEdge = new EdgePatternExpr(leftVertex, rightVertex, newDescriptor); - - // Update our path and our sub-path. - newEdgePatternExprList.add(newEdge); - newVertexPatternExprList.add(leftVertex); - newVertexPatternExprList.add(rightVertex); - subPathEdgeList.add(newEdge); - subPathVertexList.add(leftVertex); - subPathVertexList.add(rightVertex); - } - - // Bind our sub-path to a path record. - if (existingEdgeExpression.getEdgeDescriptor().getPatternType() == PatternType.PATH) { - RecordConstructor pathRecord = new RecordConstructor(); - EdgeDescriptor edgeDescriptor = existingEdgeExpression.getEdgeDescriptor(); - PathPatternAction.buildPathRecord(subPathVertexList, subPathEdgeList, pathRecord); - reboundSubPathList.add(new LetClause(edgeDescriptor.getVariableExpr(), pathRecord)); - } - } - - // Build a new path pattern to replace our old path pattern. - VariableExpr newPathVariable = null; - if (pathPatternExpr.getVariableExpr() != null) { - newPathVariable = deepCopyVisitor.visit(pathPatternExpr.getVariableExpr(), null); - } - PathPatternExpr newPathPatternExpr = - new PathPatternExpr(newVertexPatternExprList, newEdgePatternExprList, newPathVariable); - newPathPatternExpr.getReboundSubPathList().addAll(reboundSubPathList); - pathPatternReplacePair = new Pair<>(pathPatternExpr, newPathPatternExpr); - } - - // Finalize our expansion by replacing our path in our MATCH-CLAUSE. - if (pathPatternReplacePair == null) { - throw new CompilationException(ErrorCode.COMPILATION_ILLEGAL_STATE, - "Edge was expanded, but edge was not found in given SELECT-BLOCK!"); - } - PathPatternExpr oldPathPattern = pathPatternReplacePair.first; - PathPatternExpr newPathPattern = pathPatternReplacePair.second; - matchClause.getPathExpressions().replaceAll(p -> (p == oldPathPattern) ? newPathPattern : p); - } - generatedGraphSelectBlocks.add(clonedSelectBlock); - } - } - return generatedGraphSelectBlocks; - } - - private List<CanonicalEdgeContext> expandEdgePattern(VertexPatternExpr leftVertex, VertexPatternExpr rightVertex, - EdgeDescriptor edgeDescriptor, Supplier<VariableExpr> edgeVarSupplier) throws CompilationException { - List<CanonicalEdgeContext> edgeCandidates = new ArrayList<>(); - for (ElementLabel leftVertexLabel : leftVertex.getLabels()) { - for (ElementLabel rightVertexLabel : rightVertex.getLabels()) { - for (ElementLabel edgeLabel : edgeDescriptor.getEdgeLabels()) { - if (edgeDescriptor.getEdgeDirection() != EdgeDirection.LEFT_TO_RIGHT) { - VariableExpr edgeVar = deepCopyVisitor.visit(edgeVarSupplier.get(), null); - VariableExpr leftVertexVar = deepCopyVisitor.visit(leftVertex.getVariableExpr(), null); - VariableExpr rightVertexVar = deepCopyVisitor.visit(rightVertex.getVariableExpr(), null); - CanonicalEdgeContext rightToLeftContext = - new CanonicalEdgeContext(leftVertexVar, rightVertexVar, edgeVar, leftVertexLabel, - rightVertexLabel, edgeLabel, EdgeDirection.RIGHT_TO_LEFT); - edgeCandidates.add(rightToLeftContext); - } - if (edgeDescriptor.getEdgeDirection() != EdgeDirection.RIGHT_TO_LEFT) { - VariableExpr edgeVar = deepCopyVisitor.visit(edgeVarSupplier.get(), null); - VariableExpr leftVertexVar = deepCopyVisitor.visit(leftVertex.getVariableExpr(), null); - VariableExpr rightVertexVar = deepCopyVisitor.visit(rightVertex.getVariableExpr(), null); - CanonicalEdgeContext leftToRightContext = - new CanonicalEdgeContext(leftVertexVar, rightVertexVar, edgeVar, leftVertexLabel, - rightVertexLabel, edgeLabel, EdgeDirection.LEFT_TO_RIGHT); - edgeCandidates.add(leftToRightContext); - } - } - } - } - return edgeCandidates; - } - - private boolean isPathCandidateInvalid(List<CanonicalEdgeContext> pathCandidate) { - for (CanonicalEdgeContext edgeCandidate : pathCandidate) { - for (SchemaKnowledgeTable.KnowledgeRecord knowledgeRecord : schemaKnowledgeTable) { - if (edgeCandidate.edgeDirection == EdgeDirection.LEFT_TO_RIGHT) { - if (edgeCandidate.leftVertexLabel.equals(knowledgeRecord.getSourceVertexLabel()) - && edgeCandidate.edgeLabel.equals(knowledgeRecord.getEdgeLabel()) - && edgeCandidate.rightVertexLabel.equals(knowledgeRecord.getDestVertexLabel())) { - return false; - } - - } else { // edgeCandidate.edgeDirection == EdgeDescriptor.EdgeDirection.RIGHT_TO_LEFT - if (edgeCandidate.leftVertexLabel.equals(knowledgeRecord.getDestVertexLabel()) - && edgeCandidate.edgeLabel.equals(knowledgeRecord.getEdgeLabel()) - && edgeCandidate.rightVertexLabel.equals(knowledgeRecord.getSourceVertexLabel())) { - return false; - } - } - } - return true; - } - return true; - } -}
diff --git a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/common/EdgeDependencyGraph.java b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/common/EdgeDependencyGraph.java deleted file mode 100644 index 25ec13b..0000000 --- a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/common/EdgeDependencyGraph.java +++ /dev/null
@@ -1,131 +0,0 @@ -/* - * 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.graphix.lang.rewrites.common; - -import java.util.ArrayDeque; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Deque; -import java.util.HashSet; -import java.util.Iterator; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import org.apache.asterix.graphix.lang.expression.EdgePatternExpr; -import org.apache.asterix.lang.common.struct.Identifier; - -/** - * A two-level ordered list of {@link EdgePatternExpr} instances, whose order depends on the input edge-dependency - * graphs (represented as an adjacency list). Ideally, we want to return the smallest set of Hamilton - * {@link EdgePatternExpr} paths, but this is an NP-hard problem. Instead, we find the **first** set of paths that - * visit each {@link EdgePatternExpr} once. This inner-order is dependent on the order of the adjacency map iterators. - */ -public class EdgeDependencyGraph implements Iterable<Iterable<EdgePatternExpr>> { - private final List<Map<Identifier, List<Identifier>>> adjacencyMaps; - private final Map<Identifier, EdgePatternExpr> edgePatternMap; - - public EdgeDependencyGraph(List<Map<Identifier, List<Identifier>>> adjacencyMaps, - Map<Identifier, EdgePatternExpr> edgePatternMap) { - this.adjacencyMaps = adjacencyMaps; - this.edgePatternMap = edgePatternMap; - } - - @Override - public Iterator<Iterable<EdgePatternExpr>> iterator() { - List<Iterable<EdgePatternExpr>> edgePatternIterables = new ArrayList<>(); - Set<Identifier> globalVisitedSet = new HashSet<>(); - - for (Map<Identifier, List<Identifier>> adjacencyMap : adjacencyMaps) { - Deque<Identifier> edgeStack = new ArrayDeque<>(); - Set<Identifier> localVisitedSet = new HashSet<>(); - - if (!adjacencyMap.entrySet().isEmpty()) { - if (globalVisitedSet.isEmpty()) { - // We start with the first inserted edge inserted into our graph, and continue from there. - Iterator<Map.Entry<Identifier, List<Identifier>>> mapIterator = adjacencyMap.entrySet().iterator(); - Map.Entry<Identifier, List<Identifier>> seedEdge = mapIterator.next(); - edgeStack.addFirst(seedEdge.getKey()); - edgeStack.addAll(seedEdge.getValue()); - while (mapIterator.hasNext()) { - Map.Entry<Identifier, List<Identifier>> followingEdge = mapIterator.next(); - for (Identifier followingEdgeDependent : followingEdge.getValue()) { - if (!edgeStack.contains(followingEdgeDependent)) { - edgeStack.addLast(followingEdgeDependent); - } - } - } - - } else { - // This is not our first pass. Find a connecting edge to seed our stack. - Set<Identifier> disconnectedExploredEdgeSet = new LinkedHashSet<>(); - for (Map.Entry<Identifier, List<Identifier>> workingEdge : adjacencyMap.entrySet()) { - for (Identifier workingEdgeDependent : workingEdge.getValue()) { - if (globalVisitedSet.contains(workingEdgeDependent) && edgeStack.isEmpty()) { - edgeStack.addFirst(workingEdgeDependent); - localVisitedSet.add(workingEdgeDependent); - - } else if (!globalVisitedSet.contains(workingEdgeDependent)) { - disconnectedExploredEdgeSet.add(workingEdgeDependent); - - } else { - localVisitedSet.add(workingEdgeDependent); - } - } - if (workingEdge.getValue().isEmpty() && globalVisitedSet.contains(workingEdge.getKey())) { - localVisitedSet.add(workingEdge.getKey()); - - } else if (workingEdge.getValue().isEmpty()) { - disconnectedExploredEdgeSet.add(workingEdge.getKey()); - } - } - disconnectedExploredEdgeSet.forEach(edgeStack::addLast); - } - - // Build our iterable. - globalVisitedSet.addAll(edgeStack); - edgePatternIterables.add(() -> new Iterator<>() { - @Override - public boolean hasNext() { - // We are done once we have visited every node. - return localVisitedSet.size() != adjacencyMap.size(); - } - - @Override - public EdgePatternExpr next() { - Identifier edgeIdentifier = edgeStack.removeFirst(); - for (Identifier dependency : adjacencyMap.get(edgeIdentifier)) { - if (!localVisitedSet.contains(dependency)) { - edgeStack.addFirst(dependency); - } - } - localVisitedSet.add(edgeIdentifier); - return edgePatternMap.get(edgeIdentifier); - } - }); - - } else { - edgePatternIterables.add(Collections.emptyList()); - } - } - - return edgePatternIterables.iterator(); - } -}
diff --git a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/common/ElementLookupTable.java b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/common/ElementLookupTable.java deleted file mode 100644 index cfa4aae..0000000 --- a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/common/ElementLookupTable.java +++ /dev/null
@@ -1,73 +0,0 @@ -/* - * 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.graphix.lang.rewrites.common; - -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; -import java.util.Map; - -import org.apache.asterix.graphix.common.metadata.GraphElementIdentifier; -import org.apache.asterix.graphix.lang.statement.GraphElementDeclaration; - -/** - * Lookup table for {@link GraphElementDeclaration} instances, vertex keys, edge destination keys, and edge source - * keys-- indexed by {@link GraphElementIdentifier} instances. - */ -public class ElementLookupTable implements Iterable<GraphElementDeclaration> { - private final Map<GraphElementIdentifier, GraphElementDeclaration> graphElementDeclMap = new HashMap<>(); - private final Map<GraphElementIdentifier, List<List<String>>> vertexKeyMap = new HashMap<>(); - private final Map<GraphElementIdentifier, List<List<String>>> edgeDestKeysMap = new HashMap<>(); - private final Map<GraphElementIdentifier, List<List<String>>> edgeSourceKeysMap = new HashMap<>(); - - public void put(GraphElementIdentifier identifier, GraphElementDeclaration graphElementDeclaration) { - graphElementDeclMap.put(identifier, graphElementDeclaration); - } - - public void putVertexKey(GraphElementIdentifier identifier, List<List<String>> primaryKey) { - vertexKeyMap.put(identifier, primaryKey); - } - - public void putEdgeKeys(GraphElementIdentifier identifier, List<List<String>> sourceKey, - List<List<String>> destinationKey) { - edgeSourceKeysMap.put(identifier, sourceKey); - edgeDestKeysMap.put(identifier, destinationKey); - } - - public GraphElementDeclaration getElementDecl(GraphElementIdentifier identifier) { - return graphElementDeclMap.get(identifier); - } - - public List<List<String>> getVertexKey(GraphElementIdentifier identifier) { - return vertexKeyMap.get(identifier); - } - - public List<List<String>> getEdgeDestKey(GraphElementIdentifier identifier) { - return edgeDestKeysMap.get(identifier); - } - - public List<List<String>> getEdgeSourceKey(GraphElementIdentifier identifier) { - return edgeSourceKeysMap.get(identifier); - } - - @Override - public Iterator<GraphElementDeclaration> iterator() { - return graphElementDeclMap.values().iterator(); - } -}
diff --git a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/lower/EnvironmentActionFactory.java b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/lower/EnvironmentActionFactory.java deleted file mode 100644 index ebb1406..0000000 --- a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/lower/EnvironmentActionFactory.java +++ /dev/null
@@ -1,537 +0,0 @@ -/* - * 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.graphix.lang.rewrites.lower; - -import static org.apache.asterix.graphix.lang.rewrites.util.LowerRewritingUtil.buildAccessorList; -import static org.apache.asterix.graphix.lang.rewrites.util.LowerRewritingUtil.buildVertexEdgeJoin; - -import java.util.List; -import java.util.Map; -import java.util.function.Function; - -import org.apache.asterix.common.exceptions.CompilationException; -import org.apache.asterix.common.exceptions.ErrorCode; -import org.apache.asterix.graphix.common.metadata.GraphElementIdentifier; -import org.apache.asterix.graphix.common.metadata.GraphIdentifier; -import org.apache.asterix.graphix.lang.clause.CorrLetClause; -import org.apache.asterix.graphix.lang.clause.CorrWhereClause; -import org.apache.asterix.graphix.lang.clause.FromGraphClause; -import org.apache.asterix.graphix.lang.expression.EdgePatternExpr; -import org.apache.asterix.graphix.lang.expression.PathPatternExpr; -import org.apache.asterix.graphix.lang.expression.VertexPatternExpr; -import org.apache.asterix.graphix.lang.rewrites.GraphixRewritingContext; -import org.apache.asterix.graphix.lang.rewrites.common.ElementLookupTable; -import org.apache.asterix.graphix.lang.rewrites.lower.action.AbstractInlineAction; -import org.apache.asterix.graphix.lang.rewrites.lower.action.IEnvironmentAction; -import org.apache.asterix.graphix.lang.rewrites.lower.action.IsomorphismAction; -import org.apache.asterix.graphix.lang.rewrites.lower.action.PathPatternAction; -import org.apache.asterix.graphix.lang.rewrites.visitor.ElementBodyAnalysisVisitor.ElementBodyAnalysisContext; -import org.apache.asterix.graphix.lang.statement.GraphElementDeclaration; -import org.apache.asterix.graphix.lang.struct.EdgeDescriptor; -import org.apache.asterix.lang.common.base.Expression; -import org.apache.asterix.lang.common.expression.CallExpr; -import org.apache.asterix.lang.common.expression.LiteralExpr; -import org.apache.asterix.lang.common.expression.RecordConstructor; -import org.apache.asterix.lang.common.expression.VariableExpr; -import org.apache.asterix.lang.common.literal.TrueLiteral; -import org.apache.asterix.lang.common.struct.VarIdentifier; -import org.apache.asterix.lang.sqlpp.clause.JoinClause; -import org.apache.asterix.lang.sqlpp.optype.JoinType; - -/** - * Build {@link IEnvironmentAction} instances to manipulate a {@link LoweringEnvironment}. - */ -public class EnvironmentActionFactory { - private final Map<GraphElementIdentifier, ElementBodyAnalysisContext> analysisContextMap; - private final ElementLookupTable elementLookupTable; - private final LoweringAliasLookupTable aliasLookupTable; - private final GraphixRewritingContext graphixRewritingContext; - - // The following must be provided before any creation methods are used. - private GraphIdentifier graphIdentifier; - - public EnvironmentActionFactory(Map<GraphElementIdentifier, ElementBodyAnalysisContext> analysisContextMap, - ElementLookupTable elementLookupTable, LoweringAliasLookupTable aliasLookupTable, - GraphixRewritingContext graphixRewritingContext) { - this.analysisContextMap = analysisContextMap; - this.elementLookupTable = elementLookupTable; - this.aliasLookupTable = aliasLookupTable; - this.graphixRewritingContext = graphixRewritingContext; - } - - public void reset(GraphIdentifier graphIdentifier) { - this.graphIdentifier = graphIdentifier; - this.aliasLookupTable.reset(); - } - - /** - * @see PathPatternAction - */ - public IEnvironmentAction buildPathPatternAction(PathPatternExpr pathPatternExpr) { - return new PathPatternAction(pathPatternExpr); - } - - /** - * @see IsomorphismAction - */ - public IEnvironmentAction buildIsomorphismAction(FromGraphClause fromGraphClause) throws CompilationException { - return new IsomorphismAction(graphixRewritingContext, fromGraphClause, aliasLookupTable); - } - - /** - * Build an {@link IEnvironmentAction} to handle a dangling vertex / vertex that is (currently) disconnected. - * Even though we introduce CROSS-JOINs here, we will not actually perform this CROSS-JOIN if this is the first - * vertex we are lowering. There are three possible {@link IEnvironmentAction}s generated here: - * 1. An action for inlined vertices that have no projections. - * 2. An action for inlined vertices with projections. - * 3. An action for non-inlined vertices. - */ - public IEnvironmentAction buildDanglingVertexAction(VertexPatternExpr vertexPatternExpr) - throws CompilationException { - VarIdentifier vertexVar = vertexPatternExpr.getVariableExpr().getVar(); - VarIdentifier iterationVar = graphixRewritingContext.getNewGraphixVariable(); - VarIdentifier intermediateVar = graphixRewritingContext.getNewGraphixVariable(); - - // We should only be working with one identifier (given that we only have one label). - List<GraphElementIdentifier> vertexElementIDs = vertexPatternExpr.generateIdentifiers(graphIdentifier); - if (vertexElementIDs.size() != 1) { - throw new CompilationException(ErrorCode.COMPILATION_ILLEGAL_STATE, "Found non-canonical vertex pattern!"); - } - GraphElementIdentifier vertexIdentifier = vertexElementIDs.get(0); - ElementBodyAnalysisContext vertexAnalysisContext = analysisContextMap.get(vertexIdentifier); - if (vertexAnalysisContext.isExpressionInline() && vertexAnalysisContext.isSelectClauseInline()) { - return new AbstractInlineAction(graphixRewritingContext, vertexAnalysisContext, iterationVar) { - @Override - public void apply(LoweringEnvironment loweringEnvironment) throws CompilationException { - // Introduce our iteration expression. - loweringEnvironment.acceptTransformer(clauseSequence -> { - CallExpr datasetCallExpression = vertexAnalysisContext.getDatasetCallExpression(); - JoinClause joinClause = new JoinClause(JoinType.INNER, datasetCallExpression, - new VariableExpr(iterationVar), null, new LiteralExpr(TrueLiteral.INSTANCE), null); - clauseSequence.addMainClause(joinClause); - }); - - // Inline our vertex body. - super.apply(loweringEnvironment); - - // Bind our intermediate (join) variable and vertex variable. - loweringEnvironment.acceptTransformer(clauseSequence -> { - VariableExpr iterationVarExpr = new VariableExpr(iterationVar); - VariableExpr intermediateVarExpr = new VariableExpr(intermediateVar); - clauseSequence.addMainClause(new CorrLetClause(iterationVarExpr, intermediateVarExpr, null)); - clauseSequence.addRepresentativeVertexBinding(vertexVar, new VariableExpr(iterationVar)); - }); - aliasLookupTable.putIterationAlias(vertexVar, iterationVar); - aliasLookupTable.putJoinAlias(vertexVar, intermediateVar); - } - }; - - } else if (vertexAnalysisContext.isExpressionInline()) { - return new AbstractInlineAction(graphixRewritingContext, vertexAnalysisContext, iterationVar) { - @Override - public void apply(LoweringEnvironment loweringEnvironment) throws CompilationException { - // Introduce our iteration expression. - loweringEnvironment.acceptTransformer(clauseSequence -> { - CallExpr datasetCallExpression = vertexAnalysisContext.getDatasetCallExpression(); - JoinClause joinClause = new JoinClause(JoinType.INNER, datasetCallExpression, - new VariableExpr(iterationVar), null, new LiteralExpr(TrueLiteral.INSTANCE), null); - clauseSequence.addMainClause(joinClause); - }); - - // Inline our vertex body. - super.apply(loweringEnvironment); - - // Build a record constructor from our context to bind to our vertex variable. - loweringEnvironment.acceptTransformer(clauseSequence -> { - VariableExpr intermediateVarExpr = new VariableExpr(intermediateVar); - RecordConstructor recordConstructor1 = buildRecordConstructor(); - RecordConstructor recordConstructor2 = buildRecordConstructor(); - clauseSequence.addMainClause(new CorrLetClause(recordConstructor1, intermediateVarExpr, null)); - clauseSequence.addRepresentativeVertexBinding(vertexVar, recordConstructor2); - }); - aliasLookupTable.putIterationAlias(vertexVar, iterationVar); - aliasLookupTable.putJoinAlias(vertexVar, intermediateVar); - } - }; - - } else { - GraphElementDeclaration elementDeclaration = elementLookupTable.getElementDecl(vertexIdentifier); - return loweringEnvironment -> { - // Introduce our iteration expression. - loweringEnvironment.acceptTransformer(clauseSequence -> { - JoinClause joinClause = new JoinClause(JoinType.INNER, elementDeclaration.getNormalizedBody(), - new VariableExpr(iterationVar), null, new LiteralExpr(TrueLiteral.INSTANCE), null); - clauseSequence.addMainClause(joinClause); - }); - - // Bind our intermediate (join) variable and vertex variable. - loweringEnvironment.acceptTransformer(clauseSequence -> { - VariableExpr iterationVarExpr = new VariableExpr(iterationVar); - VariableExpr intermediateVarExpr = new VariableExpr(intermediateVar); - clauseSequence.addMainClause(new CorrLetClause(iterationVarExpr, intermediateVarExpr, null)); - clauseSequence.addRepresentativeVertexBinding(vertexVar, new VariableExpr(iterationVar)); - }); - }; - } - } - - /** - * Build an {@link IEnvironmentAction} to handle an edge that we can fold into (attach from) an already introduced - * vertex. A folded edge is implicitly inlined. There are two possible {@link IEnvironmentAction}s generated here: - * 1. An action for inlined, folded edges that have no projections. - * 2. An action for inlined, folded edges that have projections. - */ - public IEnvironmentAction buildFoldedEdgeAction(VertexPatternExpr vertexPatternExpr, - EdgePatternExpr edgePatternExpr) throws CompilationException { - VarIdentifier vertexVar = vertexPatternExpr.getVariableExpr().getVar(); - VarIdentifier edgeVar = edgePatternExpr.getEdgeDescriptor().getVariableExpr().getVar(); - VarIdentifier intermediateVar = graphixRewritingContext.getNewGraphixVariable(); - - // We should only be working with one identifier (given that we only have one label). - EdgeDescriptor edgeDescriptor = edgePatternExpr.getEdgeDescriptor(); - List<GraphElementIdentifier> edgeElementIDs = edgeDescriptor.generateIdentifiers(graphIdentifier); - if (edgeElementIDs.size() != 1) { - throw new CompilationException(ErrorCode.COMPILATION_ILLEGAL_STATE, "Found non-canonical edge pattern!"); - } - GraphElementIdentifier edgeIdentifier = edgeElementIDs.get(0); - ElementBodyAnalysisContext edgeAnalysisContext = analysisContextMap.get(edgeIdentifier); - if (edgeAnalysisContext.isSelectClauseInline()) { - return new AbstractInlineAction(graphixRewritingContext, edgeAnalysisContext, null) { - @Override - public void apply(LoweringEnvironment loweringEnvironment) throws CompilationException { - // We want to bind directly to the iteration variable of our vertex, not the join variable. - elementVariable = aliasLookupTable.getIterationAlias(vertexVar); - - // Inline our edge body. - super.apply(loweringEnvironment); - - // Build a binding for our edge variable. - loweringEnvironment.acceptTransformer(clauseSequence -> { - VariableExpr elementVarExpr = new VariableExpr(elementVariable); - VariableExpr intermediateVarExpr = new VariableExpr(intermediateVar); - clauseSequence.addMainClause(new CorrLetClause(elementVarExpr, intermediateVarExpr, null)); - clauseSequence.addRepresentativeEdgeBinding(edgeVar, new VariableExpr(elementVariable)); - }); - aliasLookupTable.putIterationAlias(edgeVar, elementVariable); - aliasLookupTable.putJoinAlias(edgeVar, intermediateVar); - } - }; - - } else { - return new AbstractInlineAction(graphixRewritingContext, edgeAnalysisContext, null) { - @Override - public void apply(LoweringEnvironment loweringEnvironment) throws CompilationException { - // We want to bind directly to the iteration variable of our vertex, not the join variable. - elementVariable = aliasLookupTable.getIterationAlias(vertexVar); - - // Inline our edge body. - super.apply(loweringEnvironment); - - // Build a record constructor from our context to bind to our edge and intermediate (join) var. - loweringEnvironment.acceptTransformer(clauseSequence -> { - VariableExpr intermediateVarExpr = new VariableExpr(intermediateVar); - RecordConstructor recordConstructor1 = buildRecordConstructor(); - RecordConstructor recordConstructor2 = buildRecordConstructor(); - clauseSequence.addMainClause(new CorrLetClause(recordConstructor1, intermediateVarExpr, null)); - clauseSequence.addRepresentativeEdgeBinding(edgeVar, recordConstructor2); - }); - aliasLookupTable.putIterationAlias(edgeVar, elementVariable); - aliasLookupTable.putJoinAlias(edgeVar, intermediateVar); - } - }; - } - } - - /** - * Build an {@link IEnvironmentAction} to handle an edge that we cannot fold into an already introduced vertex. - * There are three possible {@link IEnvironmentAction}s generated here: - * 1. An action for inlined edges that have no projections. - * 2. An action for inlined edges that have projections. - * 3. An action for non-inlined edges. - */ - public IEnvironmentAction buildNonFoldedEdgeAction(VertexPatternExpr vertexPatternExpr, - EdgePatternExpr edgePatternExpr, Function<GraphElementIdentifier, List<List<String>>> edgeKeyAccess) - throws CompilationException { - VarIdentifier edgeVar = edgePatternExpr.getEdgeDescriptor().getVariableExpr().getVar(); - VarIdentifier vertexVar = vertexPatternExpr.getVariableExpr().getVar(); - VarIdentifier iterationVar = graphixRewritingContext.getNewGraphixVariable(); - VarIdentifier intermediateVar = graphixRewritingContext.getNewGraphixVariable(); - - // We should only be working with one edge identifier... - EdgeDescriptor edgeDescriptor = edgePatternExpr.getEdgeDescriptor(); - List<GraphElementIdentifier> edgeElementIDs = edgeDescriptor.generateIdentifiers(graphIdentifier); - if (edgeElementIDs.size() != 1) { - throw new CompilationException(ErrorCode.COMPILATION_ILLEGAL_STATE, "Found non-canonical edge pattern!"); - } - GraphElementIdentifier edgeIdentifier = edgeElementIDs.get(0); - ElementBodyAnalysisContext edgeAnalysisContext = analysisContextMap.get(edgeIdentifier); - Expression datasetCallExpression = edgeAnalysisContext.getDatasetCallExpression(); - - // ...and only one vertex identifier (given that we only have one label). - List<GraphElementIdentifier> vertexElementIDs = vertexPatternExpr.generateIdentifiers(graphIdentifier); - if (vertexElementIDs.size() != 1) { - throw new CompilationException(ErrorCode.COMPILATION_ILLEGAL_STATE, "Found non-canonical vertex pattern!"); - } - GraphElementIdentifier vertexIdentifier = vertexElementIDs.get(0); - if (edgeAnalysisContext.isExpressionInline() && edgeAnalysisContext.isSelectClauseInline()) { - return new AbstractInlineAction(graphixRewritingContext, edgeAnalysisContext, iterationVar) { - @Override - public void apply(LoweringEnvironment loweringEnvironment) throws CompilationException { - // Join our edge iteration variable to our vertex variable. - loweringEnvironment.acceptTransformer(clauseSequence -> { - VariableExpr vertexJoinExpr = new VariableExpr(aliasLookupTable.getJoinAlias(vertexVar)); - Expression vertexEdgeJoin = buildVertexEdgeJoin( - buildAccessorList(vertexJoinExpr, elementLookupTable.getVertexKey(vertexIdentifier)), - buildAccessorList(new VariableExpr(iterationVar), edgeKeyAccess.apply(edgeIdentifier))); - JoinClause joinClause = new JoinClause(JoinType.INNER, datasetCallExpression, - new VariableExpr(iterationVar), null, vertexEdgeJoin, null); - clauseSequence.addMainClause(joinClause); - }); - - // Inline our edge body. - super.apply(loweringEnvironment); - - // Bind our intermediate (join) variable and edge variable. - loweringEnvironment.acceptTransformer(clauseSequence -> { - VariableExpr iterationVarExpr = new VariableExpr(iterationVar); - VariableExpr intermediateVarExpr = new VariableExpr(intermediateVar); - clauseSequence.addMainClause(new CorrLetClause(iterationVarExpr, intermediateVarExpr, null)); - clauseSequence.addRepresentativeEdgeBinding(edgeVar, new VariableExpr(iterationVar)); - }); - aliasLookupTable.putIterationAlias(edgeVar, iterationVar); - aliasLookupTable.putJoinAlias(edgeVar, intermediateVar); - } - }; - - } else if (edgeAnalysisContext.isExpressionInline()) { - return new AbstractInlineAction(graphixRewritingContext, edgeAnalysisContext, iterationVar) { - @Override - public void apply(LoweringEnvironment loweringEnvironment) throws CompilationException { - // Join our edge iteration variable to our vertex variable. - loweringEnvironment.acceptTransformer(clauseSequence -> { - VariableExpr vertexJoinExpr = new VariableExpr(aliasLookupTable.getJoinAlias(vertexVar)); - Expression vertexEdgeJoin = buildVertexEdgeJoin( - buildAccessorList(vertexJoinExpr, elementLookupTable.getVertexKey(vertexIdentifier)), - buildAccessorList(new VariableExpr(iterationVar), edgeKeyAccess.apply(edgeIdentifier))); - JoinClause joinClause = new JoinClause(JoinType.INNER, datasetCallExpression, - new VariableExpr(iterationVar), null, vertexEdgeJoin, null); - clauseSequence.addMainClause(joinClause); - }); - - // Inline our edge body. - super.apply(loweringEnvironment); - - // Build a record constructor from our context to bind to our edge variable. - loweringEnvironment.acceptTransformer(clauseSequence -> { - VariableExpr intermediateVarExpr = new VariableExpr(intermediateVar); - RecordConstructor recordConstructor1 = buildRecordConstructor(); - RecordConstructor recordConstructor2 = buildRecordConstructor(); - clauseSequence.addMainClause(new CorrLetClause(recordConstructor1, intermediateVarExpr, null)); - clauseSequence.addRepresentativeEdgeBinding(edgeVar, recordConstructor2); - }); - aliasLookupTable.putIterationAlias(edgeVar, iterationVar); - aliasLookupTable.putJoinAlias(edgeVar, intermediateVar); - } - }; - - } else { - GraphElementDeclaration elementDeclaration = elementLookupTable.getElementDecl(edgeIdentifier); - return loweringEnvironment -> { - // Join our edge body to our vertex variable. - loweringEnvironment.acceptTransformer(clauseSequence -> { - VariableExpr vertexJoinExpr = new VariableExpr(aliasLookupTable.getJoinAlias(vertexVar)); - Expression vertexEdgeJoin = buildVertexEdgeJoin( - buildAccessorList(vertexJoinExpr, elementLookupTable.getVertexKey(vertexIdentifier)), - buildAccessorList(new VariableExpr(iterationVar), edgeKeyAccess.apply(edgeIdentifier))); - JoinClause joinClause = new JoinClause(JoinType.INNER, elementDeclaration.getNormalizedBody(), - new VariableExpr(iterationVar), null, vertexEdgeJoin, null); - clauseSequence.addMainClause(joinClause); - }); - - // Bind our intermediate (join) variable and edge variable. - loweringEnvironment.acceptTransformer(clauseSequence -> { - VariableExpr iterationVarExpr = new VariableExpr(iterationVar); - VariableExpr intermediateVarExpr = new VariableExpr(intermediateVar); - clauseSequence.addMainClause(new CorrLetClause(iterationVarExpr, intermediateVarExpr, null)); - clauseSequence.addRepresentativeEdgeBinding(edgeVar, new VariableExpr(iterationVar)); - }); - aliasLookupTable.putIterationAlias(edgeVar, iterationVar); - aliasLookupTable.putJoinAlias(edgeVar, intermediateVar); - }; - } - } - - /** - * Build an {@link IEnvironmentAction} to introduce a WHERE-CLAUSE that will correlate a vertex and edge. - */ - public IEnvironmentAction buildRawJoinVertexAction(VertexPatternExpr vertexPatternExpr, - EdgePatternExpr edgePatternExpr, Function<GraphElementIdentifier, List<List<String>>> edgeKeyAccess) - throws CompilationException { - VarIdentifier edgeVar = edgePatternExpr.getEdgeDescriptor().getVariableExpr().getVar(); - VarIdentifier vertexVar = vertexPatternExpr.getVariableExpr().getVar(); - - // We should only be working with one edge identifier... - EdgeDescriptor edgeDescriptor = edgePatternExpr.getEdgeDescriptor(); - List<GraphElementIdentifier> edgeElementIDs = edgeDescriptor.generateIdentifiers(graphIdentifier); - if (edgeElementIDs.size() != 1) { - throw new CompilationException(ErrorCode.COMPILATION_ILLEGAL_STATE, "Found non-canonical edge pattern!"); - } - GraphElementIdentifier edgeIdentifier = edgeElementIDs.get(0); - - // ...and only one vertex identifier (given that we only have one label). - List<GraphElementIdentifier> vertexElementIDs = vertexPatternExpr.generateIdentifiers(graphIdentifier); - if (vertexElementIDs.size() != 1) { - throw new CompilationException(ErrorCode.COMPILATION_ILLEGAL_STATE, "Found non-canonical vertex pattern!"); - } - GraphElementIdentifier vertexIdentifier = vertexElementIDs.get(0); - return loweringEnvironment -> { - // No aliases need to be introduced, we just need to add a WHERE-CONJUNCT. - VariableExpr edgeJoinExpr = new VariableExpr(aliasLookupTable.getJoinAlias(edgeVar)); - VariableExpr vertexJoinExpr = new VariableExpr(aliasLookupTable.getJoinAlias(vertexVar)); - loweringEnvironment.acceptTransformer(clauseSequence -> { - Expression vertexEdgeJoin = buildVertexEdgeJoin( - buildAccessorList(vertexJoinExpr, elementLookupTable.getVertexKey(vertexIdentifier)), - buildAccessorList(edgeJoinExpr, edgeKeyAccess.apply(edgeIdentifier))); - clauseSequence.addMainClause(new CorrWhereClause(vertexEdgeJoin)); - }); - }; - } - - /** - * Build an {@link IEnvironmentAction} to handle a vertex that is bound to an existing (already introduced) edge. - * There are three possible {@link IEnvironmentAction}s generated here: - * 1. An action for inlined vertices that have no projections. - * 2. An action for inlined vertices that have projections. - * 3. An action for non-inlined vertices. - */ - public IEnvironmentAction buildBoundVertexAction(VertexPatternExpr vertexPatternExpr, - EdgePatternExpr edgePatternExpr, Function<GraphElementIdentifier, List<List<String>>> edgeKeyAccess) - throws CompilationException { - VarIdentifier edgeVar = edgePatternExpr.getEdgeDescriptor().getVariableExpr().getVar(); - VarIdentifier vertexVar = vertexPatternExpr.getVariableExpr().getVar(); - VarIdentifier iterationVar = graphixRewritingContext.getNewGraphixVariable(); - VarIdentifier intermediateVar = graphixRewritingContext.getNewGraphixVariable(); - - // We should only be working with one edge identifier... - EdgeDescriptor edgeDescriptor = edgePatternExpr.getEdgeDescriptor(); - List<GraphElementIdentifier> edgeElementIDs = edgeDescriptor.generateIdentifiers(graphIdentifier); - if (edgeElementIDs.size() != 1) { - throw new CompilationException(ErrorCode.COMPILATION_ILLEGAL_STATE, "Found non-canonical edge pattern!"); - } - GraphElementIdentifier edgeIdentifier = edgeElementIDs.get(0); - - // ...and only one vertex identifier (given that we only have one label). - List<GraphElementIdentifier> vertexElementIDs = vertexPatternExpr.generateIdentifiers(graphIdentifier); - if (vertexElementIDs.size() != 1) { - throw new CompilationException(ErrorCode.COMPILATION_ILLEGAL_STATE, "Found non-canonical vertex pattern!"); - } - GraphElementIdentifier vertexIdentifier = vertexElementIDs.get(0); - ElementBodyAnalysisContext vertexAnalysisContext = analysisContextMap.get(vertexIdentifier); - Expression datasetCallExpression = vertexAnalysisContext.getDatasetCallExpression(); - if (vertexAnalysisContext.isExpressionInline() && vertexAnalysisContext.isSelectClauseInline()) { - return new AbstractInlineAction(graphixRewritingContext, vertexAnalysisContext, iterationVar) { - @Override - public void apply(LoweringEnvironment loweringEnvironment) throws CompilationException { - // Join our vertex iteration variable to our edge variable. - loweringEnvironment.acceptTransformer(clauseSequence -> { - VariableExpr edgeJoinExpr = new VariableExpr(aliasLookupTable.getJoinAlias(edgeVar)); - VariableExpr vertexJoinExpr = new VariableExpr(iterationVar); - Expression vertexEdgeJoin = buildVertexEdgeJoin( - buildAccessorList(vertexJoinExpr, elementLookupTable.getVertexKey(vertexIdentifier)), - buildAccessorList(edgeJoinExpr, edgeKeyAccess.apply(edgeIdentifier))); - JoinClause joinClause = new JoinClause(JoinType.INNER, datasetCallExpression, - new VariableExpr(iterationVar), null, vertexEdgeJoin, null); - clauseSequence.addMainClause(joinClause); - }); - - // Inline our vertex body. - super.apply(loweringEnvironment); - - // Bind our intermediate (join) variable and vertex variable. - loweringEnvironment.acceptTransformer(clauseSequence -> { - VariableExpr iterationVarExpr = new VariableExpr(iterationVar); - VariableExpr intermediateVarExpr = new VariableExpr(intermediateVar); - clauseSequence.addMainClause(new CorrLetClause(iterationVarExpr, intermediateVarExpr, null)); - clauseSequence.addRepresentativeVertexBinding(vertexVar, new VariableExpr(iterationVar)); - }); - aliasLookupTable.putIterationAlias(vertexVar, iterationVar); - aliasLookupTable.putJoinAlias(vertexVar, intermediateVar); - } - }; - - } else if (vertexAnalysisContext.isExpressionInline()) { - return new AbstractInlineAction(graphixRewritingContext, vertexAnalysisContext, iterationVar) { - @Override - public void apply(LoweringEnvironment loweringEnvironment) throws CompilationException { - // Join our vertex iteration variable to our edge variable. - loweringEnvironment.acceptTransformer(clauseSequence -> { - VariableExpr edgeJoinExpr = new VariableExpr(aliasLookupTable.getJoinAlias(edgeVar)); - VariableExpr vertexJoinExpr = new VariableExpr(iterationVar); - Expression vertexEdgeJoin = buildVertexEdgeJoin( - buildAccessorList(vertexJoinExpr, elementLookupTable.getVertexKey(vertexIdentifier)), - buildAccessorList(edgeJoinExpr, edgeKeyAccess.apply(edgeIdentifier))); - JoinClause joinClause = new JoinClause(JoinType.INNER, datasetCallExpression, - new VariableExpr(iterationVar), null, vertexEdgeJoin, null); - clauseSequence.addMainClause(joinClause); - }); - - // Inline our vertex body. - super.apply(loweringEnvironment); - - // Build a record constructor from our context to bind to our vertex variable. - loweringEnvironment.acceptTransformer(clauseSequence -> { - VariableExpr intermediateVarExpr = new VariableExpr(intermediateVar); - RecordConstructor recordConstructor1 = buildRecordConstructor(); - RecordConstructor recordConstructor2 = buildRecordConstructor(); - clauseSequence.addMainClause(new CorrLetClause(recordConstructor1, intermediateVarExpr, null)); - clauseSequence.addRepresentativeVertexBinding(vertexVar, recordConstructor2); - }); - aliasLookupTable.putIterationAlias(vertexVar, iterationVar); - aliasLookupTable.putJoinAlias(vertexVar, intermediateVar); - } - }; - - } else { - GraphElementDeclaration elementDeclaration = elementLookupTable.getElementDecl(vertexIdentifier); - return loweringEnvironment -> { - // Join our vertex body to our edge variable. - loweringEnvironment.acceptTransformer(clauseSequence -> { - VariableExpr edgeJoinExpr = new VariableExpr(aliasLookupTable.getJoinAlias(edgeVar)); - VariableExpr vertexJoinExpr = new VariableExpr(iterationVar); - Expression vertexEdgeJoin = buildVertexEdgeJoin( - buildAccessorList(vertexJoinExpr, elementLookupTable.getVertexKey(vertexIdentifier)), - buildAccessorList(edgeJoinExpr, edgeKeyAccess.apply(edgeIdentifier))); - JoinClause joinClause = new JoinClause(JoinType.INNER, elementDeclaration.getNormalizedBody(), - new VariableExpr(iterationVar), null, vertexEdgeJoin, null); - clauseSequence.addMainClause(joinClause); - }); - - // Bind our intermediate (join) variable and vertex variable. - loweringEnvironment.acceptTransformer(clauseSequence -> { - VariableExpr iterationVarExpr = new VariableExpr(iterationVar); - VariableExpr intermediateVarExpr = new VariableExpr(intermediateVar); - clauseSequence.addMainClause(new CorrLetClause(iterationVarExpr, intermediateVarExpr, null)); - clauseSequence.addRepresentativeVertexBinding(vertexVar, new VariableExpr(iterationVar)); - }); - aliasLookupTable.putIterationAlias(vertexVar, iterationVar); - aliasLookupTable.putJoinAlias(vertexVar, intermediateVar); - }; - } - } -}
diff --git a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/lower/LoweringAliasLookupTable.java b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/lower/LoweringAliasLookupTable.java deleted file mode 100644 index 0642c20..0000000 --- a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/lower/LoweringAliasLookupTable.java +++ /dev/null
@@ -1,53 +0,0 @@ -/* - * 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.graphix.lang.rewrites.lower; - -import java.util.HashMap; -import java.util.Map; - -import org.apache.asterix.lang.common.struct.VarIdentifier; - -/** - * Lookup table for JOIN and ITERATION aliases, indexed by their representative (i.e. element) variable identifiers. - */ -public class LoweringAliasLookupTable { - private final Map<VarIdentifier, VarIdentifier> joinAliasMap = new HashMap<>(); - private final Map<VarIdentifier, VarIdentifier> iterationAliasMap = new HashMap<>(); - - public void putJoinAlias(VarIdentifier elementVariable, VarIdentifier aliasVariable) { - joinAliasMap.put(elementVariable, aliasVariable); - } - - public void putIterationAlias(VarIdentifier elementVariable, VarIdentifier aliasVariable) { - iterationAliasMap.put(elementVariable, aliasVariable); - } - - public VarIdentifier getJoinAlias(VarIdentifier elementVariable) { - return joinAliasMap.get(elementVariable); - } - - public VarIdentifier getIterationAlias(VarIdentifier elementVariable) { - return iterationAliasMap.get(elementVariable); - } - - public void reset() { - joinAliasMap.clear(); - iterationAliasMap.clear(); - } -}
diff --git a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/lower/LoweringEnvironment.java b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/lower/LoweringEnvironment.java deleted file mode 100644 index dc9f594..0000000 --- a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/lower/LoweringEnvironment.java +++ /dev/null
@@ -1,183 +0,0 @@ -/* - * 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.graphix.lang.rewrites.lower; - -import static org.apache.asterix.lang.sqlpp.util.SqlppVariableUtil.toUserDefinedVariableName; - -import java.util.ArrayList; -import java.util.List; -import java.util.ListIterator; -import java.util.function.Consumer; - -import org.apache.asterix.common.exceptions.CompilationException; -import org.apache.asterix.common.exceptions.ErrorCode; -import org.apache.asterix.graphix.lang.clause.CorrLetClause; -import org.apache.asterix.graphix.lang.clause.FromGraphClause; -import org.apache.asterix.graphix.lang.clause.GraphSelectBlock; -import org.apache.asterix.graphix.lang.rewrites.GraphixRewritingContext; -import org.apache.asterix.graphix.lang.rewrites.lower.action.IEnvironmentAction; -import org.apache.asterix.graphix.lang.rewrites.lower.transform.CorrelatedClauseSequence; -import org.apache.asterix.graphix.lang.rewrites.lower.transform.ISequenceTransformer; -import org.apache.asterix.graphix.lang.rewrites.visitor.VariableSubstitutionVisitor; -import org.apache.asterix.lang.common.base.Clause; -import org.apache.asterix.lang.common.base.Expression; -import org.apache.asterix.lang.common.base.Literal; -import org.apache.asterix.lang.common.expression.FieldAccessor; -import org.apache.asterix.lang.common.expression.LiteralExpr; -import org.apache.asterix.lang.common.expression.VariableExpr; -import org.apache.asterix.lang.common.literal.TrueLiteral; -import org.apache.asterix.lang.common.struct.VarIdentifier; -import org.apache.asterix.lang.sqlpp.clause.AbstractBinaryCorrelateClause; -import org.apache.asterix.lang.sqlpp.clause.FromClause; -import org.apache.asterix.lang.sqlpp.clause.FromTerm; -import org.apache.asterix.lang.sqlpp.clause.JoinClause; -import org.apache.asterix.lang.sqlpp.clause.Projection; -import org.apache.asterix.lang.sqlpp.clause.SelectBlock; -import org.apache.asterix.lang.sqlpp.clause.SelectClause; -import org.apache.asterix.lang.sqlpp.clause.SelectRegular; -import org.apache.asterix.lang.sqlpp.clause.SelectSetOperation; -import org.apache.asterix.lang.sqlpp.expression.SelectExpression; -import org.apache.asterix.lang.sqlpp.optype.JoinType; -import org.apache.asterix.lang.sqlpp.struct.SetOperationInput; -import org.apache.commons.collections4.IterableUtils; -import org.apache.commons.collections4.IteratorUtils; -import org.apache.hyracks.api.exceptions.IWarningCollector; -import org.apache.hyracks.api.exceptions.SourceLocation; -import org.apache.hyracks.api.exceptions.Warning; - -/** - * @see org.apache.asterix.graphix.lang.rewrites.visitor.GraphixLoweringVisitor - */ -public class LoweringEnvironment { - private final GraphSelectBlock graphSelectBlock; - private final CorrelatedClauseSequence mainClauseSequence; - private final GraphixRewritingContext graphixRewritingContext; - private CorrelatedClauseSequence leftMatchClauseSequence; - - public LoweringEnvironment(GraphSelectBlock graphSelectBlock, GraphixRewritingContext graphixRewritingContext) { - this.graphixRewritingContext = graphixRewritingContext; - this.mainClauseSequence = new CorrelatedClauseSequence(); - this.graphSelectBlock = graphSelectBlock; - this.leftMatchClauseSequence = null; - } - - public void acceptAction(IEnvironmentAction environmentAction) throws CompilationException { - environmentAction.apply(this); - } - - public void acceptTransformer(ISequenceTransformer sequenceTransformer) throws CompilationException { - boolean isLeftMatch = leftMatchClauseSequence == null; - sequenceTransformer.accept(isLeftMatch ? mainClauseSequence : leftMatchClauseSequence); - } - - public void beginLeftMatch() throws CompilationException { - if (leftMatchClauseSequence != null) { - throw new CompilationException(ErrorCode.COMPILATION_ILLEGAL_STATE, - "LEFT-MATCH lowering is currently in progress!"); - } - leftMatchClauseSequence = new CorrelatedClauseSequence(); - } - - public void endLeftMatch(IWarningCollector warningCollector) throws CompilationException { - VariableSubstitutionVisitor substitutionVisitor = new VariableSubstitutionVisitor(graphixRewritingContext); - VariableExpr nestingVariableExpr = new VariableExpr(graphixRewritingContext.getNewGraphixVariable()); - final Consumer<VarIdentifier> substitutionAdder = v -> { - VariableExpr sourceNestingVariableExpr = new VariableExpr(nestingVariableExpr.getVar()); - FieldAccessor fieldAccessor = new FieldAccessor(sourceNestingVariableExpr, v); - substitutionVisitor.addSubstitution(v, fieldAccessor); - }; - - // Build up our projection list. - List<Projection> projectionList = new ArrayList<>(); - ListIterator<AbstractBinaryCorrelateClause> forProjectIterator = leftMatchClauseSequence.getMainIterator(); - while (forProjectIterator.hasNext()) { - AbstractBinaryCorrelateClause workingClause = forProjectIterator.next(); - if (workingClause.getClauseType() == Clause.ClauseType.WHERE_CLAUSE) { - continue; - } - VarIdentifier rightVariable = workingClause.getRightVariable().getVar(); - projectionList.add(new Projection(Projection.Kind.NAMED_EXPR, new VariableExpr(rightVariable), - toUserDefinedVariableName(rightVariable).getValue())); - substitutionAdder.accept(rightVariable); - } - - // Assemble a FROM-TERM from our LEFT-MATCH sequence (do not add our representatives). - JoinClause headCorrelateClause = (JoinClause) leftMatchClauseSequence.popMainClauseSequence(); - ListIterator<AbstractBinaryCorrelateClause> mainIterator = leftMatchClauseSequence.getMainIterator(); - List<AbstractBinaryCorrelateClause> correlateClauses = IteratorUtils.toList(mainIterator); - raiseCrossJoinWarning(correlateClauses, warningCollector, null); - FromTerm fromTerm = new FromTerm(headCorrelateClause.getRightExpression(), - headCorrelateClause.getRightVariable(), headCorrelateClause.getPositionalVariable(), correlateClauses); - - // Nest our FROM-TERM. - SelectClause selectClause = new SelectClause(null, new SelectRegular(projectionList), false); - SelectBlock selectBlock = new SelectBlock(selectClause, new FromClause(List.of(fromTerm)), null, null, null); - SetOperationInput setOperationInput = new SetOperationInput(selectBlock, null); - SelectSetOperation selectSetOperation = new SelectSetOperation(setOperationInput, null); - SelectExpression selectExpression = new SelectExpression(null, selectSetOperation, null, null, true); - - // Attach our assembled sequence back to the main sequence. - Expression conditionExpression = headCorrelateClause.getConditionExpression(); - Expression newConditionExpression = conditionExpression.accept(substitutionVisitor, null); - JoinClause leftJoinClause = new JoinClause(JoinType.LEFTOUTER, selectExpression, nestingVariableExpr, null, - newConditionExpression, Literal.Type.MISSING); - mainClauseSequence.addMainClause(leftJoinClause); - - // Introduce our representative variables back into our main sequence. - for (CorrLetClause representativeVertexBinding : leftMatchClauseSequence.getRepresentativeVertexBindings()) { - VarIdentifier representativeVariable = representativeVertexBinding.getRightVariable().getVar(); - Expression rightExpression = representativeVertexBinding.getRightExpression(); - Expression reboundExpression = rightExpression.accept(substitutionVisitor, null); - mainClauseSequence.addRepresentativeVertexBinding(representativeVariable, reboundExpression); - } - for (CorrLetClause representativeEdgeBinding : leftMatchClauseSequence.getRepresentativeEdgeBindings()) { - VarIdentifier representativeVariable = representativeEdgeBinding.getRightVariable().getVar(); - Expression rightExpression = representativeEdgeBinding.getRightExpression(); - Expression reboundExpression = rightExpression.accept(substitutionVisitor, null); - mainClauseSequence.addRepresentativeEdgeBinding(representativeVariable, reboundExpression); - } - leftMatchClauseSequence = null; - } - - public void finalizeLowering(FromGraphClause fromGraphClause, IWarningCollector warningCollector) { - AbstractBinaryCorrelateClause headCorrelateClause = mainClauseSequence.popMainClauseSequence(); - List<AbstractBinaryCorrelateClause> correlateClauses = IterableUtils.toList(mainClauseSequence); - raiseCrossJoinWarning(correlateClauses, warningCollector, fromGraphClause.getSourceLocation()); - FromTerm outputFromTerm = new FromTerm(headCorrelateClause.getRightExpression(), - headCorrelateClause.getRightVariable(), headCorrelateClause.getPositionalVariable(), correlateClauses); - graphSelectBlock.setFromClause(new FromClause(List.of(outputFromTerm))); - graphSelectBlock.getFromClause().setSourceLocation(fromGraphClause.getSourceLocation()); - } - - private static void raiseCrossJoinWarning(List<AbstractBinaryCorrelateClause> correlateClauses, - IWarningCollector warningCollector, SourceLocation sourceLocation) { - for (AbstractBinaryCorrelateClause correlateClause : correlateClauses) { - if (correlateClause.getClauseType() == Clause.ClauseType.JOIN_CLAUSE) { - JoinClause joinClause = (JoinClause) correlateClause; - if (joinClause.getConditionExpression().getKind() == Expression.Kind.LITERAL_EXPRESSION) { - LiteralExpr literalExpr = (LiteralExpr) joinClause.getConditionExpression(); - if (literalExpr.getValue().equals(TrueLiteral.INSTANCE) && warningCollector.shouldWarn()) { - warningCollector.warn(Warning.of(sourceLocation, ErrorCode.COMPILATION_ERROR, - "Potential disconnected pattern encountered! A CROSS-JOIN has been introduced.")); - } - } - } - } - } -}
diff --git a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/lower/action/AbstractInlineAction.java b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/lower/action/AbstractInlineAction.java deleted file mode 100644 index b82801b..0000000 --- a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/lower/action/AbstractInlineAction.java +++ /dev/null
@@ -1,149 +0,0 @@ -/* - * 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.graphix.lang.rewrites.lower.action; - -import static org.apache.asterix.graphix.lang.rewrites.visitor.ElementBodyAnalysisVisitor.ElementBodyAnalysisContext; - -import java.util.ArrayList; -import java.util.List; - -import org.apache.asterix.common.exceptions.CompilationException; -import org.apache.asterix.common.exceptions.ErrorCode; -import org.apache.asterix.graphix.lang.clause.CorrLetClause; -import org.apache.asterix.graphix.lang.clause.CorrWhereClause; -import org.apache.asterix.graphix.lang.rewrites.GraphixRewritingContext; -import org.apache.asterix.graphix.lang.rewrites.lower.LoweringEnvironment; -import org.apache.asterix.graphix.lang.rewrites.visitor.VariableSubstitutionVisitor; -import org.apache.asterix.lang.common.base.Expression; -import org.apache.asterix.lang.common.base.ILangExpression; -import org.apache.asterix.lang.common.clause.LetClause; -import org.apache.asterix.lang.common.clause.WhereClause; -import org.apache.asterix.lang.common.expression.FieldBinding; -import org.apache.asterix.lang.common.expression.LiteralExpr; -import org.apache.asterix.lang.common.expression.RecordConstructor; -import org.apache.asterix.lang.common.expression.VariableExpr; -import org.apache.asterix.lang.common.literal.StringLiteral; -import org.apache.asterix.lang.common.struct.VarIdentifier; -import org.apache.asterix.lang.sqlpp.clause.AbstractBinaryCorrelateClause; -import org.apache.asterix.lang.sqlpp.clause.Projection; -import org.apache.asterix.lang.sqlpp.clause.UnnestClause; -import org.apache.asterix.lang.sqlpp.util.SqlppRewriteUtil; - -/** - * Inline an element body into a {@link LoweringEnvironment}. This includes a) copying {@link UnnestClause}, - * {@link LetClause}, and {@link WhereClause} AST nodes from our body analysis, and b) creating - * {@link RecordConstructor} AST nodes to inline {@link org.apache.asterix.lang.sqlpp.clause.SelectRegular} nodes. - */ -public abstract class AbstractInlineAction implements IEnvironmentAction { - protected final GraphixRewritingContext graphixRewritingContext; - protected final ElementBodyAnalysisContext bodyAnalysisContext; - - // This may be mutated by our child. - protected VarIdentifier elementVariable; - - // The following is reset on each application. - private VariableSubstitutionVisitor substitutionVisitor; - - protected AbstractInlineAction(GraphixRewritingContext graphixRewritingContext, - ElementBodyAnalysisContext bodyAnalysisContext, VarIdentifier elementVariable) { - this.graphixRewritingContext = graphixRewritingContext; - this.bodyAnalysisContext = bodyAnalysisContext; - this.elementVariable = elementVariable; - } - - @Override - public void apply(LoweringEnvironment loweringEnvironment) throws CompilationException { - // To inline, we need to ensure that we substitute variables accordingly. - substitutionVisitor = new VariableSubstitutionVisitor(graphixRewritingContext); - if (bodyAnalysisContext.getFromTermVariable() != null) { - VariableExpr fromTermVariableExpr = bodyAnalysisContext.getFromTermVariable(); - VariableExpr elementVariableExpr = new VariableExpr(elementVariable); - substitutionVisitor.addSubstitution(fromTermVariableExpr.getVar(), elementVariableExpr); - } - - // If we have any UNNEST clauses, we need to add these. - if (bodyAnalysisContext.getUnnestClauses() != null) { - for (AbstractBinaryCorrelateClause unnestClause : bodyAnalysisContext.getUnnestClauses()) { - VarIdentifier reboundUnnestVariableID = graphixRewritingContext.getNewGraphixVariable(); - VariableExpr reboundVariableExpr = new VariableExpr(reboundUnnestVariableID); - - // Remap this UNNEST-CLAUSE to include our new variables. - loweringEnvironment.acceptTransformer(clauseSequence -> { - UnnestClause copiedClause = (UnnestClause) SqlppRewriteUtil.deepCopy(unnestClause); - copiedClause.accept(substitutionVisitor, null); - VariableExpr substitutionVariableExpr = (copiedClause.hasPositionalVariable()) - ? copiedClause.getPositionalVariable() : copiedClause.getRightVariable(); - substitutionVisitor.addSubstitution(substitutionVariableExpr.getVar(), reboundVariableExpr); - UnnestClause newUnnestClause = new UnnestClause(copiedClause.getUnnestType(), - copiedClause.getRightExpression(), new VariableExpr(reboundUnnestVariableID), null, - copiedClause.getOuterUnnestMissingValueType()); - clauseSequence.addMainClause(newUnnestClause); - }); - } - } - - // If we have any LET clauses, we need to substitute them in our WHERE and SELECT clauses. - if (bodyAnalysisContext.getLetClauses() != null) { - for (LetClause letClause : bodyAnalysisContext.getLetClauses()) { - VarIdentifier reboundLetVariableID = graphixRewritingContext.getNewGraphixVariable(); - VariableExpr reboundVariableExpr = new VariableExpr(reboundLetVariableID); - - // Remap this LET-CLAUSE to include our new variables. Move this to our correlated clauses. - LetClause copiedClause = (LetClause) SqlppRewriteUtil.deepCopy(letClause); - copiedClause.accept(substitutionVisitor, null); - Expression copiedBindingExpr = copiedClause.getBindingExpr(); - substitutionVisitor.addSubstitution(copiedClause.getVarExpr().getVar(), reboundVariableExpr); - loweringEnvironment.acceptTransformer(corrSequence -> { - VariableExpr reboundLetVariableExpr = new VariableExpr(reboundLetVariableID); - corrSequence.addMainClause(new CorrLetClause(copiedBindingExpr, reboundLetVariableExpr, null)); - }); - } - } - - // If we have any WHERE clauses, we need to add these. - if (bodyAnalysisContext.getWhereClauses() != null) { - for (WhereClause whereClause : bodyAnalysisContext.getWhereClauses()) { - WhereClause copiedClause = (WhereClause) SqlppRewriteUtil.deepCopy(whereClause); - copiedClause.accept(substitutionVisitor, null); - loweringEnvironment.acceptTransformer(corrSequence -> { - CorrWhereClause corrWhereClause = new CorrWhereClause(copiedClause.getWhereExpr()); - corrSequence.addMainClause(corrWhereClause); - }); - } - } - } - - protected RecordConstructor buildRecordConstructor() throws CompilationException { - if (bodyAnalysisContext.getSelectClauseProjections() != null) { - // Map our original variable to our element variable. - List<FieldBinding> fieldBindings = new ArrayList<>(); - for (Projection projection : bodyAnalysisContext.getSelectClauseProjections()) { - LiteralExpr fieldNameExpr = new LiteralExpr(new StringLiteral(projection.getName())); - ILangExpression copiedExpr = SqlppRewriteUtil.deepCopy(projection.getExpression()); - Expression fieldValueExpr = copiedExpr.accept(substitutionVisitor, null); - fieldBindings.add(new FieldBinding(fieldNameExpr, fieldValueExpr)); - } - return new RecordConstructor(fieldBindings); - - } else { - throw new CompilationException(ErrorCode.COMPILATION_ILLEGAL_STATE, - "Non-inlineable SELECT clause encountered, but was body was marked as inline!"); - } - } -}
diff --git a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/lower/action/IsomorphismAction.java b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/lower/action/IsomorphismAction.java deleted file mode 100644 index e54cc74..0000000 --- a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/lower/action/IsomorphismAction.java +++ /dev/null
@@ -1,306 +0,0 @@ -/* - * 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.graphix.lang.rewrites.lower.action; - -import static org.apache.asterix.graphix.lang.rewrites.lower.action.IsomorphismAction.MatchEvaluationKind.COMPLETE_ISOMORPHISM; -import static org.apache.asterix.graphix.lang.rewrites.lower.action.IsomorphismAction.MatchEvaluationKind.EDGE_ISOMORPHISM; -import static org.apache.asterix.graphix.lang.rewrites.lower.action.IsomorphismAction.MatchEvaluationKind.HOMOMORPHISM; -import static org.apache.asterix.graphix.lang.rewrites.lower.action.IsomorphismAction.MatchEvaluationKind.VERTEX_ISOMORPHISM; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.ListIterator; -import java.util.Map; -import java.util.Set; -import java.util.function.Function; - -import org.apache.asterix.common.exceptions.CompilationException; -import org.apache.asterix.common.exceptions.ErrorCode; -import org.apache.asterix.common.functions.FunctionSignature; -import org.apache.asterix.graphix.algebra.compiler.provider.GraphixCompilationProvider; -import org.apache.asterix.graphix.lang.clause.CorrWhereClause; -import org.apache.asterix.graphix.lang.clause.FromGraphClause; -import org.apache.asterix.graphix.lang.clause.MatchClause; -import org.apache.asterix.graphix.lang.expression.EdgePatternExpr; -import org.apache.asterix.graphix.lang.expression.VertexPatternExpr; -import org.apache.asterix.graphix.lang.rewrites.GraphixRewritingContext; -import org.apache.asterix.graphix.lang.rewrites.lower.LoweringAliasLookupTable; -import org.apache.asterix.graphix.lang.rewrites.lower.LoweringEnvironment; -import org.apache.asterix.graphix.lang.rewrites.lower.transform.CorrelatedClauseSequence; -import org.apache.asterix.graphix.lang.rewrites.lower.transform.ISequenceTransformer; -import org.apache.asterix.graphix.lang.rewrites.visitor.AbstractGraphixQueryVisitor; -import org.apache.asterix.graphix.lang.rewrites.visitor.VariableSubstitutionVisitor; -import org.apache.asterix.graphix.lang.struct.ElementLabel; -import org.apache.asterix.lang.common.base.Clause; -import org.apache.asterix.lang.common.base.Expression; -import org.apache.asterix.lang.common.base.ILangExpression; -import org.apache.asterix.lang.common.expression.CallExpr; -import org.apache.asterix.lang.common.expression.FieldAccessor; -import org.apache.asterix.lang.common.expression.OperatorExpr; -import org.apache.asterix.lang.common.expression.VariableExpr; -import org.apache.asterix.lang.common.struct.OperatorType; -import org.apache.asterix.lang.common.struct.VarIdentifier; -import org.apache.asterix.lang.sqlpp.clause.AbstractBinaryCorrelateClause; -import org.apache.asterix.lang.sqlpp.clause.FromTerm; -import org.apache.asterix.lang.sqlpp.clause.JoinClause; -import org.apache.asterix.lang.sqlpp.clause.SelectBlock; -import org.apache.asterix.lang.sqlpp.clause.SelectSetOperation; -import org.apache.asterix.lang.sqlpp.expression.SelectExpression; -import org.apache.asterix.lang.sqlpp.optype.JoinType; -import org.apache.asterix.lang.sqlpp.util.SqlppVariableUtil; -import org.apache.asterix.metadata.declared.MetadataProvider; -import org.apache.asterix.om.functions.BuiltinFunctions; - -/** - * Define which graph elements are *not* equal to each other. We assume that all elements are named at this point and - * that our {@link FromGraphClause} is in canonical form. We enforce the following (by default, we enforce total - * isomorphism): - * 1. No vertex and no edge can appear more than once across all patterns of all {@link MatchClause} nodes. - * 2. For vertex-isomorphism, we enforce that no vertex can appear more than once across all {@link MatchClause} nodes. - * 3. For edge-isomorphism, we enforce that no edge can appear more than once across all {@link MatchClause} nodes. - * 4. For homomorphism, we enforce nothing. Edge adjacency is already implicitly preserved. - */ -public class IsomorphismAction implements IEnvironmentAction { - public enum MatchEvaluationKind { - COMPLETE_ISOMORPHISM("isomorphism"), - VERTEX_ISOMORPHISM("vertex-isomorphism"), - EDGE_ISOMORPHISM("edge-isomorphism"), - HOMOMORPHISM("homomorphism"); - - // The user specifies the options above through "SET `graphix.match-evaluation` '...';" - private final String metadataConfigOptionName; - - MatchEvaluationKind(String metadataConfigOptionName) { - this.metadataConfigOptionName = metadataConfigOptionName; - } - - @Override - public String toString() { - return metadataConfigOptionName; - } - } - - // We will walk through our FROM-GRAPH-CLAUSE and determine our isomorphism conjuncts. - private final MatchEvaluationKind matchEvaluationKind; - private final FromGraphClause fromGraphClause; - private final LoweringAliasLookupTable aliasLookupTable; - private final GraphixRewritingContext graphixRewritingContext; - - public IsomorphismAction(GraphixRewritingContext graphixRewritingContext, FromGraphClause fromGraphClause, - LoweringAliasLookupTable aliasLookupTable) throws CompilationException { - final String metadataConfigKeyName = GraphixCompilationProvider.MATCH_EVALUATION_METADATA_CONFIG; - this.graphixRewritingContext = graphixRewritingContext; - this.fromGraphClause = fromGraphClause; - this.aliasLookupTable = aliasLookupTable; - - MetadataProvider metadataProvider = graphixRewritingContext.getMetadataProvider(); - if (metadataProvider.getConfig().containsKey(metadataConfigKeyName)) { - String metadataConfigKeyValue = (String) metadataProvider.getConfig().get(metadataConfigKeyName); - if (metadataConfigKeyValue.equalsIgnoreCase(COMPLETE_ISOMORPHISM.toString())) { - this.matchEvaluationKind = COMPLETE_ISOMORPHISM; - - } else if (metadataConfigKeyValue.equalsIgnoreCase(VERTEX_ISOMORPHISM.toString())) { - this.matchEvaluationKind = VERTEX_ISOMORPHISM; - - } else if (metadataConfigKeyValue.equalsIgnoreCase(EDGE_ISOMORPHISM.toString())) { - this.matchEvaluationKind = EDGE_ISOMORPHISM; - - } else if (metadataConfigKeyValue.equalsIgnoreCase(HOMOMORPHISM.toString())) { - this.matchEvaluationKind = HOMOMORPHISM; - - } else { - throw new CompilationException(ErrorCode.ILLEGAL_SET_PARAMETER, metadataConfigKeyValue); - } - - } else { - this.matchEvaluationKind = COMPLETE_ISOMORPHISM; - } - } - - private static List<OperatorExpr> generateIsomorphismConjuncts(List<VariableExpr> variableList) { - List<OperatorExpr> isomorphismConjuncts = new ArrayList<>(); - - // Find all unique pairs from our list of variables. - for (int i = 0; i < variableList.size(); i++) { - for (int j = i + 1; j < variableList.size(); j++) { - OperatorExpr inequalityConjunct = new OperatorExpr(); - inequalityConjunct.addOperator(OperatorType.NEQ); - inequalityConjunct.addOperand(variableList.get(i)); - inequalityConjunct.addOperand(variableList.get(j)); - isomorphismConjuncts.add(inequalityConjunct); - } - } - - return isomorphismConjuncts; - } - - @Override - public void apply(LoweringEnvironment loweringEnvironment) throws CompilationException { - Map<ElementLabel, List<VariableExpr>> vertexVariableMap = new HashMap<>(); - Map<ElementLabel, List<VariableExpr>> edgeVariableMap = new HashMap<>(); - - // Populate the collections above. - fromGraphClause.accept(new AbstractGraphixQueryVisitor() { - private void populateVariableMap(ElementLabel label, VariableExpr variableExpr, - Map<ElementLabel, List<VariableExpr>> labelVariableMap) { - Function<VariableExpr, Boolean> mapMatchFinder = v -> { - final String variableName = SqlppVariableUtil.toUserDefinedName(variableExpr.getVar().getValue()); - String inputVariableName = SqlppVariableUtil.toUserDefinedName(v.getVar().getValue()); - return variableName.equals(inputVariableName); - }; - if (labelVariableMap.containsKey(label)) { - if (labelVariableMap.get(label).stream().noneMatch(mapMatchFinder::apply)) { - labelVariableMap.get(label).add(variableExpr); - } - - } else { - List<VariableExpr> variableList = new ArrayList<>(); - variableList.add(variableExpr); - labelVariableMap.put(label, variableList); - } - } - - @Override - public Expression visit(FromGraphClause fromGraphClause, ILangExpression arg) throws CompilationException { - // We only want to explore the top level of our FROM-GRAPH-CLAUSEs. - for (MatchClause matchClause : fromGraphClause.getMatchClauses()) { - matchClause.accept(this, arg); - } - return null; - } - - @Override - public Expression visit(VertexPatternExpr vertexPatternExpr, ILangExpression arg) { - VarIdentifier vertexVariableID = vertexPatternExpr.getVariableExpr().getVar(); - VarIdentifier iterationVariableID = aliasLookupTable.getIterationAlias(vertexVariableID); - ElementLabel elementLabel = vertexPatternExpr.getLabels().iterator().next(); - populateVariableMap(elementLabel, new VariableExpr(iterationVariableID), vertexVariableMap); - return null; - } - - @Override - public Expression visit(EdgePatternExpr edgePatternExpr, ILangExpression arg) { - VarIdentifier edgeVariableID = edgePatternExpr.getEdgeDescriptor().getVariableExpr().getVar(); - VarIdentifier iterationVariableID = aliasLookupTable.getIterationAlias(edgeVariableID); - ElementLabel elementLabel = edgePatternExpr.getEdgeDescriptor().getEdgeLabels().iterator().next(); - populateVariableMap(elementLabel, new VariableExpr(iterationVariableID), edgeVariableMap); - return null; - } - }, null); - - // Construct our isomorphism conjuncts. - List<OperatorExpr> isomorphismConjuncts = new ArrayList<>(); - if (matchEvaluationKind == COMPLETE_ISOMORPHISM || matchEvaluationKind == VERTEX_ISOMORPHISM) { - vertexVariableMap.values().stream().map(IsomorphismAction::generateIsomorphismConjuncts) - .forEach(isomorphismConjuncts::addAll); - } - if (matchEvaluationKind == COMPLETE_ISOMORPHISM || matchEvaluationKind == EDGE_ISOMORPHISM) { - edgeVariableMap.values().stream().map(IsomorphismAction::generateIsomorphismConjuncts) - .forEach(isomorphismConjuncts::addAll); - } - - // Iterate through our clause sequence, and introduce our isomorphism conjuncts eagerly. - VariableSubstitutionVisitor substitutionVisitor = new VariableSubstitutionVisitor(graphixRewritingContext); - loweringEnvironment.acceptTransformer(new ISequenceTransformer() { - private final Set<VarIdentifier> visitedVariables = new HashSet<>(); - - private void acceptLeftMatchJoin(ListIterator<AbstractBinaryCorrelateClause> clauseIterator, - JoinClause joinClause) throws CompilationException { - // We can make the following assumptions about our JOIN here (i.e. the casts here are valid). - Expression rightExpression = joinClause.getRightExpression(); - SelectExpression selectExpression = (SelectExpression) rightExpression; - SelectSetOperation selectSetOperation = selectExpression.getSelectSetOperation(); - SelectBlock selectBlock = selectSetOperation.getLeftInput().getSelectBlock(); - FromTerm fromTerm = selectBlock.getFromClause().getFromTerms().get(0); - for (AbstractBinaryCorrelateClause workingClause : fromTerm.getCorrelateClauses()) { - if (workingClause.getClauseType() == Clause.ClauseType.WHERE_CLAUSE) { - continue; - } - VarIdentifier rightVariable = workingClause.getRightVariable().getVar(); - - // Add our isomorphism conjunct to our main iterator. - Set<OperatorExpr> appliedIsomorphismConjuncts = new HashSet<>(); - for (OperatorExpr isomorphismConjunct : isomorphismConjuncts) { - List<Expression> operandList = isomorphismConjunct.getExprList(); - VarIdentifier termVariable1 = ((VariableExpr) operandList.get(0)).getVar(); - VarIdentifier termVariable2 = ((VariableExpr) operandList.get(1)).getVar(); - if (!termVariable1.equals(rightVariable) && !termVariable2.equals(rightVariable)) { - continue; - } - - // Add a substitution for our right variable. - VariableExpr nestingVariableExpr1 = new VariableExpr(joinClause.getRightVariable().getVar()); - VariableExpr nestingVariableExpr2 = new VariableExpr(joinClause.getRightVariable().getVar()); - FieldAccessor fieldAccessor1 = new FieldAccessor(nestingVariableExpr1, rightVariable); - FieldAccessor fieldAccessor2 = new FieldAccessor(nestingVariableExpr2, rightVariable); - substitutionVisitor.addSubstitution(rightVariable, fieldAccessor1); - Expression qualifiedConjunct = substitutionVisitor.visit(isomorphismConjunct, null); - - // Our right variable can also be optional. - FunctionSignature functionSignature = new FunctionSignature(BuiltinFunctions.IS_MISSING); - CallExpr isMissingCallExpr = new CallExpr(functionSignature, List.of(fieldAccessor2)); - OperatorExpr disjunctionExpr = new OperatorExpr(); - disjunctionExpr.addOperator(OperatorType.OR); - disjunctionExpr.addOperand(isMissingCallExpr); - disjunctionExpr.addOperand(qualifiedConjunct); - clauseIterator.add(new CorrWhereClause(disjunctionExpr)); - appliedIsomorphismConjuncts.add(isomorphismConjunct); - visitedVariables.add(rightVariable); - } - isomorphismConjuncts.removeAll(appliedIsomorphismConjuncts); - } - } - - @Override - public void accept(CorrelatedClauseSequence clauseSequence) throws CompilationException { - ListIterator<AbstractBinaryCorrelateClause> clauseIterator = clauseSequence.getMainIterator(); - while (clauseIterator.hasNext()) { - AbstractBinaryCorrelateClause workingClause = clauseIterator.next(); - if (workingClause.getClauseType() == Clause.ClauseType.WHERE_CLAUSE) { - continue; - } - visitedVariables.add(workingClause.getRightVariable().getVar()); - - // If we encounter a LEFT-JOIN, then we have created a LEFT-MATCH branch. - if (workingClause.getClauseType() == Clause.ClauseType.JOIN_CLAUSE) { - JoinClause joinClause = (JoinClause) workingClause; - if (joinClause.getJoinType() == JoinType.LEFTOUTER) { - acceptLeftMatchJoin(clauseIterator, joinClause); - } - } - - // Only introduce our conjunct if we have visited both variables. - Set<OperatorExpr> appliedIsomorphismConjuncts = new HashSet<>(); - for (OperatorExpr isomorphismConjunct : isomorphismConjuncts) { - List<Expression> operandList = isomorphismConjunct.getExprList(); - VarIdentifier termVariable1 = ((VariableExpr) operandList.get(0)).getVar(); - VarIdentifier termVariable2 = ((VariableExpr) operandList.get(1)).getVar(); - if (visitedVariables.contains(termVariable1) && visitedVariables.contains(termVariable2)) { - clauseIterator.add(new CorrWhereClause(isomorphismConjunct)); - appliedIsomorphismConjuncts.add(isomorphismConjunct); - } - } - isomorphismConjuncts.removeAll(appliedIsomorphismConjuncts); - } - } - }); - } -}
diff --git a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/lower/transform/CorrelatedClauseSequence.java b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/lower/transform/CorrelatedClauseSequence.java deleted file mode 100644 index 3ea61d1..0000000 --- a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/lower/transform/CorrelatedClauseSequence.java +++ /dev/null
@@ -1,89 +0,0 @@ -/* - * 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.graphix.lang.rewrites.lower.transform; - -import java.util.ArrayList; -import java.util.Iterator; -import java.util.LinkedList; -import java.util.List; -import java.util.ListIterator; - -import org.apache.asterix.graphix.lang.clause.CorrLetClause; -import org.apache.asterix.lang.common.base.Expression; -import org.apache.asterix.lang.common.expression.VariableExpr; -import org.apache.asterix.lang.common.struct.VarIdentifier; -import org.apache.asterix.lang.sqlpp.clause.AbstractBinaryCorrelateClause; - -/** - * A "general" list for maintaining a sequence of {@link AbstractBinaryCorrelateClause} AST nodes. We have: - * 1. A list of main clauses, which our callers have free rein to manipulate. - * 2. A list of deferred clauses, which will be inserted at the tail end of the final list. - * 3. A list of vertex bindings, which will be inserted immediately after the main clause list. - * 4. A list of edge bindings, which will be inserted immediately after the vertex binding list. - */ -public class CorrelatedClauseSequence implements Iterable<AbstractBinaryCorrelateClause> { - private final LinkedList<AbstractBinaryCorrelateClause> deferredCorrelatedClauseSequence = new LinkedList<>(); - private final LinkedList<AbstractBinaryCorrelateClause> mainCorrelatedClauseSequence = new LinkedList<>(); - private final List<CorrLetClause> representativeVertexBindings = new ArrayList<>(); - private final List<CorrLetClause> representativeEdgeBindings = new ArrayList<>(); - - public void addMainClause(AbstractBinaryCorrelateClause correlateClause) { - mainCorrelatedClauseSequence.addLast(correlateClause); - } - - public void addDeferredClause(AbstractBinaryCorrelateClause correlateClause) { - deferredCorrelatedClauseSequence.add(correlateClause); - } - - public void addRepresentativeVertexBinding(VarIdentifier representativeVariable, Expression bindingExpression) { - VariableExpr representativeVariableExpr = new VariableExpr(representativeVariable); - representativeVertexBindings.add(new CorrLetClause(bindingExpression, representativeVariableExpr, null)); - } - - public void addRepresentativeEdgeBinding(VarIdentifier representativeVariable, Expression bindingExpression) { - VariableExpr representativeVariableExpr = new VariableExpr(representativeVariable); - representativeEdgeBindings.add(new CorrLetClause(bindingExpression, representativeVariableExpr, null)); - } - - public AbstractBinaryCorrelateClause popMainClauseSequence() { - return mainCorrelatedClauseSequence.removeFirst(); - } - - public ListIterator<AbstractBinaryCorrelateClause> getMainIterator() { - return mainCorrelatedClauseSequence.listIterator(); - } - - public List<CorrLetClause> getRepresentativeVertexBindings() { - return representativeVertexBindings; - } - - public List<CorrLetClause> getRepresentativeEdgeBindings() { - return representativeEdgeBindings; - } - - @Override - public Iterator<AbstractBinaryCorrelateClause> iterator() { - List<AbstractBinaryCorrelateClause> correlateClauses = new ArrayList<>(); - correlateClauses.addAll(mainCorrelatedClauseSequence); - correlateClauses.addAll(representativeVertexBindings); - correlateClauses.addAll(representativeEdgeBindings); - correlateClauses.addAll(deferredCorrelatedClauseSequence); - return correlateClauses.listIterator(); - } -}
diff --git a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/resolve/IGraphElementResolver.java b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/resolve/IGraphElementResolver.java deleted file mode 100644 index aa939fb..0000000 --- a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/resolve/IGraphElementResolver.java +++ /dev/null
@@ -1,38 +0,0 @@ -/* - * 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.graphix.lang.rewrites.resolve; - -import org.apache.asterix.common.exceptions.CompilationException; -import org.apache.asterix.graphix.lang.clause.FromGraphClause; -import org.apache.asterix.graphix.lang.rewrites.visitor.StructureResolutionVisitor; - -/** - * @see StructureResolutionVisitor - */ -public interface IGraphElementResolver { - /** - * @param fromGraphClause FROM-GRAPH-CLAUSE to resolve edge & vertex labels, and edge directions for. - */ - void resolve(FromGraphClause fromGraphClause) throws CompilationException; - - /** - * @return True if we cannot resolve any more elements. False otherwise. - */ - boolean isAtFixedPoint(); -}
diff --git a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/resolve/InferenceBasedResolver.java b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/resolve/InferenceBasedResolver.java deleted file mode 100644 index 1d558fb..0000000 --- a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/resolve/InferenceBasedResolver.java +++ /dev/null
@@ -1,224 +0,0 @@ -/* - * 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.graphix.lang.rewrites.resolve; - -import java.util.Set; -import java.util.function.BiFunction; - -import org.apache.asterix.common.exceptions.CompilationException; -import org.apache.asterix.graphix.lang.clause.FromGraphClause; -import org.apache.asterix.graphix.lang.clause.MatchClause; -import org.apache.asterix.graphix.lang.expression.EdgePatternExpr; -import org.apache.asterix.graphix.lang.expression.PathPatternExpr; -import org.apache.asterix.graphix.lang.expression.VertexPatternExpr; -import org.apache.asterix.graphix.lang.rewrites.visitor.GraphixDeepCopyVisitor; -import org.apache.asterix.graphix.lang.rewrites.visitor.LabelConsistencyVisitor; -import org.apache.asterix.graphix.lang.rewrites.visitor.QueryKnowledgeVisitor; -import org.apache.asterix.graphix.lang.struct.EdgeDescriptor; -import org.apache.asterix.graphix.lang.struct.ElementLabel; - -/** - * Recursively attempt to resolve any element labels / edge directions in a FROM-GRAPH-CLAUSE. - */ -public class InferenceBasedResolver implements IGraphElementResolver { - public static final String METADATA_CONFIG_NAME = "inference-based"; - - private final QueryKnowledgeVisitor queryKnowledgeVisitor; - private final GraphixDeepCopyVisitor graphixDeepCopyVisitor; - private final SchemaKnowledgeTable schemaKnowledgeTable; - private boolean isAtFixedPoint = false; - - public InferenceBasedResolver(SchemaKnowledgeTable schemaKnowledgeTable) { - this.queryKnowledgeVisitor = new QueryKnowledgeVisitor(); - this.graphixDeepCopyVisitor = new GraphixDeepCopyVisitor(); - this.schemaKnowledgeTable = schemaKnowledgeTable; - } - - @Override - public void resolve(FromGraphClause fromGraphClause) throws CompilationException { - isAtFixedPoint = true; - - // Update our query knowledge, then unify vertices across our FROM-GRAPH-CLAUSE. - queryKnowledgeVisitor.visit(fromGraphClause, null); - new LabelConsistencyVisitor(queryKnowledgeVisitor.getQueryKnowledgeTable()).visit(fromGraphClause, null); - - // Perform our resolution. - for (MatchClause matchClause : fromGraphClause.getMatchClauses()) { - for (PathPatternExpr pathExpression : matchClause.getPathExpressions()) { - // We can only infer labels on edges. We ignore dangling vertices. - for (EdgePatternExpr edgeExpression : pathExpression.getEdgeExpressions()) { - isAtFixedPoint &= resolveEdge(edgeExpression); - } - } - } - } - - /** - * Attempt to resolve the unknowns in an {@link EdgePatternExpr}. This includes the labels of the contained - * vertices and edges, as well as the edge direction. - * @return True if no new information was added. False otherwise. - */ - private boolean resolveEdge(EdgePatternExpr edgePatternExpr) throws CompilationException { - EdgeDescriptor edgeDescriptor = edgePatternExpr.getEdgeDescriptor(); - if (edgeDescriptor.getPatternType() == EdgeDescriptor.PatternType.PATH) { - VertexPatternExpr workingLeftVertex = edgePatternExpr.getLeftVertex(); - - // We have a sub-path. Recurse with the edges of this sub-path. - boolean intermediateResult = true; - for (int i = 0; i < edgeDescriptor.getMaximumHops(); i++) { - VertexPatternExpr rightVertex; - if (i == edgeDescriptor.getMaximumHops() - 1) { - // This is the final vertex in our path. - rightVertex = edgePatternExpr.getRightVertex(); - - } else { - // We need to get an intermediate vertex. - rightVertex = edgePatternExpr.getInternalVertices().get(i); - } - - // Build our EDGE-PATTERN-EXPR and recurse. - EdgeDescriptor newDescriptor = new EdgeDescriptor(edgeDescriptor.getEdgeDirection(), - EdgeDescriptor.PatternType.EDGE, edgeDescriptor.getEdgeLabels(), null, null, null); - intermediateResult &= resolveEdge(new EdgePatternExpr(workingLeftVertex, rightVertex, newDescriptor)); - - // Update the labels of our edge and our internal vertex. - edgeDescriptor.getEdgeLabels().addAll(newDescriptor.getEdgeLabels()); - if (i != edgeDescriptor.getMaximumHops() - 1) { - for (ElementLabel label : rightVertex.getLabels()) { - // Mark our labels as not inferred to prevent invalidation of what we just found. - label.markInferred(false); - } - } - workingLeftVertex = rightVertex; - } - - return intermediateResult; - } - - if (edgeDescriptor.getEdgeDirection() == EdgeDescriptor.EdgeDirection.UNDIRECTED) { - // We have an undirected edge. Recurse with a LEFT_TO_RIGHT edge... - EdgePatternExpr leftToRightEdgePatternExpr = graphixDeepCopyVisitor.visit(edgePatternExpr, null); - leftToRightEdgePatternExpr.getEdgeDescriptor().setEdgeDirection(EdgeDescriptor.EdgeDirection.LEFT_TO_RIGHT); - boolean isLeftToRightModified = !resolveEdge(leftToRightEdgePatternExpr); - - // ...and a RIGHT_TO_LEFT edge. - EdgePatternExpr rightToLeftEdgePatternExpr = graphixDeepCopyVisitor.visit(edgePatternExpr, null); - rightToLeftEdgePatternExpr.getEdgeDescriptor().setEdgeDirection(EdgeDescriptor.EdgeDirection.RIGHT_TO_LEFT); - boolean isRightToLeftModified = !resolveEdge(rightToLeftEdgePatternExpr); - - // Determine the direction of our edge, if possible. - if (isLeftToRightModified && !isRightToLeftModified) { - edgeDescriptor.setEdgeDirection(EdgeDescriptor.EdgeDirection.LEFT_TO_RIGHT); - - } else if (!isLeftToRightModified && isRightToLeftModified) { - edgeDescriptor.setEdgeDirection(EdgeDescriptor.EdgeDirection.RIGHT_TO_LEFT); - - } else { - edgeDescriptor.setEdgeDirection(EdgeDescriptor.EdgeDirection.UNDIRECTED); - } - - // Propagate our label sets. - VertexPatternExpr leftVertexExpr = edgePatternExpr.getLeftVertex(); - VertexPatternExpr rightVertexExpr = edgePatternExpr.getRightVertex(); - if (isLeftToRightModified) { - edgeDescriptor.getEdgeLabels().addAll(leftToRightEdgePatternExpr.getEdgeDescriptor().getEdgeLabels()); - leftVertexExpr.getLabels().addAll(leftToRightEdgePatternExpr.getLeftVertex().getLabels()); - rightVertexExpr.getLabels().addAll(leftToRightEdgePatternExpr.getRightVertex().getLabels()); - } - if (isRightToLeftModified) { - edgeDescriptor.getEdgeLabels().addAll(rightToLeftEdgePatternExpr.getEdgeDescriptor().getEdgeLabels()); - leftVertexExpr.getLabels().addAll(rightToLeftEdgePatternExpr.getLeftVertex().getLabels()); - rightVertexExpr.getLabels().addAll(rightToLeftEdgePatternExpr.getRightVertex().getLabels()); - } - return !(isLeftToRightModified || isRightToLeftModified); - } - - // We have a _directed_ *edge*. Determine our source and destination vertex. - VertexPatternExpr sourceVertexPattern, destinationVertexPattern; - if (edgeDescriptor.getEdgeDirection() == EdgeDescriptor.EdgeDirection.LEFT_TO_RIGHT) { - sourceVertexPattern = edgePatternExpr.getLeftVertex(); - destinationVertexPattern = edgePatternExpr.getRightVertex(); - - } else { // edgeDescriptor.getEdgeDirection() == EdgeDescriptor.EdgeDirection.RIGHT_TO_LEFT - sourceVertexPattern = edgePatternExpr.getRightVertex(); - destinationVertexPattern = edgePatternExpr.getLeftVertex(); - } - - // An unknown is valid iff the element is not contained within the label set and **every** label is inferred. - final BiFunction<Set<ElementLabel>, ElementLabel, Boolean> validUnknownPredicate = - (s, e) -> !s.contains(e) && s.stream().allMatch(ElementLabel::isInferred); - Set<ElementLabel> sourceLabels = sourceVertexPattern.getLabels(); - Set<ElementLabel> destLabels = destinationVertexPattern.getLabels(); - Set<ElementLabel> edgeLabels = edgeDescriptor.getEdgeLabels(); - - // Iterate through our knowledge table. Attempt to fill in unknowns. - boolean isUnknownFilled = false; - for (SchemaKnowledgeTable.KnowledgeRecord knowledgeRecord : schemaKnowledgeTable) { - ElementLabel recordSourceLabel = knowledgeRecord.getSourceVertexLabel(); - ElementLabel recordDestLabel = knowledgeRecord.getDestVertexLabel(); - ElementLabel recordEdgeLabel = knowledgeRecord.getEdgeLabel(); - boolean isSourceValidUnknown = validUnknownPredicate.apply(sourceLabels, recordSourceLabel); - boolean isDestValidUnknown = validUnknownPredicate.apply(destLabels, recordDestLabel); - boolean isEdgeValidUnknown = validUnknownPredicate.apply(edgeLabels, recordEdgeLabel); - if (sourceLabels.contains(recordSourceLabel)) { - if (edgeLabels.contains(recordEdgeLabel) && isDestValidUnknown) { - // Source is known, edge is known, dest is unknown. - destLabels.add(recordDestLabel.asInferred()); - isUnknownFilled = true; - - } else if (isEdgeValidUnknown && destLabels.contains(recordDestLabel)) { - // Source is known, edge is unknown, dest is known. - edgeLabels.add(recordEdgeLabel.asInferred()); - isUnknownFilled = true; - - } else if (isEdgeValidUnknown && isDestValidUnknown) { - // Source is known, edge is unknown, dest is unknown. - destLabels.add(recordDestLabel.asInferred()); - edgeLabels.add(recordEdgeLabel.asInferred()); - isUnknownFilled = true; - } - - } else if (edgeLabels.contains(recordEdgeLabel)) { - if (isSourceValidUnknown && destLabels.contains(recordDestLabel)) { - // Source is unknown, edge is known, dest is known. - sourceLabels.add(recordSourceLabel.asInferred()); - isUnknownFilled = true; - - } else if (isSourceValidUnknown && isDestValidUnknown) { - // Source is unknown, edge is known, dest is unknown. - sourceLabels.add(recordSourceLabel.asInferred()); - destLabels.add(recordDestLabel.asInferred()); - isUnknownFilled = true; - } - - } else if (destLabels.contains(recordDestLabel) && isSourceValidUnknown && isEdgeValidUnknown) { - // Source is unknown, edge is unknown, dest is known. - sourceLabels.add(recordSourceLabel.asInferred()); - edgeLabels.add(recordEdgeLabel.asInferred()); - isUnknownFilled = true; - } - } - return !isUnknownFilled; - } - - @Override - public boolean isAtFixedPoint() { - return isAtFixedPoint; - } -}
diff --git a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/resolve/QueryKnowledgeTable.java b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/resolve/QueryKnowledgeTable.java deleted file mode 100644 index 23b54e4..0000000 --- a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/resolve/QueryKnowledgeTable.java +++ /dev/null
@@ -1,46 +0,0 @@ -/* - * 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.graphix.lang.rewrites.resolve; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; - -import org.apache.asterix.graphix.lang.expression.VertexPatternExpr; -import org.apache.asterix.graphix.lang.struct.ElementLabel; -import org.apache.asterix.lang.common.struct.Identifier; - -/** - * A knowledge base of our query graph, stored as a map of vertex variable IDs to a list of vertex labels. - * - * @see InferenceBasedResolver - */ -public class QueryKnowledgeTable extends HashMap<Identifier, List<ElementLabel>> { - private static final long serialVersionUID = 1L; - - public void put(VertexPatternExpr vertexPatternExpr) { - Identifier vertexIdentifier = vertexPatternExpr.getVariableExpr().getVar(); - if (!vertexPatternExpr.getLabels().isEmpty()) { - if (!super.containsKey(vertexIdentifier)) { - super.put(vertexIdentifier, new ArrayList<>()); - } - super.get(vertexIdentifier).addAll(vertexPatternExpr.getLabels()); - } - } -}
diff --git a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/resolve/SchemaKnowledgeTable.java b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/resolve/SchemaKnowledgeTable.java deleted file mode 100644 index 1b2984d..0000000 --- a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/resolve/SchemaKnowledgeTable.java +++ /dev/null
@@ -1,120 +0,0 @@ -/* - * 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.graphix.lang.rewrites.resolve; - -import java.util.HashSet; -import java.util.Iterator; -import java.util.Objects; -import java.util.Set; - -import org.apache.asterix.graphix.lang.expression.GraphConstructor; -import org.apache.asterix.graphix.lang.struct.ElementLabel; -import org.apache.asterix.graphix.metadata.entity.schema.Graph; -import org.apache.asterix.graphix.metadata.entity.schema.Vertex; - -/** - * A collection of ground truths, derived from the graph schema (either a {@link Graph} or {@link GraphConstructor}). - * - * @see InferenceBasedResolver - */ -public class SchemaKnowledgeTable implements Iterable<SchemaKnowledgeTable.KnowledgeRecord> { - private final Set<KnowledgeRecord> knowledgeRecordSet = new HashSet<>(); - private final Set<ElementLabel> vertexLabelSet = new HashSet<>(); - private final Set<ElementLabel> edgeLabelSet = new HashSet<>(); - - public SchemaKnowledgeTable(Graph graph) { - graph.getGraphSchema().getVertices().stream().map(Vertex::getLabel).forEach(vertexLabelSet::add); - graph.getGraphSchema().getEdges().forEach(e -> { - vertexLabelSet.add(e.getSourceLabel()); - vertexLabelSet.add(e.getDestinationLabel()); - edgeLabelSet.add(e.getLabel()); - knowledgeRecordSet.add(new KnowledgeRecord(e.getSourceLabel(), e.getDestinationLabel(), e.getLabel())); - }); - } - - public SchemaKnowledgeTable(GraphConstructor graphConstructor) { - graphConstructor.getVertexElements().forEach(v -> vertexLabelSet.add(v.getLabel())); - graphConstructor.getEdgeElements().forEach(e -> { - vertexLabelSet.add(e.getSourceLabel()); - vertexLabelSet.add(e.getDestinationLabel()); - edgeLabelSet.add(e.getEdgeLabel()); - knowledgeRecordSet.add(new KnowledgeRecord(e.getSourceLabel(), e.getDestinationLabel(), e.getEdgeLabel())); - }); - } - - public Set<ElementLabel> getVertexLabelSet() { - return vertexLabelSet; - } - - public Set<ElementLabel> getEdgeLabelSet() { - return edgeLabelSet; - } - - @Override - public Iterator<KnowledgeRecord> iterator() { - return knowledgeRecordSet.iterator(); - } - - public static class KnowledgeRecord { - private final ElementLabel sourceVertexLabel; - private final ElementLabel destVertexLabel; - private final ElementLabel edgeLabel; - - public KnowledgeRecord(ElementLabel sourceVertexLabel, ElementLabel destVertexLabel, ElementLabel edgeLabel) { - this.sourceVertexLabel = Objects.requireNonNull(sourceVertexLabel); - this.destVertexLabel = Objects.requireNonNull(destVertexLabel); - this.edgeLabel = Objects.requireNonNull(edgeLabel); - } - - public ElementLabel getSourceVertexLabel() { - return sourceVertexLabel; - } - - public ElementLabel getDestVertexLabel() { - return destVertexLabel; - } - - public ElementLabel getEdgeLabel() { - return edgeLabel; - } - - @Override - public String toString() { - return String.format("(%s)-[%s]->(%s)", sourceVertexLabel, edgeLabel, destVertexLabel); - } - - @Override - public boolean equals(Object object) { - if (this == object) { - return true; - } - if (!(object instanceof KnowledgeRecord)) { - return false; - } - KnowledgeRecord target = (KnowledgeRecord) object; - return sourceVertexLabel.equals(target.sourceVertexLabel) && destVertexLabel.equals(target.destVertexLabel) - && edgeLabel.equals(target.edgeLabel); - } - - @Override - public int hashCode() { - return Objects.hash(sourceVertexLabel, destVertexLabel, edgeLabel); - } - } -}
diff --git a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/visitor/CanonicalExpansionVisitor.java b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/visitor/CanonicalExpansionVisitor.java deleted file mode 100644 index b7b1b33..0000000 --- a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/visitor/CanonicalExpansionVisitor.java +++ /dev/null
@@ -1,279 +0,0 @@ -/* - * 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.graphix.lang.rewrites.visitor; - -import static org.apache.asterix.graphix.extension.GraphixMetadataExtension.getGraph; - -import java.util.ArrayDeque; -import java.util.ArrayList; -import java.util.Deque; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import org.apache.asterix.common.exceptions.CompilationException; -import org.apache.asterix.common.exceptions.ErrorCode; -import org.apache.asterix.common.metadata.DataverseName; -import org.apache.asterix.graphix.common.metadata.GraphIdentifier; -import org.apache.asterix.graphix.lang.clause.FromGraphClause; -import org.apache.asterix.graphix.lang.clause.GraphSelectBlock; -import org.apache.asterix.graphix.lang.clause.MatchClause; -import org.apache.asterix.graphix.lang.expression.EdgePatternExpr; -import org.apache.asterix.graphix.lang.expression.PathPatternExpr; -import org.apache.asterix.graphix.lang.expression.VertexPatternExpr; -import org.apache.asterix.graphix.lang.rewrites.GraphixRewritingContext; -import org.apache.asterix.graphix.lang.rewrites.canonical.DanglingVertexExpander; -import org.apache.asterix.graphix.lang.rewrites.canonical.EdgeSubPathExpander; -import org.apache.asterix.graphix.lang.rewrites.resolve.SchemaKnowledgeTable; -import org.apache.asterix.graphix.lang.statement.DeclareGraphStatement; -import org.apache.asterix.graphix.metadata.entity.schema.Graph; -import org.apache.asterix.lang.common.base.AbstractClause; -import org.apache.asterix.lang.common.base.Clause; -import org.apache.asterix.lang.common.base.Expression; -import org.apache.asterix.lang.common.base.ILangExpression; -import org.apache.asterix.lang.common.clause.LetClause; -import org.apache.asterix.lang.common.struct.Identifier; -import org.apache.asterix.lang.common.struct.VarIdentifier; -import org.apache.asterix.lang.sqlpp.clause.AbstractBinaryCorrelateClause; -import org.apache.asterix.lang.sqlpp.clause.SelectBlock; -import org.apache.asterix.lang.sqlpp.clause.SelectSetOperation; -import org.apache.asterix.lang.sqlpp.expression.SelectExpression; -import org.apache.asterix.lang.sqlpp.optype.SetOpType; -import org.apache.asterix.lang.sqlpp.struct.SetOperationInput; -import org.apache.asterix.lang.sqlpp.struct.SetOperationRight; -import org.apache.asterix.metadata.declared.MetadataProvider; -import org.apache.hyracks.algebricks.common.exceptions.AlgebricksException; - -/** - * Expand a single {@link SelectSetOperation} with {@link VertexPatternExpr} and {@link EdgePatternExpr} nodes into - * several UNION-ALL branches of "canonical form". This preprocessing step allows for simpler lowering logic (and - * therefore, potentially more efficient plans) at the cost of a wider AST. - * - * @see DanglingVertexExpander - * @see EdgeSubPathExpander - */ -public class CanonicalExpansionVisitor extends AbstractGraphixQueryVisitor { - private final GraphixRewritingContext graphixRewritingContext; - private final Deque<CanonicalPatternEnvironment> environmentStack; - private final DanglingVertexExpander danglingVertexExpander; - private final EdgeSubPathExpander edgeSubPathExpander; - - // To avoid "over-expanding", we require knowledge of our schema. - private final Map<GraphIdentifier, DeclareGraphStatement> declaredGraphs; - private final MetadataProvider metadataProvider; - - private static class CanonicalPatternEnvironment { - private List<GraphSelectBlock> graphSelectBlockList; - private List<SetOperationInput> setOperationInputList; - private SchemaKnowledgeTable schemaKnowledgeTable; - private boolean wasExpanded = false; - - // The following is collected for our post-canonicalization pass. - private GraphSelectBlock selectBlockExpansionSource; - private Set<SetOperationInput> generatedSetInputs; - private List<VarIdentifier> userLiveVariables; - } - - public CanonicalExpansionVisitor(GraphixRewritingContext graphixRewritingContext) { - this.metadataProvider = graphixRewritingContext.getMetadataProvider(); - this.declaredGraphs = graphixRewritingContext.getDeclaredGraphs(); - this.danglingVertexExpander = new DanglingVertexExpander(); - this.edgeSubPathExpander = new EdgeSubPathExpander(graphixRewritingContext::getNewGraphixVariable); - this.graphixRewritingContext = graphixRewritingContext; - - // Keep an empty environment in the stack, in case we have a non-SELECT expression. - this.environmentStack = new ArrayDeque<>(); - this.environmentStack.addLast(new CanonicalPatternEnvironment()); - } - - @Override - public Expression visit(SelectExpression selectExpression, ILangExpression arg) throws CompilationException { - environmentStack.addLast(new CanonicalPatternEnvironment()); - super.visit(selectExpression, arg); - - // If expansion has occurred, we need to perform clean-up if we have output modifiers / grouping. - CanonicalPatternEnvironment workingEnvironment = environmentStack.removeLast(); - SelectExpression workingSelectExpression = selectExpression; - if (workingEnvironment.wasExpanded && (selectExpression.hasLimit() || selectExpression.hasOrderby() - || workingEnvironment.graphSelectBlockList.stream().anyMatch(SelectBlock::hasGroupbyClause))) { - PostCanonicalizationVisitor postCanonicalizationVisitor = new PostCanonicalizationVisitor( - graphixRewritingContext, workingEnvironment.selectBlockExpansionSource, - workingEnvironment.generatedSetInputs, workingEnvironment.userLiveVariables); - workingSelectExpression = (SelectExpression) postCanonicalizationVisitor.visit(selectExpression, arg); - } - return workingSelectExpression; - } - - @Override - public Expression visit(SelectSetOperation selectSetOperation, ILangExpression arg) throws CompilationException { - CanonicalPatternEnvironment workingEnvironment = environmentStack.getLast(); - workingEnvironment.setOperationInputList = new ArrayList<>(); - selectSetOperation.getLeftInput().accept(this, arg); - for (SetOperationRight right : selectSetOperation.getRightInputs()) { - right.getSetOperationRightInput().accept(this, arg); - } - for (int i = 0; i < workingEnvironment.setOperationInputList.size(); i++) { - SetOperationInput setOperationInput = workingEnvironment.setOperationInputList.get(i); - if (i == 0) { - selectSetOperation.getLeftInput().setSelectBlock(setOperationInput.getSelectBlock()); - selectSetOperation.getLeftInput().setSubquery(setOperationInput.getSubquery()); - - } else if (selectSetOperation.getRightInputs().size() > i) { - SetOperationInput setOperationRightInput = - selectSetOperation.getRightInputs().get(i - 1).getSetOperationRightInput(); - setOperationRightInput.setSelectBlock(setOperationInput.getSelectBlock()); - setOperationRightInput.setSubquery(setOperationRightInput.getSubquery()); - - } else { - SetOperationRight setOperationRight = new SetOperationRight(SetOpType.UNION, false, setOperationInput); - selectSetOperation.getRightInputs().add(setOperationRight); - } - } - return null; - } - - @Override - public Expression visit(GraphSelectBlock graphSelectBlock, ILangExpression arg) throws CompilationException { - CanonicalPatternEnvironment workingEnvironment = environmentStack.getLast(); - workingEnvironment.graphSelectBlockList = new ArrayList<>(); - workingEnvironment.graphSelectBlockList.add(graphSelectBlock); - super.visit(graphSelectBlock, arg); - - if (workingEnvironment.wasExpanded) { - // If we have expanded, then we need to keep track of this GRAPH-SELECT-BLOCK. - workingEnvironment.selectBlockExpansionSource = new GraphixDeepCopyVisitor().visit(graphSelectBlock, null); - - // We also need to collect all live variables up to this point. - workingEnvironment.userLiveVariables = new ArrayList<>(); - for (MatchClause matchClause : graphSelectBlock.getFromGraphClause().getMatchClauses()) { - for (PathPatternExpr pathExpression : matchClause.getPathExpressions()) { - if (pathExpression.getVariableExpr() != null) { - workingEnvironment.userLiveVariables.add(pathExpression.getVariableExpr().getVar()); - } - for (VertexPatternExpr vertexExpression : pathExpression.getVertexExpressions()) { - VarIdentifier vertexVariable = vertexExpression.getVariableExpr().getVar(); - if (!GraphixRewritingContext.isGraphixVariable(vertexVariable)) { - workingEnvironment.userLiveVariables.add(vertexVariable); - } - } - for (EdgePatternExpr edgeExpression : pathExpression.getEdgeExpressions()) { - VarIdentifier edgeVariable = edgeExpression.getEdgeDescriptor().getVariableExpr().getVar(); - if (!GraphixRewritingContext.isGraphixVariable(edgeVariable)) { - workingEnvironment.userLiveVariables.add(edgeVariable); - } - } - } - } - if (!graphSelectBlock.getFromGraphClause().getCorrelateClauses().isEmpty()) { - FromGraphClause fromGraphClause = graphSelectBlock.getFromGraphClause(); - List<AbstractBinaryCorrelateClause> correlateClauses = fromGraphClause.getCorrelateClauses(); - for (AbstractBinaryCorrelateClause correlateClause : correlateClauses) { - VarIdentifier bindingVariable = correlateClause.getRightVariable().getVar(); - if (!GraphixRewritingContext.isGraphixVariable(bindingVariable)) { - workingEnvironment.userLiveVariables.add(bindingVariable); - } - } - } - if (graphSelectBlock.hasLetWhereClauses()) { - for (AbstractClause abstractClause : graphSelectBlock.getLetWhereList()) { - if (abstractClause.getClauseType() == Clause.ClauseType.LET_CLAUSE) { - LetClause letClause = (LetClause) abstractClause; - VarIdentifier bindingVariable = letClause.getVarExpr().getVar(); - if (!GraphixRewritingContext.isGraphixVariable(bindingVariable)) { - workingEnvironment.userLiveVariables.add(bindingVariable); - } - } - } - } - } - return null; - } - - @Override - public Expression visit(FromGraphClause fromGraphClause, ILangExpression arg) throws CompilationException { - CanonicalPatternEnvironment workingEnvironment = environmentStack.getLast(); - - // Establish our schema knowledge. - if (fromGraphClause.getGraphConstructor() == null) { - DataverseName dataverseName = (fromGraphClause.getDataverseName() == null) - ? metadataProvider.getDefaultDataverseName() : fromGraphClause.getDataverseName(); - Identifier graphName = fromGraphClause.getGraphName(); - - // First, try to find our graph inside our declared graph set. - GraphIdentifier graphIdentifier = new GraphIdentifier(dataverseName, graphName.getValue()); - DeclareGraphStatement declaredGraph = declaredGraphs.get(graphIdentifier); - if (declaredGraph != null) { - workingEnvironment.schemaKnowledgeTable = new SchemaKnowledgeTable(declaredGraph.getGraphConstructor()); - - } else { - // Otherwise, fetch the graph from our metadata. - try { - Graph graphFromMetadata = - getGraph(metadataProvider.getMetadataTxnContext(), dataverseName, graphName.getValue()); - if (graphFromMetadata == null) { - throw new CompilationException(ErrorCode.COMPILATION_ERROR, fromGraphClause.getSourceLocation(), - "Graph " + graphName.getValue() + " does not exist."); - } - workingEnvironment.schemaKnowledgeTable = new SchemaKnowledgeTable(graphFromMetadata); - - } catch (AlgebricksException e) { - throw new CompilationException(ErrorCode.COMPILATION_ERROR, fromGraphClause.getSourceLocation(), - "Graph " + graphName.getValue() + " does not exist."); - } - } - - } else { - workingEnvironment.schemaKnowledgeTable = new SchemaKnowledgeTable(fromGraphClause.getGraphConstructor()); - } - super.visit(fromGraphClause, arg); - - // Create SOI from our GSBs back to our immediate ancestor SELECT-EXPR. - if (workingEnvironment.graphSelectBlockList.size() > 1) { - workingEnvironment.generatedSetInputs = new HashSet<>(); - workingEnvironment.wasExpanded = true; - } - for (GraphSelectBlock graphSelectBlock : workingEnvironment.graphSelectBlockList) { - SetOperationInput setOperationInput = new SetOperationInput(graphSelectBlock, null); - workingEnvironment.setOperationInputList.add(setOperationInput); - if (workingEnvironment.wasExpanded) { - workingEnvironment.generatedSetInputs.add(setOperationInput); - } - } - return null; - } - - @Override - public Expression visit(PathPatternExpr pathPatternExpr, ILangExpression arg) throws CompilationException { - CanonicalPatternEnvironment workingEnvironment = environmentStack.getLast(); - Set<VertexPatternExpr> connectedVertices = new HashSet<>(); - for (EdgePatternExpr edgeExpression : pathPatternExpr.getEdgeExpressions()) { - connectedVertices.add(edgeExpression.getLeftVertex()); - connectedVertices.add(edgeExpression.getRightVertex()); - edgeSubPathExpander.resetSchema(workingEnvironment.schemaKnowledgeTable); - edgeSubPathExpander.apply(edgeExpression, workingEnvironment.graphSelectBlockList); - } - for (VertexPatternExpr vertexExpression : pathPatternExpr.getVertexExpressions()) { - if (!connectedVertices.contains(vertexExpression)) { - danglingVertexExpander.apply(vertexExpression, workingEnvironment.graphSelectBlockList); - } - } - return pathPatternExpr; - } -}
diff --git a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/visitor/GraphixLoweringVisitor.java b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/visitor/GraphixLoweringVisitor.java deleted file mode 100644 index 0b55145..0000000 --- a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/visitor/GraphixLoweringVisitor.java +++ /dev/null
@@ -1,315 +0,0 @@ -/* - * 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.graphix.lang.rewrites.visitor; - -import java.util.ArrayDeque; -import java.util.Deque; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Set; -import java.util.function.Function; - -import org.apache.asterix.common.exceptions.CompilationException; -import org.apache.asterix.common.exceptions.ErrorCode; -import org.apache.asterix.common.functions.FunctionSignature; -import org.apache.asterix.common.metadata.DataverseName; -import org.apache.asterix.graphix.common.metadata.GraphElementIdentifier; -import org.apache.asterix.graphix.common.metadata.GraphIdentifier; -import org.apache.asterix.graphix.function.GraphixFunctionIdentifiers; -import org.apache.asterix.graphix.lang.clause.FromGraphClause; -import org.apache.asterix.graphix.lang.clause.GraphSelectBlock; -import org.apache.asterix.graphix.lang.expression.EdgePatternExpr; -import org.apache.asterix.graphix.lang.expression.PathPatternExpr; -import org.apache.asterix.graphix.lang.expression.VertexPatternExpr; -import org.apache.asterix.graphix.lang.rewrites.GraphixRewritingContext; -import org.apache.asterix.graphix.lang.rewrites.common.ElementLookupTable; -import org.apache.asterix.graphix.lang.rewrites.lower.EnvironmentActionFactory; -import org.apache.asterix.graphix.lang.rewrites.lower.LoweringAliasLookupTable; -import org.apache.asterix.graphix.lang.rewrites.lower.LoweringEnvironment; -import org.apache.asterix.graphix.lang.rewrites.visitor.ElementBodyAnalysisVisitor.ElementBodyAnalysisContext; -import org.apache.asterix.graphix.lang.rewrites.visitor.StructureAnalysisVisitor.StructureContext; -import org.apache.asterix.graphix.lang.statement.GraphElementDeclaration; -import org.apache.asterix.graphix.lang.struct.EdgeDescriptor; -import org.apache.asterix.lang.common.base.Expression; -import org.apache.asterix.lang.common.base.ILangExpression; -import org.apache.asterix.lang.common.expression.CallExpr; -import org.apache.asterix.lang.common.struct.VarIdentifier; -import org.apache.asterix.lang.sqlpp.clause.FromTerm; -import org.apache.asterix.lang.sqlpp.expression.SelectExpression; -import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier; - -/** - * Rewrite a graph AST to utilize non-graph AST nodes (i.e. replace GRAPH-SELECT-BLOCKs with a SELECT-BLOCK). - */ -public class GraphixLoweringVisitor extends AbstractGraphixQueryVisitor { - private final Map<FromGraphClause, StructureContext> fromGraphClauseContextMap; - private final ElementLookupTable elementLookupTable; - private final GraphixRewritingContext graphixRewritingContext; - private SelectExpression topLevelSelectExpression; - - // Our stack corresponds to which GRAPH-SELECT-BLOCK we are currently working with. - private final Map<GraphElementIdentifier, ElementBodyAnalysisContext> analysisContextMap; - private final Deque<LoweringEnvironment> environmentStack; - private final LoweringAliasLookupTable aliasLookupTable; - private final EnvironmentActionFactory environmentActionFactory; - - public GraphixLoweringVisitor(GraphixRewritingContext graphixRewritingContext, - ElementLookupTable elementLookupTable, Map<FromGraphClause, StructureContext> fromGraphClauseContextMap) { - this.fromGraphClauseContextMap = Objects.requireNonNull(fromGraphClauseContextMap); - this.elementLookupTable = Objects.requireNonNull(elementLookupTable); - this.graphixRewritingContext = Objects.requireNonNull(graphixRewritingContext); - this.aliasLookupTable = new LoweringAliasLookupTable(); - this.environmentStack = new ArrayDeque<>(); - - // All actions on our environment are supplied by the factory below. - Map<GraphElementIdentifier, ElementBodyAnalysisContext> bodyAnalysisContextMap = new HashMap<>(); - this.environmentActionFactory = new EnvironmentActionFactory(bodyAnalysisContextMap, elementLookupTable, - aliasLookupTable, graphixRewritingContext); - this.analysisContextMap = bodyAnalysisContextMap; - } - - @Override - public Expression visit(SelectExpression selectExpression, ILangExpression arg) throws CompilationException { - if (!selectExpression.isSubquery() || topLevelSelectExpression == null) { - topLevelSelectExpression = selectExpression; - } - return super.visit(selectExpression, arg); - } - - @Override - public Expression visit(GraphSelectBlock graphSelectBlock, ILangExpression arg) throws CompilationException { - SelectExpression selectExpression = (SelectExpression) arg; - if (graphSelectBlock.hasFromGraphClause()) { - FromGraphClause fromGraphClause = graphSelectBlock.getFromGraphClause(); - - // Initialize a new lowering environment. - GraphIdentifier graphIdentifier = fromGraphClauseContextMap.get(fromGraphClause).getGraphIdentifier(); - LoweringEnvironment newEnvironment = new LoweringEnvironment(graphSelectBlock, graphixRewritingContext); - environmentActionFactory.reset(graphIdentifier); - - // We will remove the FROM-GRAPH node and replace this with a FROM node on the child visit. - environmentStack.addLast(newEnvironment); - super.visit(graphSelectBlock, graphSelectBlock); - environmentStack.removeLast(); - - // See if there are Graphix functions declared anywhere in our query. - Set<FunctionIdentifier> graphixFunctionSet = new HashSet<>(); - topLevelSelectExpression.accept(new AbstractGraphixQueryVisitor() { - @Override - public Expression visit(CallExpr callExpr, ILangExpression arg) throws CompilationException { - FunctionSignature functionSignature = callExpr.getFunctionSignature(); - if (functionSignature.getDataverseName().equals(GraphixFunctionIdentifiers.GRAPHIX_DV)) { - graphixFunctionSet.add(functionSignature.createFunctionIdentifier()); - } - return super.visit(callExpr, arg); - } - }, null); - - // If so, then we need to perform a pass for schema enrichment. - if (!graphixFunctionSet.isEmpty()) { - SchemaEnrichmentVisitor schemaEnrichmentVisitor = new SchemaEnrichmentVisitor(elementLookupTable, - graphIdentifier, graphSelectBlock, graphixFunctionSet); - selectExpression.accept(schemaEnrichmentVisitor, null); - if (selectExpression.hasOrderby()) { - selectExpression.getOrderbyClause().accept(schemaEnrichmentVisitor, null); - } - if (selectExpression.hasLimit()) { - selectExpression.getLimitClause().accept(schemaEnrichmentVisitor, null); - } - } - - } else { - super.visit(graphSelectBlock, arg); - } - return null; - } - - @Override - public Expression visit(FromGraphClause fromGraphClause, ILangExpression arg) throws CompilationException { - // Perform an analysis pass over each element body. We need to determine what we can and can't inline. - for (GraphElementDeclaration graphElementDeclaration : elementLookupTable) { - ElementBodyAnalysisVisitor elementBodyAnalysisVisitor = new ElementBodyAnalysisVisitor(); - GraphElementIdentifier elementIdentifier = graphElementDeclaration.getIdentifier(); - graphElementDeclaration.getNormalizedBody().accept(elementBodyAnalysisVisitor, null); - analysisContextMap.put(elementIdentifier, elementBodyAnalysisVisitor.getElementBodyAnalysisContext()); - } - LoweringEnvironment workingEnvironment = environmentStack.getLast(); - - // Lower our MATCH-CLAUSEs. We should be working with canonical-ized patterns. - boolean wasInitialEdgeOrderingEncountered = false; - StructureContext structureContext = fromGraphClauseContextMap.get(fromGraphClause); - Deque<List<VertexPatternExpr>> danglingVertexQueue = structureContext.getDanglingVertexQueue(); - Deque<List<PathPatternExpr>> pathPatternQueue = structureContext.getPathPatternQueue(); - for (Iterable<EdgePatternExpr> edgeOrdering : structureContext.getEdgeDependencyGraph()) { - if (wasInitialEdgeOrderingEncountered) { - workingEnvironment.beginLeftMatch(); - } - for (EdgePatternExpr edgePatternExpr : edgeOrdering) { - edgePatternExpr.accept(this, fromGraphClause); - } - for (VertexPatternExpr danglingVertexExpr : danglingVertexQueue.removeFirst()) { - workingEnvironment.acceptAction(environmentActionFactory.buildDanglingVertexAction(danglingVertexExpr)); - } - if (wasInitialEdgeOrderingEncountered) { - workingEnvironment.endLeftMatch(graphixRewritingContext.getWarningCollector()); - } - for (PathPatternExpr pathPatternExpr : pathPatternQueue.removeFirst()) { - workingEnvironment.acceptAction(environmentActionFactory.buildPathPatternAction(pathPatternExpr)); - } - wasInitialEdgeOrderingEncountered = true; - } - if (!structureContext.getEdgeDependencyGraph().iterator().hasNext()) { - for (VertexPatternExpr danglingVertexExpr : danglingVertexQueue.removeFirst()) { - workingEnvironment.acceptAction(environmentActionFactory.buildDanglingVertexAction(danglingVertexExpr)); - } - for (PathPatternExpr pathPatternExpr : pathPatternQueue.removeFirst()) { - workingEnvironment.acceptAction(environmentActionFactory.buildPathPatternAction(pathPatternExpr)); - } - } - workingEnvironment.acceptAction(environmentActionFactory.buildIsomorphismAction(fromGraphClause)); - - // Finalize our lowering by removing this FROM-GRAPH-CLAUSE from our parent GRAPH-SELECT-BLOCK. - workingEnvironment.finalizeLowering(fromGraphClause, graphixRewritingContext.getWarningCollector()); - - // Add our correlate clauses, if any, to our tail FROM-TERM. - if (!fromGraphClause.getCorrelateClauses().isEmpty()) { - GraphSelectBlock graphSelectBlock = (GraphSelectBlock) arg; - List<FromTerm> fromTerms = graphSelectBlock.getFromClause().getFromTerms(); - FromTerm tailFromTerm = fromTerms.get(fromTerms.size() - 1); - tailFromTerm.getCorrelateClauses().addAll(fromGraphClause.getCorrelateClauses()); - } - return null; - } - - @Override - public Expression visit(EdgePatternExpr edgePatternExpr, ILangExpression arg) throws CompilationException { - EdgeDescriptor edgeDescriptor = edgePatternExpr.getEdgeDescriptor(); - LoweringEnvironment lowerEnvironment = environmentStack.getLast(); - - // We should only be working with one identifier (given that we only have one label). - GraphIdentifier graphIdentifier = fromGraphClauseContextMap.get((FromGraphClause) arg).getGraphIdentifier(); - List<GraphElementIdentifier> edgeElementIDs = edgeDescriptor.generateIdentifiers(graphIdentifier); - if (edgeElementIDs.size() != 1) { - throw new CompilationException(ErrorCode.COMPILATION_ILLEGAL_STATE, "Found non-canonical edge pattern!"); - } - GraphElementIdentifier edgeIdentifier = edgeElementIDs.get(0); - ElementBodyAnalysisContext edgeBodyAnalysisContext = analysisContextMap.get(edgeIdentifier); - DataverseName edgeDataverseName = edgeBodyAnalysisContext.getDataverseName(); - String edgeDatasetName = edgeBodyAnalysisContext.getDatasetName(); - boolean isEdgeInline = edgeBodyAnalysisContext.isExpressionInline(); - - // Determine our source and destination vertices. - VertexPatternExpr sourceVertex, destVertex; - if (edgeDescriptor.getEdgeDirection() == EdgeDescriptor.EdgeDirection.LEFT_TO_RIGHT) { - sourceVertex = edgePatternExpr.getLeftVertex(); - destVertex = edgePatternExpr.getRightVertex(); - - } else { // edgeDescriptor.getEdgeDirection() == EdgeDescriptor.EdgeDirection.RIGHT_TO_LEFT - sourceVertex = edgePatternExpr.getRightVertex(); - destVertex = edgePatternExpr.getLeftVertex(); - } - - // Collect information about our source -> edge JOIN. - GraphElementIdentifier sourceIdentifier = sourceVertex.generateIdentifiers(graphIdentifier).get(0); - ElementBodyAnalysisContext sourceBodyAnalysisContext = analysisContextMap.get(sourceIdentifier); - VarIdentifier sourceVertexVariable = sourceVertex.getVariableExpr().getVar(); - List<List<String>> sourceVertexKey = elementLookupTable.getVertexKey(sourceIdentifier); - List<List<String>> sourceEdgeKey = elementLookupTable.getEdgeSourceKey(edgeIdentifier); - Function<GraphElementIdentifier, List<List<String>>> sourceKey = elementLookupTable::getEdgeSourceKey; - boolean isSourceInline = sourceBodyAnalysisContext.isExpressionInline(); - boolean isSourceIntroduced = aliasLookupTable.getIterationAlias(sourceVertexVariable) != null; - boolean isSourceFolded = isSourceInline && sourceBodyAnalysisContext.getDatasetName().equals(edgeDatasetName) - && sourceBodyAnalysisContext.getDataverseName().equals(edgeDataverseName) - && sourceVertexKey.equals(sourceEdgeKey); - - // ...and our dest -> edge JOIN. - GraphElementIdentifier destIdentifier = destVertex.generateIdentifiers(graphIdentifier).get(0); - ElementBodyAnalysisContext destBodyAnalysisContext = analysisContextMap.get(destIdentifier); - VarIdentifier destVertexVariable = destVertex.getVariableExpr().getVar(); - List<List<String>> destVertexKey = elementLookupTable.getVertexKey(destIdentifier); - List<List<String>> destEdgeKey = elementLookupTable.getEdgeDestKey(edgeIdentifier); - Function<GraphElementIdentifier, List<List<String>>> destKey = elementLookupTable::getEdgeDestKey; - boolean isDestInline = destBodyAnalysisContext.isExpressionInline(); - boolean isDestIntroduced = aliasLookupTable.getIterationAlias(destVertexVariable) != null; - boolean isDestFolded = isDestInline && destBodyAnalysisContext.getDatasetName().equals(edgeDatasetName) - && destBodyAnalysisContext.getDataverseName().equals(edgeDataverseName) - && destVertexKey.equals(destEdgeKey); - - // Determine our strategy for lowering our edge. - if (isEdgeInline && isSourceFolded && !isDestIntroduced) { - if (!isSourceIntroduced) { - lowerEnvironment.acceptAction(environmentActionFactory.buildDanglingVertexAction(sourceVertex)); - } - lowerEnvironment - .acceptAction(environmentActionFactory.buildFoldedEdgeAction(sourceVertex, edgePatternExpr)); - lowerEnvironment.acceptAction( - environmentActionFactory.buildBoundVertexAction(destVertex, edgePatternExpr, destKey)); - - } else if (isEdgeInline && isDestFolded && !isSourceIntroduced) { - if (!isDestIntroduced) { - lowerEnvironment.acceptAction(environmentActionFactory.buildDanglingVertexAction(destVertex)); - } - lowerEnvironment.acceptAction(environmentActionFactory.buildFoldedEdgeAction(destVertex, edgePatternExpr)); - lowerEnvironment.acceptAction( - environmentActionFactory.buildBoundVertexAction(sourceVertex, edgePatternExpr, sourceKey)); - - } else if (isSourceIntroduced && isDestIntroduced) { - lowerEnvironment.acceptAction( - environmentActionFactory.buildNonFoldedEdgeAction(sourceVertex, edgePatternExpr, sourceKey)); - lowerEnvironment.acceptAction( - environmentActionFactory.buildRawJoinVertexAction(destVertex, edgePatternExpr, destKey)); - - } else if (isSourceIntroduced) { // !isDestIntroduced - lowerEnvironment.acceptAction( - environmentActionFactory.buildNonFoldedEdgeAction(sourceVertex, edgePatternExpr, sourceKey)); - lowerEnvironment.acceptAction( - environmentActionFactory.buildBoundVertexAction(destVertex, edgePatternExpr, destKey)); - - } else if (isDestIntroduced) { // !isSourceIntroduced - lowerEnvironment.acceptAction( - environmentActionFactory.buildNonFoldedEdgeAction(destVertex, edgePatternExpr, destKey)); - lowerEnvironment.acceptAction( - environmentActionFactory.buildBoundVertexAction(sourceVertex, edgePatternExpr, sourceKey)); - - } else { // !isSourceIntroduced && !isDestIntroduced - // When nothing is introduced, start off from LEFT to RIGHT instead of considering our source and dest. - VertexPatternExpr leftVertex = edgePatternExpr.getLeftVertex(); - VertexPatternExpr rightVertex = edgePatternExpr.getRightVertex(); - Function<GraphElementIdentifier, List<List<String>>> leftKey; - Function<GraphElementIdentifier, List<List<String>>> rightKey; - if (edgeDescriptor.getEdgeDirection() == EdgeDescriptor.EdgeDirection.LEFT_TO_RIGHT) { - leftKey = sourceKey; - rightKey = destKey; - - } else { // edgeDescriptor.getEdgeDirection() == EdgeDescriptor.EdgeDirection.RIGHT_TO_LEFT - leftKey = destKey; - rightKey = sourceKey; - } - lowerEnvironment.acceptAction(environmentActionFactory.buildDanglingVertexAction(leftVertex)); - lowerEnvironment.acceptAction( - environmentActionFactory.buildNonFoldedEdgeAction(leftVertex, edgePatternExpr, leftKey)); - lowerEnvironment.acceptAction( - environmentActionFactory.buildBoundVertexAction(rightVertex, edgePatternExpr, rightKey)); - } - return edgePatternExpr; - } -} \ No newline at end of file
diff --git a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/visitor/GroupByAggSugarVisitor.java b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/visitor/GroupByAggSugarVisitor.java deleted file mode 100644 index fe4b8b8..0000000 --- a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/visitor/GroupByAggSugarVisitor.java +++ /dev/null
@@ -1,58 +0,0 @@ -/* - * 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.graphix.lang.rewrites.visitor; - -import java.util.Collection; -import java.util.Collections; - -import org.apache.asterix.common.exceptions.CompilationException; -import org.apache.asterix.common.exceptions.ErrorCode; -import org.apache.asterix.graphix.lang.clause.CorrLetClause; -import org.apache.asterix.lang.common.base.Expression; -import org.apache.asterix.lang.common.base.ILangExpression; -import org.apache.asterix.lang.common.context.Scope; -import org.apache.asterix.lang.common.expression.VariableExpr; -import org.apache.asterix.lang.common.rewrites.LangRewritingContext; -import org.apache.asterix.lang.common.struct.VarIdentifier; -import org.apache.asterix.lang.sqlpp.rewrites.visitor.SqlppGroupByAggregationSugarVisitor; -import org.apache.asterix.lang.sqlpp.util.SqlppVariableUtil; - -/** - * An extension of {@link SqlppGroupByAggregationSugarVisitor} to properly handle {@link CorrLetClause} nodes. - */ -public class GroupByAggSugarVisitor extends SqlppGroupByAggregationSugarVisitor - implements ILetCorrelateClauseVisitor<Expression, ILangExpression> { - public GroupByAggSugarVisitor(LangRewritingContext context, Collection<VarIdentifier> externalVars) { - super(context, externalVars); - } - - @Override - public Expression visit(CorrLetClause corrLetClause, ILangExpression arg) throws CompilationException { - // Do NOT extend the current scope. - corrLetClause.setRightExpression(visit(corrLetClause.getRightExpression(), corrLetClause)); - VariableExpr varExpr = corrLetClause.getRightVariable(); - Scope currentScope = scopeChecker.getCurrentScope(); - if (currentScope.findLocalSymbol(varExpr.getVar().getValue()) != null) { - throw new CompilationException(ErrorCode.COMPILATION_ERROR, varExpr.getSourceLocation(), - "Duplicate alias definitions: " + SqlppVariableUtil.toUserDefinedName(varExpr.getVar().getValue())); - } - currentScope.addNewVarSymbolToScope(varExpr.getVar(), Collections.emptySet()); - return null; - } -}
diff --git a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/visitor/ILetCorrelateClauseVisitor.java b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/visitor/ILetCorrelateClauseVisitor.java deleted file mode 100644 index dd8a89d..0000000 --- a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/visitor/ILetCorrelateClauseVisitor.java +++ /dev/null
@@ -1,27 +0,0 @@ -/* - * 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.graphix.lang.rewrites.visitor; - -import org.apache.asterix.common.exceptions.CompilationException; -import org.apache.asterix.graphix.lang.clause.CorrLetClause; -import org.apache.asterix.lang.common.visitor.base.ILangVisitor; - -public interface ILetCorrelateClauseVisitor<R, T> extends ILangVisitor<R, T> { - R visit(CorrLetClause lcc, T arg) throws CompilationException; -}
diff --git a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/visitor/LabelConsistencyVisitor.java b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/visitor/LabelConsistencyVisitor.java deleted file mode 100644 index 1b8bd4b..0000000 --- a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/visitor/LabelConsistencyVisitor.java +++ /dev/null
@@ -1,49 +0,0 @@ -/* - * 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.graphix.lang.rewrites.visitor; - -import org.apache.asterix.graphix.lang.expression.VertexPatternExpr; -import org.apache.asterix.graphix.lang.rewrites.resolve.QueryKnowledgeTable; -import org.apache.asterix.lang.common.base.Expression; -import org.apache.asterix.lang.common.base.ILangExpression; -import org.apache.asterix.lang.common.struct.Identifier; - -/** - * Unify the labels across vertices of the same variable name. - * - * @see org.apache.asterix.graphix.lang.rewrites.resolve.InferenceBasedResolver - */ -public class LabelConsistencyVisitor extends AbstractGraphixQueryVisitor { - private final QueryKnowledgeTable queryKnowledgeTable; - - public LabelConsistencyVisitor(QueryKnowledgeTable queryKnowledgeTable) { - this.queryKnowledgeTable = queryKnowledgeTable; - } - - @Override - public Expression visit(VertexPatternExpr vertexPatternExpr, ILangExpression arg) { - if (vertexPatternExpr.getLabels().isEmpty()) { - Identifier vertexIdentifier = vertexPatternExpr.getVariableExpr().getVar(); - if (queryKnowledgeTable.containsKey(vertexIdentifier)) { - vertexPatternExpr.getLabels().addAll(queryKnowledgeTable.get(vertexIdentifier)); - } - } - return vertexPatternExpr; - } -}
diff --git a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/visitor/PopulateUnknownsVisitor.java b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/visitor/PopulateUnknownsVisitor.java deleted file mode 100644 index 7da0df5..0000000 --- a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/visitor/PopulateUnknownsVisitor.java +++ /dev/null
@@ -1,144 +0,0 @@ -/* - * 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.graphix.lang.rewrites.visitor; - -import java.util.ArrayList; -import java.util.List; -import java.util.function.Supplier; - -import org.apache.asterix.common.exceptions.CompilationException; -import org.apache.asterix.graphix.lang.clause.FromGraphClause; -import org.apache.asterix.graphix.lang.clause.GraphSelectBlock; -import org.apache.asterix.graphix.lang.clause.MatchClause; -import org.apache.asterix.graphix.lang.expression.EdgePatternExpr; -import org.apache.asterix.graphix.lang.expression.PathPatternExpr; -import org.apache.asterix.graphix.lang.expression.VertexPatternExpr; -import org.apache.asterix.graphix.lang.rewrites.GraphixRewritingContext; -import org.apache.asterix.graphix.lang.struct.EdgeDescriptor; -import org.apache.asterix.lang.common.base.AbstractClause; -import org.apache.asterix.lang.common.base.Clause; -import org.apache.asterix.lang.common.base.Expression; -import org.apache.asterix.lang.common.base.ILangExpression; -import org.apache.asterix.lang.common.clause.LetClause; -import org.apache.asterix.lang.common.expression.VariableExpr; -import org.apache.asterix.lang.common.struct.Identifier; -import org.apache.asterix.lang.common.struct.VarIdentifier; -import org.apache.asterix.lang.sqlpp.clause.AbstractBinaryCorrelateClause; -import org.apache.asterix.lang.sqlpp.expression.SelectExpression; -import org.apache.asterix.lang.sqlpp.rewrites.visitor.GenerateColumnNameVisitor; -import org.apache.asterix.lang.sqlpp.util.SqlppVariableUtil; -import org.apache.hyracks.algebricks.common.utils.Pair; - -/** - * A pre-Graphix transformation pass to populate a number of unknowns in our Graphix AST. - * a) Populate all unknown graph elements (vertices and edges). - * b) Populate all unknown column names in SELECT-CLAUSEs. - * c) Populate all unknown GROUP-BY keys. - * d) Fill in all GROUP-BY fields. - */ -public class PopulateUnknownsVisitor extends AbstractGraphixQueryVisitor { - private final GenerateColumnNameVisitor generateColumnNameVisitor; - private final Supplier<VarIdentifier> newVariableSupplier; - - public PopulateUnknownsVisitor(GraphixRewritingContext graphixRewritingContext) { - generateColumnNameVisitor = new GenerateColumnNameVisitor(graphixRewritingContext); - newVariableSupplier = graphixRewritingContext::getNewGraphixVariable; - } - - @Override - public Expression visit(SelectExpression selectExpression, ILangExpression arg) throws CompilationException { - selectExpression.accept(generateColumnNameVisitor, arg); - return super.visit(selectExpression, arg); - } - - @Override - public Expression visit(GraphSelectBlock graphSelectBlock, ILangExpression arg) throws CompilationException { - super.visit(graphSelectBlock, arg); - - if (graphSelectBlock.hasGroupbyClause()) { - // Collect all variables that should belong in the GROUP-BY field list. - List<VarIdentifier> userLiveVariables = new ArrayList<>(); - for (MatchClause matchClause : graphSelectBlock.getFromGraphClause().getMatchClauses()) { - for (PathPatternExpr pathExpression : matchClause.getPathExpressions()) { - if (pathExpression.getVariableExpr() != null) { - userLiveVariables.add(pathExpression.getVariableExpr().getVar()); - } - for (VertexPatternExpr vertexExpression : pathExpression.getVertexExpressions()) { - VarIdentifier vertexVariable = vertexExpression.getVariableExpr().getVar(); - if (!GraphixRewritingContext.isGraphixVariable(vertexVariable)) { - userLiveVariables.add(vertexVariable); - } - } - for (EdgePatternExpr edgeExpression : pathExpression.getEdgeExpressions()) { - VarIdentifier edgeVariable = edgeExpression.getEdgeDescriptor().getVariableExpr().getVar(); - if (!GraphixRewritingContext.isGraphixVariable(edgeVariable)) { - userLiveVariables.add(edgeVariable); - } - } - } - } - if (!graphSelectBlock.getFromGraphClause().getCorrelateClauses().isEmpty()) { - FromGraphClause fromGraphClause = graphSelectBlock.getFromGraphClause(); - List<AbstractBinaryCorrelateClause> correlateClauses = fromGraphClause.getCorrelateClauses(); - for (AbstractBinaryCorrelateClause correlateClause : correlateClauses) { - VarIdentifier bindingVariable = correlateClause.getRightVariable().getVar(); - if (!GraphixRewritingContext.isGraphixVariable(bindingVariable)) { - userLiveVariables.add(bindingVariable); - } - } - } - if (graphSelectBlock.hasLetWhereClauses()) { - for (AbstractClause abstractClause : graphSelectBlock.getLetWhereList()) { - if (abstractClause.getClauseType() == Clause.ClauseType.LET_CLAUSE) { - LetClause letClause = (LetClause) abstractClause; - VarIdentifier bindingVariable = letClause.getVarExpr().getVar(); - if (!GraphixRewritingContext.isGraphixVariable(bindingVariable)) { - userLiveVariables.add(bindingVariable); - } - } - } - } - - // Add the live variables to our GROUP-BY field list. - List<Pair<Expression, Identifier>> newGroupFieldList = new ArrayList<>(); - for (VarIdentifier userLiveVariable : userLiveVariables) { - String variableName = SqlppVariableUtil.toUserDefinedName(userLiveVariable.getValue()); - VariableExpr variableExpr = new VariableExpr(userLiveVariable); - newGroupFieldList.add(new Pair<>(variableExpr, new Identifier(variableName))); - } - graphSelectBlock.getGroupbyClause().setGroupFieldList(newGroupFieldList); - } - return null; - } - - @Override - public Expression visit(VertexPatternExpr vertexExpression, ILangExpression arg) throws CompilationException { - if (vertexExpression.getVariableExpr() == null) { - vertexExpression.setVariableExpr(new VariableExpr(newVariableSupplier.get())); - } - return super.visit(vertexExpression, arg); - } - - @Override - public Expression visit(EdgePatternExpr edgeExpression, ILangExpression arg) throws CompilationException { - EdgeDescriptor edgeDescriptor = edgeExpression.getEdgeDescriptor(); - if (edgeDescriptor.getVariableExpr() == null) { - edgeDescriptor.setVariableExpr(new VariableExpr(newVariableSupplier.get())); - } - return super.visit(edgeExpression, arg); - } -}
diff --git a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/visitor/PostRewriteVariableVisitor.java b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/visitor/PostRewriteVariableVisitor.java deleted file mode 100644 index eea6b98..0000000 --- a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/visitor/PostRewriteVariableVisitor.java +++ /dev/null
@@ -1,60 +0,0 @@ -/* - * 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.graphix.lang.rewrites.visitor; - -import java.util.Collection; -import java.util.Collections; - -import org.apache.asterix.common.exceptions.CompilationException; -import org.apache.asterix.common.exceptions.ErrorCode; -import org.apache.asterix.graphix.lang.clause.CorrLetClause; -import org.apache.asterix.lang.common.base.Expression; -import org.apache.asterix.lang.common.base.ILangExpression; -import org.apache.asterix.lang.common.context.Scope; -import org.apache.asterix.lang.common.expression.VariableExpr; -import org.apache.asterix.lang.common.rewrites.LangRewritingContext; -import org.apache.asterix.lang.common.struct.VarIdentifier; -import org.apache.asterix.lang.sqlpp.rewrites.visitor.VariableCheckAndRewriteVisitor; -import org.apache.asterix.lang.sqlpp.util.SqlppVariableUtil; -import org.apache.asterix.metadata.declared.MetadataProvider; - -/** - * An extension of {@link VariableCheckAndRewriteVisitor} to properly handle {@link CorrLetClause} nodes. - */ -public class PostRewriteVariableVisitor extends VariableCheckAndRewriteVisitor - implements ILetCorrelateClauseVisitor<Expression, ILangExpression> { - public PostRewriteVariableVisitor(LangRewritingContext context, MetadataProvider metadataProvider, - Collection<VarIdentifier> externalVars) { - super(context, metadataProvider, externalVars); - } - - @Override - public Expression visit(CorrLetClause corrLetClause, ILangExpression arg) throws CompilationException { - // Do NOT extend the current scope. - corrLetClause.setRightExpression(visit(corrLetClause.getRightExpression(), corrLetClause)); - VariableExpr varExpr = corrLetClause.getRightVariable(); - Scope currentScope = scopeChecker.getCurrentScope(); - if (currentScope.findLocalSymbol(varExpr.getVar().getValue()) != null) { - throw new CompilationException(ErrorCode.COMPILATION_ERROR, varExpr.getSourceLocation(), - "Duplicate alias definitions: " + SqlppVariableUtil.toUserDefinedName(varExpr.getVar().getValue())); - } - currentScope.addNewVarSymbolToScope(varExpr.getVar(), Collections.emptySet()); - return null; - } -}
diff --git a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/visitor/QueryKnowledgeVisitor.java b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/visitor/QueryKnowledgeVisitor.java deleted file mode 100644 index d56419b..0000000 --- a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/visitor/QueryKnowledgeVisitor.java +++ /dev/null
@@ -1,41 +0,0 @@ -/* - * 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.graphix.lang.rewrites.visitor; - -import org.apache.asterix.graphix.lang.expression.VertexPatternExpr; -import org.apache.asterix.graphix.lang.rewrites.resolve.QueryKnowledgeTable; -import org.apache.asterix.lang.common.base.Expression; -import org.apache.asterix.lang.common.base.ILangExpression; - -/** - * @see QueryKnowledgeTable - */ -public class QueryKnowledgeVisitor extends AbstractGraphixQueryVisitor { - private final QueryKnowledgeTable queryKnowledgeTable = new QueryKnowledgeTable(); - - @Override - public Expression visit(VertexPatternExpr vertexPatternExpr, ILangExpression arg) { - queryKnowledgeTable.put(vertexPatternExpr); - return vertexPatternExpr; - } - - public QueryKnowledgeTable getQueryKnowledgeTable() { - return queryKnowledgeTable; - } -}
diff --git a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/visitor/SchemaEnrichmentVisitor.java b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/visitor/SchemaEnrichmentVisitor.java deleted file mode 100644 index f91ebb7..0000000 --- a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/visitor/SchemaEnrichmentVisitor.java +++ /dev/null
@@ -1,121 +0,0 @@ -/* - * 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.graphix.lang.rewrites.visitor; - -import static org.apache.asterix.graphix.function.GraphixFunctionIdentifiers.isEdgeFunction; -import static org.apache.asterix.graphix.function.GraphixFunctionIdentifiers.isVertexFunction; -import static org.apache.asterix.graphix.function.GraphixFunctionMap.getFunctionPrepare; - -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.stream.Collectors; - -import org.apache.asterix.common.exceptions.CompilationException; -import org.apache.asterix.graphix.common.metadata.GraphIdentifier; -import org.apache.asterix.graphix.function.prepare.IFunctionPrepare; -import org.apache.asterix.graphix.lang.clause.CorrLetClause; -import org.apache.asterix.graphix.lang.clause.GraphSelectBlock; -import org.apache.asterix.graphix.lang.clause.MatchClause; -import org.apache.asterix.graphix.lang.expression.EdgePatternExpr; -import org.apache.asterix.graphix.lang.expression.VertexPatternExpr; -import org.apache.asterix.graphix.lang.rewrites.common.ElementLookupTable; -import org.apache.asterix.graphix.lang.struct.EdgeDescriptor; -import org.apache.asterix.lang.common.base.Expression; -import org.apache.asterix.lang.common.base.ILangExpression; -import org.apache.asterix.lang.common.expression.VariableExpr; -import org.apache.asterix.lang.common.struct.VarIdentifier; -import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier; - -/** - * Perform a pass to enrich any {@link CorrLetClause} nodes with necessary schema information. Note that this - * process may overestimate the amount of enrichment actually required (to minimize this difference requires some form - * of equivalence classes @ the rewriter level). - */ -public class SchemaEnrichmentVisitor extends AbstractGraphixQueryVisitor { - private final ElementLookupTable elementLookupTable; - private final Set<FunctionIdentifier> functionIdentifiers; - private final Map<VarIdentifier, Expression> expressionMap; - private final GraphSelectBlock workingSelectBlock; - private final GraphIdentifier graphIdentifier; - - public SchemaEnrichmentVisitor(ElementLookupTable elementLookupTable, GraphIdentifier graphIdentifier, - GraphSelectBlock workingSelectBlock, Set<FunctionIdentifier> functionIdentifiers) { - this.graphIdentifier = graphIdentifier; - this.elementLookupTable = elementLookupTable; - this.workingSelectBlock = workingSelectBlock; - this.functionIdentifiers = functionIdentifiers; - this.expressionMap = new HashMap<>(); - } - - @Override - public Expression visit(GraphSelectBlock graphSelectBlock, ILangExpression arg) throws CompilationException { - // Visit our immediate MATCH AST nodes (do not visit lower levels). - for (MatchClause matchClause : graphSelectBlock.getFromGraphClause().getMatchClauses()) { - matchClause.accept(this, arg); - } - - // We are going to enrich these LET-CORRELATE clauses w/ the necessary schema. - if (workingSelectBlock.equals(graphSelectBlock)) { - List<CorrLetClause> corrLetClauses = graphSelectBlock.getFromClause().getFromTerms().get(0) - .getCorrelateClauses().stream().filter(c -> c instanceof CorrLetClause).map(c -> (CorrLetClause) c) - .collect(Collectors.toList()); - Collections.reverse(corrLetClauses); - for (FunctionIdentifier functionIdentifier : functionIdentifiers) { - IFunctionPrepare functionPrepare = getFunctionPrepare(functionIdentifier); - Set<VarIdentifier> visitedBindings = new HashSet<>(); - for (CorrLetClause corrLetClause : corrLetClauses) { - VarIdentifier rightVar = corrLetClause.getRightVariable().getVar(); - Expression graphExpr = expressionMap.getOrDefault(rightVar, null); - boolean isVertex = isVertexFunction(functionIdentifier) && graphExpr instanceof VertexPatternExpr; - boolean isEdge = isEdgeFunction(functionIdentifier) && graphExpr instanceof EdgePatternExpr; - if (!visitedBindings.contains(rightVar) && (isEdge || isVertex)) { - Expression outputExpr = functionPrepare.prepare(corrLetClause.getRightExpression(), graphExpr, - graphIdentifier, elementLookupTable); - corrLetClause.setRightExpression(outputExpr); - visitedBindings.add(rightVar); - } - } - } - } - return null; - } - - @Override - public Expression visit(VertexPatternExpr vertexPatternExpr, ILangExpression arg) throws CompilationException { - VariableExpr variableExpr = vertexPatternExpr.getVariableExpr(); - if (variableExpr != null) { - expressionMap.put(variableExpr.getVar(), vertexPatternExpr); - } - return super.visit(vertexPatternExpr, arg); - } - - @Override - public Expression visit(EdgePatternExpr edgePatternExpr, ILangExpression arg) throws CompilationException { - EdgeDescriptor edgeDescriptor = edgePatternExpr.getEdgeDescriptor(); - VariableExpr variableExpr = edgeDescriptor.getVariableExpr(); - if (variableExpr != null) { - expressionMap.put(variableExpr.getVar(), edgePatternExpr); - } - return super.visit(edgePatternExpr, arg); - } -}
diff --git a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/visitor/StructureAnalysisVisitor.java b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/visitor/StructureAnalysisVisitor.java deleted file mode 100644 index 36969e2..0000000 --- a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/visitor/StructureAnalysisVisitor.java +++ /dev/null
@@ -1,231 +0,0 @@ -/* - * 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.graphix.lang.rewrites.visitor; - -import java.util.ArrayDeque; -import java.util.ArrayList; -import java.util.Deque; -import java.util.HashMap; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.stream.Collectors; - -import org.apache.asterix.common.exceptions.CompilationException; -import org.apache.asterix.common.metadata.DataverseName; -import org.apache.asterix.graphix.common.metadata.GraphIdentifier; -import org.apache.asterix.graphix.lang.clause.FromGraphClause; -import org.apache.asterix.graphix.lang.clause.MatchClause; -import org.apache.asterix.graphix.lang.expression.EdgePatternExpr; -import org.apache.asterix.graphix.lang.expression.PathPatternExpr; -import org.apache.asterix.graphix.lang.expression.VertexPatternExpr; -import org.apache.asterix.graphix.lang.optype.MatchType; -import org.apache.asterix.graphix.lang.rewrites.GraphixRewritingContext; -import org.apache.asterix.graphix.lang.rewrites.common.EdgeDependencyGraph; -import org.apache.asterix.lang.common.base.Expression; -import org.apache.asterix.lang.common.base.ILangExpression; -import org.apache.asterix.lang.common.struct.Identifier; -import org.apache.asterix.lang.common.struct.VarIdentifier; -import org.apache.asterix.metadata.declared.MetadataProvider; - -/** - * Perform an analysis pass of our AST to generate three items: a) a list of edge orderings, b) a list of dangling - * vertices, and c) a list of path patterns. - * - To generate an {@link EdgeDependencyGraph}, we create an adjacency map of query edges whose adjacency between - * other query edges is defined as sharing some vertex. The adjacency list associated with each query edge is ordered - * by visitation, and will be used in {@link EdgeDependencyGraph#iterator()}- in short, this means that the order of - * the patterns in query may affect the resulting lowered AST. - * - For each LEFT-MATCH-CLAUSE, we build a new dependency subgraph. Every subsequent subgraph should possess a - * leading edge that contains a vertex found in the previous subgraph (if it exists). - */ -public class StructureAnalysisVisitor extends AbstractGraphixQueryVisitor { - private static class AnalysisEnvironment { - // We must populate these maps (edge ID -> edge IDs). - private final List<Map<Identifier, List<Identifier>>> adjacencyMaps; - private final Map<Identifier, EdgePatternExpr> edgePatternMap; - private Map<Identifier, List<Identifier>> workingAdjacencyMap; - - // Build a separate map to define dependencies (vertex ID -> edge IDs). - private final Map<Identifier, List<Identifier>> vertexEdgeMap; - private final Deque<List<VertexPatternExpr>> danglingVertices; - private final Deque<List<PathPatternExpr>> pathPatterns; - - private AnalysisEnvironment() { - this.adjacencyMaps = new ArrayList<>(); - this.edgePatternMap = new LinkedHashMap<>(); - this.workingAdjacencyMap = new LinkedHashMap<>(); - - this.pathPatterns = new ArrayDeque<>(); - this.danglingVertices = new ArrayDeque<>(); - this.pathPatterns.addLast(new ArrayList<>()); - this.danglingVertices.addLast(new ArrayList<>()); - this.vertexEdgeMap = new LinkedHashMap<>(); - } - } - - // We will return instances of the following back to our caller. - public static class StructureContext { - private final Deque<List<PathPatternExpr>> pathPatternQueue; - private final Deque<List<VertexPatternExpr>> danglingVertexQueue; - private final EdgeDependencyGraph edgeDependencyGraph; - private final GraphIdentifier graphIdentifier; - - private StructureContext(EdgeDependencyGraph edgeGraph, Deque<List<PathPatternExpr>> pathPatternQueue, - Deque<List<VertexPatternExpr>> danglingVertexQueue, GraphIdentifier graphIdentifier) { - this.edgeDependencyGraph = edgeGraph; - this.pathPatternQueue = pathPatternQueue; - this.danglingVertexQueue = danglingVertexQueue; - this.graphIdentifier = graphIdentifier; - } - - public EdgeDependencyGraph getEdgeDependencyGraph() { - return edgeDependencyGraph; - } - - public Deque<List<VertexPatternExpr>> getDanglingVertexQueue() { - return danglingVertexQueue; - } - - public Deque<List<PathPatternExpr>> getPathPatternQueue() { - return pathPatternQueue; - } - - public GraphIdentifier getGraphIdentifier() { - return graphIdentifier; - } - } - - // We will build new environments on each visit of a FROM-GRAPH-CLAUSE. - private final Map<FromGraphClause, AnalysisEnvironment> analysisEnvironmentMap; - private final GraphixRewritingContext graphixRewritingContext; - private AnalysisEnvironment workingEnvironment; - - public StructureAnalysisVisitor(GraphixRewritingContext graphixRewritingContext) { - this.analysisEnvironmentMap = new HashMap<>(); - this.graphixRewritingContext = graphixRewritingContext; - } - - private void addEdgeDependency(VarIdentifier vertexID, VarIdentifier edgeID, List<Identifier> dependencyList) { - if (workingEnvironment.vertexEdgeMap.containsKey(vertexID)) { - dependencyList.addAll(workingEnvironment.vertexEdgeMap.get(vertexID)); - workingEnvironment.vertexEdgeMap.get(vertexID).add(edgeID); - - } else { - List<Identifier> vertexDependencies = new ArrayList<>(); - vertexDependencies.add(edgeID); - workingEnvironment.vertexEdgeMap.put(vertexID, vertexDependencies); - } - } - - @Override - public Expression visit(FromGraphClause fromGraphClause, ILangExpression arg) throws CompilationException { - // Add to our a map a new analysis environment. - workingEnvironment = new AnalysisEnvironment(); - analysisEnvironmentMap.put(fromGraphClause, workingEnvironment); - - // Collect our structure context. - return super.visit(fromGraphClause, arg); - } - - @Override - public Expression visit(MatchClause matchClause, ILangExpression arg) throws CompilationException { - // Reset to build a new graph. - if (matchClause.getMatchType() == MatchType.LEFTOUTER) { - workingEnvironment.danglingVertices.addLast(new ArrayList<>()); - workingEnvironment.pathPatterns.addLast(new ArrayList<>()); - workingEnvironment.vertexEdgeMap.clear(); - - // We should retain all adjacency information from the previous walks. - workingEnvironment.adjacencyMaps.add(workingEnvironment.workingAdjacencyMap); - workingEnvironment.workingAdjacencyMap = new LinkedHashMap<>(workingEnvironment.workingAdjacencyMap); - } - return super.visit(matchClause, arg); - } - - @Override - public Expression visit(PathPatternExpr pathExpression, ILangExpression arg) throws CompilationException { - // Visit our edges first, then our vertices. This allows us to determine our dangling vertices. - for (EdgePatternExpr edgeExpression : pathExpression.getEdgeExpressions()) { - edgeExpression.accept(this, arg); - } - for (VertexPatternExpr vertexExpression : pathExpression.getVertexExpressions()) { - vertexExpression.accept(this, arg); - } - workingEnvironment.pathPatterns.getLast().add(pathExpression); - return pathExpression; - } - - @Override - public Expression visit(VertexPatternExpr vertexExpression, ILangExpression arg) throws CompilationException { - VarIdentifier vertexID = vertexExpression.getVariableExpr().getVar(); - if (!workingEnvironment.vertexEdgeMap.containsKey(vertexID)) { - workingEnvironment.danglingVertices.getLast().add(vertexExpression); - } - return vertexExpression; - } - - @Override - public Expression visit(EdgePatternExpr edgeExpression, ILangExpression arg) throws CompilationException { - VarIdentifier leftVertexID = edgeExpression.getLeftVertex().getVariableExpr().getVar(); - VarIdentifier rightVertexID = edgeExpression.getRightVertex().getVariableExpr().getVar(); - VarIdentifier edgeID = edgeExpression.getEdgeDescriptor().getVariableExpr().getVar(); - List<Identifier> edgeDependencies = new ArrayList<>(); - - addEdgeDependency(leftVertexID, edgeID, edgeDependencies); - addEdgeDependency(rightVertexID, edgeID, edgeDependencies); - workingEnvironment.workingAdjacencyMap.put(edgeID, edgeDependencies); - workingEnvironment.edgePatternMap.put(edgeID, edgeExpression); - for (Identifier dependencyEdgeID : edgeDependencies) { - // Populate our map in reverse as well. - workingEnvironment.workingAdjacencyMap.get(dependencyEdgeID).add(edgeID); - } - - // We ignore any internal vertices here. - return edgeExpression; - } - - public Map<FromGraphClause, StructureContext> getFromGraphClauseContextMap() { - return analysisEnvironmentMap.entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, e -> { - AnalysisEnvironment analysisEnvironment = e.getValue(); - if (!analysisEnvironment.workingAdjacencyMap.isEmpty()) { - // We have not finished building our edge dependency subgraph. - analysisEnvironment.adjacencyMaps.add(analysisEnvironment.workingAdjacencyMap); - analysisEnvironment.workingAdjacencyMap = new LinkedHashMap<>(); - analysisEnvironment.vertexEdgeMap.clear(); - } - - // Build our graph identifier. - FromGraphClause fromGraphClause = e.getKey(); - MetadataProvider metadataProvider = graphixRewritingContext.getMetadataProvider(); - DataverseName dataverseName = (fromGraphClause.getDataverseName() == null) - ? metadataProvider.getDefaultDataverseName() : fromGraphClause.getDataverseName(); - String graphName = (fromGraphClause.getGraphName() != null) ? fromGraphClause.getGraphName().getValue() - : fromGraphClause.getGraphConstructor().getInstanceID(); - GraphIdentifier graphIdentifier = new GraphIdentifier(dataverseName, graphName); - - // Construct our output. - List<Map<Identifier, List<Identifier>>> adjacencyMaps = e.getValue().adjacencyMaps; - Map<Identifier, EdgePatternExpr> edgePatternMap = e.getValue().edgePatternMap; - EdgeDependencyGraph edgeDependencyGraph = new EdgeDependencyGraph(adjacencyMaps, edgePatternMap); - Deque<List<VertexPatternExpr>> danglingVertices = e.getValue().danglingVertices; - Deque<List<PathPatternExpr>> pathPatterns = e.getValue().pathPatterns; - return new StructureContext(edgeDependencyGraph, pathPatterns, danglingVertices, graphIdentifier); - })); - } -}
diff --git a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/visitor/StructureResolutionVisitor.java b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/visitor/StructureResolutionVisitor.java deleted file mode 100644 index 38bebe9..0000000 --- a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/visitor/StructureResolutionVisitor.java +++ /dev/null
@@ -1,167 +0,0 @@ -/* - * 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.graphix.lang.rewrites.visitor; - -import static org.apache.asterix.graphix.extension.GraphixMetadataExtension.getGraph; - -import java.util.Map; - -import org.apache.asterix.common.exceptions.CompilationException; -import org.apache.asterix.common.exceptions.ErrorCode; -import org.apache.asterix.common.metadata.DataverseName; -import org.apache.asterix.graphix.algebra.compiler.provider.GraphixCompilationProvider; -import org.apache.asterix.graphix.common.metadata.GraphIdentifier; -import org.apache.asterix.graphix.lang.clause.FromGraphClause; -import org.apache.asterix.graphix.lang.expression.EdgePatternExpr; -import org.apache.asterix.graphix.lang.expression.VertexPatternExpr; -import org.apache.asterix.graphix.lang.rewrites.GraphixRewritingContext; -import org.apache.asterix.graphix.lang.rewrites.resolve.IGraphElementResolver; -import org.apache.asterix.graphix.lang.rewrites.resolve.InferenceBasedResolver; -import org.apache.asterix.graphix.lang.rewrites.resolve.SchemaKnowledgeTable; -import org.apache.asterix.graphix.lang.statement.DeclareGraphStatement; -import org.apache.asterix.graphix.lang.struct.EdgeDescriptor; -import org.apache.asterix.graphix.metadata.entity.schema.Graph; -import org.apache.asterix.lang.common.base.Expression; -import org.apache.asterix.lang.common.base.ILangExpression; -import org.apache.asterix.lang.common.struct.Identifier; -import org.apache.asterix.metadata.MetadataTransactionContext; -import org.apache.asterix.metadata.declared.MetadataProvider; -import org.apache.hyracks.algebricks.common.exceptions.AlgebricksException; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - -/** - * Resolve graph element labels and edge directions in our AST. We assume that all graph elements have a variable. - * - * @see InferenceBasedResolver - */ -public class StructureResolutionVisitor extends AbstractGraphixQueryVisitor { - private static final Logger LOGGER = LogManager.getLogger(StructureResolutionVisitor.class); - - // If we exceed 500 iterations, something is probably wrong... log this. - private static final long DEFAULT_RESOLVER_ITERATION_MAX = 500; - - private final MetadataProvider metadataProvider; - private final Map<GraphIdentifier, DeclareGraphStatement> declaredGraphs; - - public StructureResolutionVisitor(GraphixRewritingContext graphixRewritingContext) { - this.declaredGraphs = graphixRewritingContext.getDeclaredGraphs(); - this.metadataProvider = graphixRewritingContext.getMetadataProvider(); - } - - @Override - public Expression visit(FromGraphClause fromGraphClause, ILangExpression arg) throws CompilationException { - // Establish our schema knowledge. - SchemaKnowledgeTable schemaKnowledgeTable; - if (fromGraphClause.getGraphConstructor() == null) { - DataverseName dataverseName = (fromGraphClause.getDataverseName() == null) - ? metadataProvider.getDefaultDataverseName() : fromGraphClause.getDataverseName(); - Identifier graphName = fromGraphClause.getGraphName(); - - // First, try to find our graph inside our declared graph set. - GraphIdentifier graphIdentifier = new GraphIdentifier(dataverseName, graphName.getValue()); - DeclareGraphStatement declaredGraph = declaredGraphs.get(graphIdentifier); - if (declaredGraph != null) { - schemaKnowledgeTable = new SchemaKnowledgeTable(declaredGraph.getGraphConstructor()); - - } else { - // Otherwise, fetch the graph from our metadata. - try { - MetadataTransactionContext metadataTxnContext = metadataProvider.getMetadataTxnContext(); - Graph graphFromMetadata = getGraph(metadataTxnContext, dataverseName, graphName.getValue()); - if (graphFromMetadata == null) { - throw new CompilationException(ErrorCode.COMPILATION_ERROR, fromGraphClause.getSourceLocation(), - "Graph " + graphName.getValue() + " does not exist."); - } - schemaKnowledgeTable = new SchemaKnowledgeTable(graphFromMetadata); - - } catch (AlgebricksException e) { - throw new CompilationException(ErrorCode.COMPILATION_ERROR, fromGraphClause.getSourceLocation(), - "Graph " + graphName.getValue() + " does not exist."); - } - } - - } else { - schemaKnowledgeTable = new SchemaKnowledgeTable(fromGraphClause.getGraphConstructor()); - } - - // Determine our resolution strategy. By default, we will perform bare-bones resolution. - IGraphElementResolver graphElementResolver; - String resolverMetadataKeyName = GraphixCompilationProvider.RESOLVER_METADATA_CONFIG; - if (metadataProvider.getConfig().containsKey(resolverMetadataKeyName)) { - String resolverProperty = metadataProvider.getProperty(resolverMetadataKeyName); - if (resolverProperty.equalsIgnoreCase(InferenceBasedResolver.METADATA_CONFIG_NAME)) { - graphElementResolver = new InferenceBasedResolver(schemaKnowledgeTable); - - } else { - throw new CompilationException(ErrorCode.ILLEGAL_SET_PARAMETER, resolverProperty); - } - - } else { - graphElementResolver = new InferenceBasedResolver(schemaKnowledgeTable); - } - - // Perform our resolution passes (repeat until we reach a fixed point or the iteration max). - String resolverIterationMaxMetadataKeyName = GraphixCompilationProvider.RESOLVER_ITERATION_MAX_METADATA_CONFIG; - long resolverIterationMax; - if (metadataProvider.getConfig().containsKey(resolverIterationMaxMetadataKeyName)) { - String resolverIterationMaxProperty = metadataProvider.getProperty(resolverIterationMaxMetadataKeyName); - try { - resolverIterationMax = Long.parseLong(resolverIterationMaxProperty); - - } catch (NumberFormatException e) { - throw new CompilationException(ErrorCode.ILLEGAL_SET_PARAMETER, resolverIterationMaxProperty); - } - - } else { - resolverIterationMax = DEFAULT_RESOLVER_ITERATION_MAX; - } - for (int i = 0; i < resolverIterationMax && !graphElementResolver.isAtFixedPoint(); i++) { - graphElementResolver.resolve(fromGraphClause); - if (i == resolverIterationMax - 1) { - LOGGER.warn("Number of iterations for element resolution has exceeded " + resolverIterationMax); - } - } - - // Perform the final pass of our FROM-GRAPH-CLAUSE. - new AbstractGraphixQueryVisitor() { - @Override - public Expression visit(VertexPatternExpr vertexPatternExpr, ILangExpression arg) { - if (vertexPatternExpr.getLabels().isEmpty()) { - vertexPatternExpr.getLabels().addAll(schemaKnowledgeTable.getVertexLabelSet()); - } - return vertexPatternExpr; - } - - @Override - public Expression visit(EdgePatternExpr edgePatternExpr, ILangExpression arg) throws CompilationException { - EdgeDescriptor edgeDescriptor = edgePatternExpr.getEdgeDescriptor(); - if (edgeDescriptor.getEdgeLabels().isEmpty()) { - edgeDescriptor.getEdgeLabels().addAll(schemaKnowledgeTable.getEdgeLabelSet()); - } - for (VertexPatternExpr internalVertex : edgePatternExpr.getInternalVertices()) { - internalVertex.accept(this, arg); - } - return edgePatternExpr; - } - }.visit(fromGraphClause, null); - - return null; - } -}
diff --git a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/visitor/VariableSubstitutionVisitor.java b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/visitor/VariableSubstitutionVisitor.java deleted file mode 100644 index 2f99a94..0000000 --- a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/visitor/VariableSubstitutionVisitor.java +++ /dev/null
@@ -1,55 +0,0 @@ -/* - * 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.graphix.lang.rewrites.visitor; - -import java.util.HashMap; -import java.util.Map; - -import org.apache.asterix.common.exceptions.CompilationException; -import org.apache.asterix.lang.common.base.Expression; -import org.apache.asterix.lang.common.base.ILangExpression; -import org.apache.asterix.lang.common.expression.VariableExpr; -import org.apache.asterix.lang.common.rewrites.LangRewritingContext; -import org.apache.asterix.lang.common.struct.VarIdentifier; -import org.apache.asterix.lang.sqlpp.util.SqlppRewriteUtil; -import org.apache.asterix.lang.sqlpp.visitor.base.AbstractSqlppExpressionScopingVisitor; - -/** - * Substitute qualifying {@link VariableExpr} nodes (via their {@link VarIdentifier}) with a deep-copy of an expression. - */ -public class VariableSubstitutionVisitor extends AbstractSqlppExpressionScopingVisitor { - private final Map<VarIdentifier, Expression> substitutionMap = new HashMap<>(); - - public VariableSubstitutionVisitor(LangRewritingContext context) { - super(context); - } - - public void addSubstitution(VarIdentifier varIdentifier, Expression substitution) { - substitutionMap.put(varIdentifier, substitution); - } - - @Override - public Expression visit(VariableExpr variableExpr, ILangExpression arg) throws CompilationException { - Expression substitution = substitutionMap.getOrDefault(variableExpr.getVar(), variableExpr); - if (substitution != null) { - substitution = (Expression) SqlppRewriteUtil.deepCopy(substitution); - } - return substitution; - } -}
diff --git a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/statement/CreateGraphStatement.java b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/statement/CreateGraphStatement.java index 730db34..235a45f 100644 --- a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/statement/CreateGraphStatement.java +++ b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/statement/CreateGraphStatement.java
@@ -26,8 +26,8 @@ import org.apache.asterix.common.exceptions.CompilationException; import org.apache.asterix.common.metadata.DataverseName; import org.apache.asterix.graphix.lang.expression.GraphConstructor; -import org.apache.asterix.graphix.lang.rewrites.visitor.IGraphixLangVisitor; import org.apache.asterix.graphix.lang.util.GraphStatementHandlingUtil; +import org.apache.asterix.graphix.lang.visitor.base.IGraphixLangVisitor; import org.apache.asterix.lang.common.visitor.base.ILangVisitor; import org.apache.asterix.metadata.declared.MetadataProvider; import org.apache.asterix.translator.IRequestParameters; @@ -37,10 +37,12 @@ /** * Statement for storing a {@link GraphConstructor} instance in our metadata. - * - A CREATE GRAPH statement MUST always include a graph name. - * - We can specify "CREATE OR REPLACE" to perform an upsert of our graph. - * - We can specify "CREATE ... IF NOT EXISTS" to insert the graph if it doesn't exist, and not raise an error if the - * graph already exists. + * <ul> + * <li>A CREATE GRAPH statement MUST always include a graph name.</li> + * <li>We can specify "CREATE OR REPLACE" to perform an upsert of our graph.</li> + * <li>We can specify "CREATE ... IF NOT EXISTS" to insert the graph if it doesn't exist, and not raise an error if the + * graph already exists.</li> + * </ul> */ public class CreateGraphStatement extends ExtensionStatement { private final GraphConstructor graphConstructor;
diff --git a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/statement/DeclareGraphStatement.java b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/statement/DeclareGraphStatement.java index b5fd970..b1b0e14 100644 --- a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/statement/DeclareGraphStatement.java +++ b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/statement/DeclareGraphStatement.java
@@ -25,7 +25,7 @@ import org.apache.asterix.common.metadata.DataverseName; import org.apache.asterix.graphix.app.translator.GraphixQueryTranslator; import org.apache.asterix.graphix.lang.expression.GraphConstructor; -import org.apache.asterix.graphix.lang.rewrites.visitor.IGraphixLangVisitor; +import org.apache.asterix.graphix.lang.visitor.base.IGraphixLangVisitor; import org.apache.asterix.lang.common.visitor.base.ILangVisitor; import org.apache.asterix.metadata.declared.MetadataProvider; import org.apache.asterix.translator.IRequestParameters; @@ -33,7 +33,7 @@ import org.apache.hyracks.api.client.IHyracksClientConnection; /** - * Statement for storing a {@link GraphConstructor} instance in our _context_ instead of our metadata. + * Statement for storing a {@link GraphConstructor} instance in our <i>context</i> instead of our metadata. */ public class DeclareGraphStatement extends ExtensionStatement { private final GraphConstructor graphConstructor;
diff --git a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/statement/GraphDropStatement.java b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/statement/GraphDropStatement.java index 9aadb97..e21286f 100644 --- a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/statement/GraphDropStatement.java +++ b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/statement/GraphDropStatement.java
@@ -24,8 +24,8 @@ import org.apache.asterix.app.translator.QueryTranslator; import org.apache.asterix.common.exceptions.CompilationException; import org.apache.asterix.common.metadata.DataverseName; -import org.apache.asterix.graphix.lang.rewrites.visitor.IGraphixLangVisitor; import org.apache.asterix.graphix.lang.util.GraphStatementHandlingUtil; +import org.apache.asterix.graphix.lang.visitor.base.IGraphixLangVisitor; import org.apache.asterix.lang.common.visitor.base.ILangVisitor; import org.apache.asterix.metadata.declared.MetadataProvider; import org.apache.asterix.translator.IRequestParameters; @@ -36,9 +36,11 @@ /** * Statement for removing a {@link org.apache.asterix.graphix.lang.expression.GraphConstructor} instance from our * metadata. - * - A DROP GRAPH statement MUST always include a graph name. - * - We can specify "DROP ... IF EXISTS" to drop the graph if it exists, and not raise an error if the graph doesn't - * exist. + * <ul> + * <li>A DROP GRAPH statement MUST always include a graph name.</li> + * <li>We can specify "DROP ... IF EXISTS" to drop the graph if it exists, and not raise an error if the graph + * doesn't exist,</li> + * </ul> */ public class GraphDropStatement extends ExtensionStatement { private final DataverseName dataverseName;
diff --git a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/statement/GraphElementDeclaration.java b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/statement/GraphElementDeclaration.java index 790a6be..32b720c 100644 --- a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/statement/GraphElementDeclaration.java +++ b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/statement/GraphElementDeclaration.java
@@ -23,9 +23,9 @@ import org.apache.asterix.algebra.extension.ExtensionStatement; import org.apache.asterix.common.exceptions.CompilationException; import org.apache.asterix.common.exceptions.ErrorCode; -import org.apache.asterix.graphix.common.metadata.GraphElementIdentifier; import org.apache.asterix.graphix.common.metadata.GraphIdentifier; -import org.apache.asterix.graphix.lang.rewrites.visitor.IGraphixLangVisitor; +import org.apache.asterix.graphix.common.metadata.IElementIdentifier; +import org.apache.asterix.graphix.lang.visitor.base.IGraphixLangVisitor; import org.apache.asterix.lang.common.base.Expression; import org.apache.asterix.lang.common.visitor.base.ILangVisitor; import org.apache.asterix.metadata.declared.MetadataProvider; @@ -38,16 +38,16 @@ * use this class to store the directly parsed AST and a normalized AST for the bodies themselves. */ public final class GraphElementDeclaration extends ExtensionStatement { - private final GraphElementIdentifier identifier; + private final IElementIdentifier identifier; private final Expression rawBody; private Expression normalizedBody; - public GraphElementDeclaration(GraphElementIdentifier identifier, Expression rawBody) { + public GraphElementDeclaration(IElementIdentifier identifier, Expression rawBody) { this.identifier = Objects.requireNonNull(identifier); this.rawBody = Objects.requireNonNull(rawBody); } - public GraphElementIdentifier getIdentifier() { + public IElementIdentifier getIdentifier() { return identifier; }
diff --git a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/struct/EdgeDescriptor.java b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/struct/EdgeDescriptor.java index d5f88e4..094cdb4 100644 --- a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/struct/EdgeDescriptor.java +++ b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/struct/EdgeDescriptor.java
@@ -19,23 +19,24 @@ package org.apache.asterix.graphix.lang.struct; import java.io.Serializable; -import java.util.List; import java.util.Objects; import java.util.Set; import java.util.stream.Collectors; -import org.apache.asterix.graphix.common.metadata.GraphElementIdentifier; -import org.apache.asterix.graphix.common.metadata.GraphIdentifier; +import org.apache.asterix.lang.common.base.Expression; import org.apache.asterix.lang.common.expression.VariableExpr; /** * Descriptor for a query edge instance. A query edge has the following: - * 1. A set of edge labels. - * 2. A variable associated with the query edge. - * 3. A pattern type. An edge pattern can either be a pure edge, or a sub-path. - * 4. A minimum number of hops (allowed to be NULL, indicating a minimum of 1 hop). - * 5. A maximum number of hops (not allowed to be NULL). - * 6. An edge direction (left to right, right to left, or undirected). + * <ul> + * <li>A set of edge labels.</li> + * <li>A variable associated with the query edge.</li> + * <li>A pattern type. An edge pattern can either be a pure edge, or a sub-path.</li> + * <li>A minimum number of hops (allowed to be NULL, indicating a minimum of 1 hop).</li> + * <li>A maximum number of hops (allowed to be NULL, indicating an unbounded maximum).</li> + * <li>An edge direction (left to right, right to left, or undirected).</li> + * <li>A filter expression (allowed to be NULL).</li> + * </ul> */ public class EdgeDescriptor implements Serializable { private static final long serialVersionUID = 1L; @@ -44,18 +45,20 @@ private final Integer minimumHops; private final Integer maximumHops; private final PatternType patternType; + private final Expression filterExpr; // We must be able to assign variables to our edges, as well as change the direction of UNDIRECTED edges. private VariableExpr variableExpr; private EdgeDirection edgeDirection; public EdgeDescriptor(EdgeDirection edgeDirection, PatternType patternType, Set<ElementLabel> edgeLabels, - VariableExpr variableExpr, Integer minimumHops, Integer maximumHops) { - this.edgeDirection = edgeDirection; + Expression filterExpr, VariableExpr variableExpr, Integer minimumHops, Integer maximumHops) { this.edgeLabels = edgeLabels; + this.patternType = patternType; this.minimumHops = minimumHops; this.maximumHops = maximumHops; - this.patternType = patternType; + this.filterExpr = filterExpr; + this.edgeDirection = edgeDirection; this.variableExpr = variableExpr; } @@ -83,6 +86,10 @@ return patternType; } + public Expression getFilterExpr() { + return filterExpr; + } + public VariableExpr getVariableExpr() { return variableExpr; } @@ -91,15 +98,9 @@ this.variableExpr = variableExpr; } - public List<GraphElementIdentifier> generateIdentifiers(GraphIdentifier graphIdentifier) { - return edgeLabels.stream() - .map(e -> new GraphElementIdentifier(graphIdentifier, GraphElementIdentifier.Kind.EDGE, e)) - .collect(Collectors.toList()); - } - @Override public int hashCode() { - return Objects.hash(edgeDirection, patternType, edgeLabels, variableExpr, minimumHops, maximumHops); + return Objects.hash(edgeDirection, patternType, edgeLabels, variableExpr, filterExpr, minimumHops, maximumHops); } @Override @@ -115,6 +116,7 @@ && Objects.equals(this.patternType, that.patternType) && Objects.equals(this.edgeLabels, that.edgeLabels) && Objects.equals(this.variableExpr, that.variableExpr) + && Objects.equals(this.filterExpr, that.filterExpr) && Objects.equals(this.minimumHops, that.minimumHops) && Objects.equals(this.maximumHops, that.maximumHops); } @@ -123,16 +125,31 @@ public String toString() { String labelsString = edgeLabels.stream().map(ElementLabel::toString).collect(Collectors.joining("|")); String variableString = (variableExpr != null) ? variableExpr.getVar().toString() : ""; - String subPathString = (patternType != PatternType.PATH) ? "" - : "{" + ((minimumHops == null) ? "" : minimumHops) + "," + maximumHops + "}"; - return String.format("%s-[%s:(%s)%s]-%s", (edgeDirection == EdgeDirection.LEFT_TO_RIGHT) ? "" : "<", - variableString, labelsString, subPathString, (edgeDirection == EdgeDirection.RIGHT_TO_LEFT) ? "" : ">"); + String minHopsString = ((minimumHops == null) ? "" : minimumHops.toString()); + String maxHopsString = ((maximumHops == null) ? "" : maximumHops.toString()); + String subPathString = (patternType != PatternType.PATH) ? "" : "{" + minHopsString + "," + maxHopsString + "}"; + String filterString = (filterExpr == null) ? "" : (" WHERE " + filterExpr + " "); + return String.format("%s-[%s:(%s)%s%s]-%s", (edgeDirection == EdgeDirection.LEFT_TO_RIGHT) ? "" : "<", + variableString, labelsString, subPathString, filterString, + (edgeDirection == EdgeDirection.RIGHT_TO_LEFT) ? "" : ">"); } public enum EdgeDirection { - LEFT_TO_RIGHT, - RIGHT_TO_LEFT, - UNDIRECTED + LEFT_TO_RIGHT("L2R"), + RIGHT_TO_LEFT("R2L"), + UNDIRECTED("U"); + + // For printing purposes... + private final String shortName; + + EdgeDirection(String shortName) { + this.shortName = shortName; + } + + @Override + public String toString() { + return shortName; + } } public enum PatternType {
diff --git a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/struct/ElementLabel.java b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/struct/ElementLabel.java index a65afc6..a3807ce 100644 --- a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/struct/ElementLabel.java +++ b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/struct/ElementLabel.java
@@ -21,41 +21,36 @@ import java.io.Serializable; import java.util.Objects; +/** + * Label for a vertex, uniquely identified by the label name. + */ public class ElementLabel implements Serializable { - private static final long serialVersionUID = 1L; + private static final long serialVersionUID = 2L; private final String labelName; - private boolean isInferred; + private final boolean isNegated; - public ElementLabel(String labelName) { - this(labelName, false); - } - - private ElementLabel(String labelName, boolean isInferred) { + public ElementLabel(String labelName, boolean isNegated) { this.labelName = Objects.requireNonNull(labelName); - this.isInferred = isInferred; + this.isNegated = isNegated; } - public ElementLabel asInferred() { - return new ElementLabel(labelName, true); + public boolean isNegated() { + return isNegated; } - public void markInferred(boolean isInferred) { - this.isInferred = isInferred; - } - - public boolean isInferred() { - return isInferred; - } - - @Override - public String toString() { + public String getLabelName() { return labelName; } @Override + public String toString() { + return (isNegated ? "NOT " : "") + labelName; + } + + @Override public int hashCode() { - return Objects.hashCode(labelName); + return Objects.hash(labelName, isNegated); } @Override @@ -65,7 +60,7 @@ } if (o instanceof ElementLabel) { ElementLabel that = (ElementLabel) o; - return this.labelName.equals(that.labelName); + return this.labelName.equals(that.labelName) && this.isNegated == that.isNegated; } return false; }
diff --git a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/struct/PatternGroup.java b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/struct/PatternGroup.java new file mode 100644 index 0000000..7356668 --- /dev/null +++ b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/struct/PatternGroup.java
@@ -0,0 +1,86 @@ +/* + * 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.graphix.lang.struct; + +import java.util.HashSet; +import java.util.Iterator; +import java.util.NoSuchElementException; +import java.util.Set; + +import org.apache.asterix.graphix.lang.expression.EdgePatternExpr; +import org.apache.asterix.graphix.lang.expression.VertexPatternExpr; +import org.apache.asterix.lang.common.base.AbstractExpression; + +/** + * A container for a collection of vertices and edges. In contrast to the expression + * {@link org.apache.asterix.graphix.lang.expression.PathPatternExpr}, the following elements do not have to be + * connected. + */ +public class PatternGroup implements Iterable<AbstractExpression> { + // Users should add to the following sets directly. + private final Set<VertexPatternExpr> vertexPatternSet = new HashSet<>(); + private final Set<EdgePatternExpr> edgePatternSet = new HashSet<>(); + + public Set<VertexPatternExpr> getVertexPatternSet() { + return vertexPatternSet; + } + + public Set<EdgePatternExpr> getEdgePatternSet() { + return edgePatternSet; + } + + public void replace(VertexPatternExpr searchExpression, VertexPatternExpr replaceExpression) { + if (!vertexPatternSet.contains(searchExpression)) { + throw new IllegalArgumentException("Vertex pattern not found in group!"); + } + vertexPatternSet.remove(searchExpression); + vertexPatternSet.add(replaceExpression); + } + + public void replace(EdgePatternExpr searchExpression, EdgePatternExpr replaceExpression) { + if (!edgePatternSet.contains(searchExpression)) { + throw new IllegalArgumentException("Edge pattern not found in group!"); + } + edgePatternSet.remove(searchExpression); + edgePatternSet.add(replaceExpression); + } + + @Override + public Iterator<AbstractExpression> iterator() { + Iterator<VertexPatternExpr> vertexPatternIterator = vertexPatternSet.iterator(); + Iterator<EdgePatternExpr> edgePatternIterator = edgePatternSet.iterator(); + return new Iterator<>() { + @Override + public boolean hasNext() { + return vertexPatternIterator.hasNext() || edgePatternIterator.hasNext(); + } + + @Override + public AbstractExpression next() { + if (vertexPatternIterator.hasNext()) { + return vertexPatternIterator.next(); + } + if (edgePatternIterator.hasNext()) { + return edgePatternIterator.next(); + } + throw new NoSuchElementException(); + } + }; + } +}
diff --git a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/util/GraphStatementHandlingUtil.java b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/util/GraphStatementHandlingUtil.java index c4cc975..df2569f 100644 --- a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/util/GraphStatementHandlingUtil.java +++ b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/util/GraphStatementHandlingUtil.java
@@ -30,16 +30,17 @@ import org.apache.asterix.common.exceptions.ErrorCode; import org.apache.asterix.common.metadata.DataverseName; import org.apache.asterix.graphix.app.translator.GraphixQueryTranslator; -import org.apache.asterix.graphix.common.metadata.GraphElementIdentifier; +import org.apache.asterix.graphix.common.metadata.EdgeIdentifier; import org.apache.asterix.graphix.common.metadata.GraphIdentifier; +import org.apache.asterix.graphix.common.metadata.VertexIdentifier; import org.apache.asterix.graphix.extension.GraphixMetadataExtension; import org.apache.asterix.graphix.lang.clause.FromGraphClause; import org.apache.asterix.graphix.lang.expression.GraphConstructor; -import org.apache.asterix.graphix.lang.rewrites.GraphixQueryRewriter; -import org.apache.asterix.graphix.lang.rewrites.visitor.AbstractGraphixQueryVisitor; +import org.apache.asterix.graphix.lang.rewrite.GraphixQueryRewriter; import org.apache.asterix.graphix.lang.statement.CreateGraphStatement; import org.apache.asterix.graphix.lang.statement.GraphDropStatement; import org.apache.asterix.graphix.lang.statement.GraphElementDeclaration; +import org.apache.asterix.graphix.lang.visitor.base.AbstractGraphixQueryVisitor; import org.apache.asterix.graphix.metadata.entity.dependency.DependencyIdentifier; import org.apache.asterix.graphix.metadata.entity.dependency.GraphRequirements; import org.apache.asterix.graphix.metadata.entity.dependency.IEntityRequirements; @@ -139,7 +140,7 @@ schemaBuilder.addVertex(vertex.getLabel(), vertex.getPrimaryKeyFields(), vertex.getDefinition()); switch (schemaBuilder.getLastError()) { case NO_ERROR: - GraphElementIdentifier id = schemaVertex.getIdentifier(); + VertexIdentifier id = schemaVertex.getIdentifier(); GraphElementDeclaration decl = new GraphElementDeclaration(id, vertex.getExpression()); decl.setSourceLocation(vertex.getSourceLocation()); graphElementDeclarations.add(decl); @@ -160,7 +161,7 @@ edge.getDestinationKeyFields(), edge.getSourceKeyFields(), edge.getDefinition()); switch (schemaBuilder.getLastError()) { case NO_ERROR: - GraphElementIdentifier id = schemaEdge.getIdentifier(); + EdgeIdentifier id = schemaEdge.getIdentifier(); GraphElementDeclaration decl = new GraphElementDeclaration(id, edge.getExpression()); decl.setSourceLocation(edge.getSourceLocation()); graphElementDeclarations.add(decl);
diff --git a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/visitor/AbstractGraphixQueryVisitor.java b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/visitor/base/AbstractGraphixQueryVisitor.java similarity index 77% rename from asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/visitor/AbstractGraphixQueryVisitor.java rename to asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/visitor/base/AbstractGraphixQueryVisitor.java index 8fdc0da..c281058 100644 --- a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/visitor/AbstractGraphixQueryVisitor.java +++ b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/visitor/base/AbstractGraphixQueryVisitor.java
@@ -16,11 +16,12 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.asterix.graphix.lang.rewrites.visitor; +package org.apache.asterix.graphix.lang.visitor.base; + +import java.util.ListIterator; import org.apache.asterix.common.exceptions.CompilationException; import org.apache.asterix.graphix.lang.clause.FromGraphClause; -import org.apache.asterix.graphix.lang.clause.GraphSelectBlock; import org.apache.asterix.graphix.lang.clause.MatchClause; import org.apache.asterix.graphix.lang.expression.EdgePatternExpr; import org.apache.asterix.graphix.lang.expression.GraphConstructor; @@ -31,11 +32,10 @@ import org.apache.asterix.graphix.lang.statement.GraphDropStatement; import org.apache.asterix.graphix.lang.statement.GraphElementDeclaration; import org.apache.asterix.graphix.lang.struct.EdgeDescriptor; -import org.apache.asterix.lang.common.base.AbstractClause; import org.apache.asterix.lang.common.base.Expression; import org.apache.asterix.lang.common.base.ILangExpression; import org.apache.asterix.lang.sqlpp.clause.AbstractBinaryCorrelateClause; -import org.apache.asterix.lang.sqlpp.clause.SelectBlock; +import org.apache.asterix.lang.sqlpp.clause.FromClause; import org.apache.asterix.lang.sqlpp.visitor.base.AbstractSqlppSimpleExpressionVisitor; public abstract class AbstractGraphixQueryVisitor extends AbstractSqlppSimpleExpressionVisitor @@ -63,34 +63,13 @@ } @Override - public Expression visit(SelectBlock sb, ILangExpression arg) throws CompilationException { - return (sb instanceof GraphSelectBlock) ? this.visit((GraphSelectBlock) sb, arg) : super.visit(sb, arg); - } + public Expression visit(FromClause fc, ILangExpression arg) throws CompilationException { + if (fc instanceof FromGraphClause) { + return visit((FromGraphClause) fc, arg); - @Override - public Expression visit(GraphSelectBlock gsb, ILangExpression arg) throws CompilationException { - // Traverse in the same order as a regular SELECT-BLOCK: FROM, LET/WHERE, GROUP-BY, LET/HAVING, SELECT. - if (gsb.hasFromGraphClause()) { - gsb.getFromGraphClause().accept(this, arg); - - } else if (gsb.hasFromClause()) { - gsb.getFromClause().accept(this, arg); + } else { + return super.visit(fc, arg); } - if (gsb.hasLetWhereClauses()) { - for (AbstractClause clause : gsb.getLetWhereList()) { - clause.accept(this, arg); - } - } - if (gsb.hasGroupbyClause()) { - gsb.getGroupbyClause().accept(this, arg); - } - if (gsb.hasLetHavingClausesAfterGroupby()) { - for (AbstractClause clause : gsb.getLetHavingListAfterGroupby()) { - clause.accept(this, arg); - } - } - gsb.getSelectClause().accept(this, arg); - return null; } @Override @@ -110,8 +89,9 @@ @Override public Expression visit(MatchClause mc, ILangExpression arg) throws CompilationException { - for (PathPatternExpr pathPatternExpr : mc.getPathExpressions()) { - pathPatternExpr.accept(this, arg); + ListIterator<PathPatternExpr> ppeIterator = mc.getPathExpressions().listIterator(); + while (ppeIterator.hasNext()) { + ppeIterator.set((PathPatternExpr) ppeIterator.next().accept(this, arg)); } return null; } @@ -138,8 +118,11 @@ if (edgeDescriptor.getVariableExpr() != null) { edgeDescriptor.getVariableExpr().accept(this, arg); } - for (VertexPatternExpr internalVertex : epe.getInternalVertices()) { - internalVertex.accept(this, arg); + if (epe.getInternalVertex() != null) { + epe.getInternalVertex().accept(this, arg); + } + if (edgeDescriptor.getFilterExpr() != null) { + edgeDescriptor.getFilterExpr().accept(this, arg); } return epe; } @@ -149,6 +132,9 @@ if (vpe.getVariableExpr() != null) { vpe.getVariableExpr().accept(this, arg); } + if (vpe.getFilterExpr() != null) { + vpe.getFilterExpr().accept(this, arg); + } return vpe; }
diff --git a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/visitor/IGraphixLangVisitor.java b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/visitor/base/IGraphixLangVisitor.java similarity index 92% rename from asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/visitor/IGraphixLangVisitor.java rename to asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/visitor/base/IGraphixLangVisitor.java index 9d5ece9..9da17d9 100644 --- a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/visitor/IGraphixLangVisitor.java +++ b/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/visitor/base/IGraphixLangVisitor.java
@@ -16,11 +16,10 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.asterix.graphix.lang.rewrites.visitor; +package org.apache.asterix.graphix.lang.visitor.base; import org.apache.asterix.common.exceptions.CompilationException; import org.apache.asterix.graphix.lang.clause.FromGraphClause; -import org.apache.asterix.graphix.lang.clause.GraphSelectBlock; import org.apache.asterix.graphix.lang.clause.MatchClause; import org.apache.asterix.graphix.lang.expression.EdgePatternExpr; import org.apache.asterix.graphix.lang.expression.GraphConstructor; @@ -47,8 +46,6 @@ R visit(GraphDropStatement gds, T arg) throws CompilationException; - R visit(GraphSelectBlock gsb, T arg) throws CompilationException; - R visit(FromGraphClause fgc, T arg) throws CompilationException; R visit(MatchClause mc, T arg) throws CompilationException;
diff --git a/asterix-graphix/src/main/java/org/apache/asterix/graphix/metadata/entity/dependency/FunctionRequirements.java b/asterix-graphix/src/main/java/org/apache/asterix/graphix/metadata/entity/dependency/FunctionRequirements.java index 0b88f27..ec3964e 100644 --- a/asterix-graphix/src/main/java/org/apache/asterix/graphix/metadata/entity/dependency/FunctionRequirements.java +++ b/asterix-graphix/src/main/java/org/apache/asterix/graphix/metadata/entity/dependency/FunctionRequirements.java
@@ -29,7 +29,7 @@ /** * A collection of {@link org.apache.asterix.graphix.metadata.entity.schema.Graph} dependencies associated with a - * {@link org.apache.asterix.metadata.entities.Function} instance. This does **not** include non-graph dependencies + * {@link org.apache.asterix.metadata.entities.Function} instance. This does <b>not</b> include non-graph dependencies * for functions. */ public class FunctionRequirements implements IEntityRequirements {
diff --git a/asterix-graphix/src/main/java/org/apache/asterix/graphix/metadata/entity/dependency/ViewRequirements.java b/asterix-graphix/src/main/java/org/apache/asterix/graphix/metadata/entity/dependency/ViewRequirements.java index 6c84eab..ade330e 100644 --- a/asterix-graphix/src/main/java/org/apache/asterix/graphix/metadata/entity/dependency/ViewRequirements.java +++ b/asterix-graphix/src/main/java/org/apache/asterix/graphix/metadata/entity/dependency/ViewRequirements.java
@@ -29,7 +29,7 @@ /** * A collection of {@link org.apache.asterix.graphix.metadata.entity.schema.Graph} dependencies associated with a view - * instance. This does **not** include non-graph dependencies for views. + * instance. This does <b>not</b> include non-graph dependencies for views. */ public class ViewRequirements implements IEntityRequirements { private static final long serialVersionUID = 1L;
diff --git a/asterix-graphix/src/main/java/org/apache/asterix/graphix/metadata/entity/schema/Edge.java b/asterix-graphix/src/main/java/org/apache/asterix/graphix/metadata/entity/schema/Edge.java index 1c4cc17..d9a8ca9 100644 --- a/asterix-graphix/src/main/java/org/apache/asterix/graphix/metadata/entity/schema/Edge.java +++ b/asterix-graphix/src/main/java/org/apache/asterix/graphix/metadata/entity/schema/Edge.java
@@ -21,24 +21,24 @@ import java.util.List; import java.util.Objects; -import org.apache.asterix.graphix.common.metadata.GraphElementIdentifier; +import org.apache.asterix.graphix.common.metadata.EdgeIdentifier; import org.apache.asterix.graphix.lang.struct.ElementLabel; /** * Metadata representation of an edge. An edge consists of the following: - * 1. A {@link GraphElementIdentifier}, to uniquely identify the edge across other graph elements. - * 2. An {@link ElementLabel} instance associated with the source vertex. - * 3. An {@link ElementLabel} instance associated with the destination vertex. - * 4. A list of source key fields, associated with the definition body. - * 5. A list of destination key fields, associated with the definition body. - * 6. A SQL++ string denoting the definition body. + * <ul> + * <li>A {@link EdgeIdentifier}, to uniquely identify the edge across other graph elements.</li> + * <li>An {@link ElementLabel} instance associated with the source vertex.</li> + * <li>An {@link ElementLabel} instance associated with the destination vertex.</li> + * <li>A list of source key fields, associated with the definition body.</li> + * <li>A list of destination key fields, associated with the definition body.</li> + * <li>A SQL++ string denoting the definition body.</li> + * </ul> */ public class Edge implements IElement { private static final long serialVersionUID = 1L; - private final GraphElementIdentifier identifier; - private final ElementLabel sourceVertexLabel; - private final ElementLabel destinationVertexLabel; + private final EdgeIdentifier identifier; private final List<List<String>> sourceKeyFieldNames; private final List<List<String>> destinationKeyFieldNames; private final String definitionBody; @@ -46,11 +46,9 @@ /** * Use {@link Schema.Builder} to build Edge instances instead of this constructor. */ - Edge(GraphElementIdentifier identifier, ElementLabel sourceVertexLabel, ElementLabel destinationVertexLabel, - List<List<String>> sourceKeyFieldNames, List<List<String>> destKeyFieldNames, String definitionBody) { + Edge(EdgeIdentifier identifier, List<List<String>> sourceKeyFieldNames, List<List<String>> destKeyFieldNames, + String definitionBody) { this.identifier = Objects.requireNonNull(identifier); - this.sourceVertexLabel = Objects.requireNonNull(sourceVertexLabel); - this.destinationVertexLabel = Objects.requireNonNull(destinationVertexLabel); this.sourceKeyFieldNames = Objects.requireNonNull(sourceKeyFieldNames); this.destinationKeyFieldNames = Objects.requireNonNull(destKeyFieldNames); this.definitionBody = Objects.requireNonNull(definitionBody); @@ -82,11 +80,11 @@ } public ElementLabel getDestinationLabel() { - return destinationVertexLabel; + return identifier.getDestinationLabel(); } public ElementLabel getSourceLabel() { - return sourceVertexLabel; + return identifier.getSourceLabel(); } public List<List<String>> getSourceKeyFieldNames() { @@ -98,13 +96,13 @@ } @Override - public GraphElementIdentifier getIdentifier() { + public EdgeIdentifier getIdentifier() { return identifier; } @Override public ElementLabel getLabel() { - return identifier.getElementLabel(); + return identifier.getEdgeLabel(); } @Override
diff --git a/asterix-graphix/src/main/java/org/apache/asterix/graphix/metadata/entity/schema/IElement.java b/asterix-graphix/src/main/java/org/apache/asterix/graphix/metadata/entity/schema/IElement.java index 3637a45..0e38e92 100644 --- a/asterix-graphix/src/main/java/org/apache/asterix/graphix/metadata/entity/schema/IElement.java +++ b/asterix-graphix/src/main/java/org/apache/asterix/graphix/metadata/entity/schema/IElement.java
@@ -20,17 +20,17 @@ import java.io.Serializable; -import org.apache.asterix.graphix.common.metadata.GraphElementIdentifier; +import org.apache.asterix.graphix.common.metadata.IElementIdentifier; import org.apache.asterix.graphix.lang.struct.ElementLabel; /** * Metadata interface for a graph element (i.e. edge or vertex). An element has the following: - * 1. A {@link GraphElementIdentifier}, to uniquely identify the element across other graph elements. - * 2. A {@link ElementLabel} unique amongst the element classes (e.g. an edge label is unique amongst all graph edges). + * 1. A {@link Serializable}, to uniquely identify the element across other graph elements. + * 2. A {@link ElementLabel} unique amongst the element classes. * 3. A non-null SQL++ string, representing a graph element body. */ public interface IElement extends Serializable { - GraphElementIdentifier getIdentifier(); + IElementIdentifier getIdentifier(); ElementLabel getLabel();
diff --git a/asterix-graphix/src/main/java/org/apache/asterix/graphix/metadata/entity/schema/Schema.java b/asterix-graphix/src/main/java/org/apache/asterix/graphix/metadata/entity/schema/Schema.java index d19590f..c2f9e54 100644 --- a/asterix-graphix/src/main/java/org/apache/asterix/graphix/metadata/entity/schema/Schema.java +++ b/asterix-graphix/src/main/java/org/apache/asterix/graphix/metadata/entity/schema/Schema.java
@@ -18,23 +18,26 @@ */ package org.apache.asterix.graphix.metadata.entity.schema; -import static org.apache.asterix.graphix.common.metadata.GraphElementIdentifier.Kind.EDGE; -import static org.apache.asterix.graphix.common.metadata.GraphElementIdentifier.Kind.VERTEX; - import java.io.Serializable; import java.util.ArrayList; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Objects; +import java.util.Set; -import org.apache.asterix.graphix.common.metadata.GraphElementIdentifier; +import org.apache.asterix.graphix.common.metadata.EdgeIdentifier; import org.apache.asterix.graphix.common.metadata.GraphIdentifier; +import org.apache.asterix.graphix.common.metadata.VertexIdentifier; import org.apache.asterix.graphix.lang.struct.ElementLabel; /** * Metadata representation of a graph schema. A graph schema consists of: - * 1. A list of {@link Vertex} instances. - * 2. A list of {@link Edge} instances, which link the aforementioned vertices. + * <ul> + * <li>A list of {@link Vertex} instances.</li> + * <li>A list of {@link Edge} instances, which link the aforementioned vertices.</li> + * </ul> */ public class Schema implements Serializable { private static final long serialVersionUID = 1L; @@ -59,7 +62,7 @@ public static class Builder { private final Map<ElementLabel, Vertex> vertexLabelMap = new HashMap<>(); - private final Map<ElementLabel, List<Edge>> edgeLabelMap = new HashMap<>(); + private final Map<EdgeLabel, List<Edge>> edgeLabelMap = new HashMap<>(); // We aim to populate the schema object below. private final Schema workingSchema; @@ -74,12 +77,12 @@ /** * @return Null if a vertex with the same label already exists. The vertex to-be-added otherwise. */ - public Vertex addVertex(ElementLabel vertexLabel, List<List<String>> primaryKeyFieldNames, String definition) { - if (!vertexLabelMap.containsKey(vertexLabel)) { - GraphElementIdentifier identifier = new GraphElementIdentifier(graphIdentifier, VERTEX, vertexLabel); + public Vertex addVertex(ElementLabel elementLabel, List<List<String>> primaryKeyFieldNames, String definition) { + if (!vertexLabelMap.containsKey(elementLabel)) { + VertexIdentifier identifier = new VertexIdentifier(graphIdentifier, elementLabel); Vertex newVertex = new Vertex(identifier, primaryKeyFieldNames, definition); workingSchema.vertexList.add(newVertex); - vertexLabelMap.put(vertexLabel, newVertex); + vertexLabelMap.put(elementLabel, newVertex); return newVertex; } else { @@ -102,22 +105,27 @@ } else if (!vertexLabelMap.containsKey(destinationLabel)) { lastError = Error.DESTINATION_VERTEX_NOT_FOUND; return null; + } - } else if (edgeLabelMap.containsKey(edgeLabel)) { + // Ensure we have unique <source, edge, dest> triples. + EdgeLabel edgePatternLabel = new EdgeLabel(); + edgePatternLabel.endpointLabels.add(sourceLabel); + edgePatternLabel.endpointLabels.add(destinationLabel); + edgePatternLabel.edgeLabel = edgeLabel; + if (edgeLabelMap.containsKey(edgePatternLabel)) { lastError = Error.EDGE_LABEL_CONFLICT; return null; } // Update our schema. - GraphElementIdentifier identifier = new GraphElementIdentifier(graphIdentifier, EDGE, edgeLabel); - Edge newEdge = new Edge(identifier, sourceLabel, destinationLabel, sourceKeyFieldNames, - destinationKeyFieldNames, definitionBody); + EdgeIdentifier identifier = new EdgeIdentifier(graphIdentifier, sourceLabel, edgeLabel, destinationLabel); + Edge newEdge = new Edge(identifier, sourceKeyFieldNames, destinationKeyFieldNames, definitionBody); workingSchema.edgeList.add(newEdge); // Update our edge label map. ArrayList<Edge> edgeList = new ArrayList<>(); edgeList.add(newEdge); - edgeLabelMap.put(edgeLabel, edgeList); + edgeLabelMap.put(edgePatternLabel, edgeList); return newEdge; } @@ -137,4 +145,27 @@ DESTINATION_VERTEX_NOT_FOUND } } + + private static class EdgeLabel { + public Set<ElementLabel> endpointLabels = new HashSet<>(); + public ElementLabel edgeLabel = null; + + @Override + public int hashCode() { + return Objects.hash(edgeLabel, endpointLabels); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o instanceof EdgeLabel) { + EdgeLabel that = (EdgeLabel) o; + return Objects.equals(this.endpointLabels, that.endpointLabels) + && Objects.equals(this.edgeLabel, that.edgeLabel); + } + return false; + } + } }
diff --git a/asterix-graphix/src/main/java/org/apache/asterix/graphix/metadata/entity/schema/Vertex.java b/asterix-graphix/src/main/java/org/apache/asterix/graphix/metadata/entity/schema/Vertex.java index dd4a21e..e25df84 100644 --- a/asterix-graphix/src/main/java/org/apache/asterix/graphix/metadata/entity/schema/Vertex.java +++ b/asterix-graphix/src/main/java/org/apache/asterix/graphix/metadata/entity/schema/Vertex.java
@@ -21,26 +21,28 @@ import java.util.List; import java.util.Objects; -import org.apache.asterix.graphix.common.metadata.GraphElementIdentifier; +import org.apache.asterix.graphix.common.metadata.VertexIdentifier; import org.apache.asterix.graphix.lang.struct.ElementLabel; /** * Metadata representation of a vertex. A vertex consists of the following: - * 1. A {@link GraphElementIdentifier}, to uniquely identify the vertex across other graph elements. - * 2. A list of primary key fields, associated with the definition body. - * 3. A SQL++ string denoting the definition body. + * <ul> + * <li>A {@link VertexIdentifier}, to uniquely identify the vertex across other graph elements.</li> + * <li>A list of primary key fields, associated with the definition body.</li> + * <li>A SQL++ string denoting the definition body.</li> + * </ul> */ public class Vertex implements IElement { private static final long serialVersionUID = 1L; - private final GraphElementIdentifier identifier; + private final VertexIdentifier identifier; private final List<List<String>> primaryKeyFieldNames; private final String definitionBody; /** * Use {@link Schema.Builder} to build Vertex instances instead of this constructor. */ - Vertex(GraphElementIdentifier identifier, List<List<String>> primaryKeyFieldNames, String definitionBody) { + Vertex(VertexIdentifier identifier, List<List<String>> primaryKeyFieldNames, String definitionBody) { this.identifier = Objects.requireNonNull(identifier); this.primaryKeyFieldNames = primaryKeyFieldNames; this.definitionBody = Objects.requireNonNull(definitionBody); @@ -51,13 +53,13 @@ } @Override - public GraphElementIdentifier getIdentifier() { + public VertexIdentifier getIdentifier() { return identifier; } @Override public ElementLabel getLabel() { - return identifier.getElementLabel(); + return identifier.getVertexLabel(); } @Override
diff --git a/asterix-graphix/src/main/java/org/apache/asterix/graphix/metadata/entitytupletranslators/GraphTupleTranslator.java b/asterix-graphix/src/main/java/org/apache/asterix/graphix/metadata/entitytupletranslators/GraphTupleTranslator.java index 446ae27..fef8772 100644 --- a/asterix-graphix/src/main/java/org/apache/asterix/graphix/metadata/entitytupletranslators/GraphTupleTranslator.java +++ b/asterix-graphix/src/main/java/org/apache/asterix/graphix/metadata/entitytupletranslators/GraphTupleTranslator.java
@@ -126,7 +126,7 @@ // Read in the label name. IAObject labelNameObj = VERTEX_RECORD_DETAIL.getObjectForField(vertex, FIELD_NAME_LABEL); - ElementLabel vertexLabel = new ElementLabel(((AString) labelNameObj).getStringValue()); + ElementLabel elementLabel = new ElementLabel(((AString) labelNameObj).getStringValue(), false); // Read in the primary key fields. List<List<String>> primaryKeyFields = new ArrayList<>(); @@ -142,14 +142,14 @@ String definitionBody = ((AString) bodyObj).getStringValue(); // Read in the vertex definition, and perform validation of the metadata record. - schemaBuilder.addVertex(vertexLabel, primaryKeyFields, definitionBody); + schemaBuilder.addVertex(elementLabel, primaryKeyFields, definitionBody); switch (schemaBuilder.getLastError()) { case NO_ERROR: break; case VERTEX_LABEL_CONFLICT: throw new AsterixException(ErrorCode.METADATA_ERROR, - "Conflicting vertex label found: " + vertexLabel); + "Conflicting vertex label found: " + elementLabel); default: throw new AsterixException(ErrorCode.METADATA_ERROR, @@ -165,15 +165,16 @@ // Read in the label name. IAObject labelNameObj = EDGE_RECORD_DETAIL.getObjectForField(edge, FIELD_NAME_LABEL); - ElementLabel edgeLabel = new ElementLabel(((AString) labelNameObj).getStringValue()); + ElementLabel edgeLabel = new ElementLabel(((AString) labelNameObj).getStringValue(), false); // Read in the destination label name. IAObject destinationLabelNameObj = EDGE_RECORD_DETAIL.getObjectForField(edge, FIELD_NAME_DESTINATION_LABEL); - ElementLabel destinationLabel = new ElementLabel(((AString) destinationLabelNameObj).getStringValue()); + AString destinationLabelString = (AString) destinationLabelNameObj; + ElementLabel destinationLabel = new ElementLabel(destinationLabelString.getStringValue(), false); // Read in the source label name. IAObject sourceLabelNameObj = EDGE_RECORD_DETAIL.getObjectForField(edge, FIELD_NAME_SOURCE_LABEL); - ElementLabel sourceLabel = new ElementLabel(((AString) sourceLabelNameObj).getStringValue()); + ElementLabel sourceLabel = new ElementLabel(((AString) sourceLabelNameObj).getStringValue(), false); // Read in the source key fields. List<List<String>> sourceKeyFields = new ArrayList<>();
diff --git a/asterix-graphix/src/main/java/org/apache/asterix/graphix/runtime/evaluator/AppendInternalPathDescriptor.java b/asterix-graphix/src/main/java/org/apache/asterix/graphix/runtime/evaluator/AppendInternalPathDescriptor.java new file mode 100644 index 0000000..5b07204 --- /dev/null +++ b/asterix-graphix/src/main/java/org/apache/asterix/graphix/runtime/evaluator/AppendInternalPathDescriptor.java
@@ -0,0 +1,142 @@ +/* + * 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.graphix.runtime.evaluator; + +import static org.apache.asterix.graphix.runtime.pointable.InternalPathPointable.HEADER_EDGE_LIST_END; +import static org.apache.asterix.graphix.runtime.pointable.InternalPathPointable.HEADER_VERTEX_LIST_END; +import static org.apache.asterix.graphix.runtime.pointable.InternalPathPointable.PATH_HEADER_LENGTH; +import static org.apache.asterix.graphix.runtime.pointable.InternalPathPointable.PATH_SERIALIZED_TYPE_TAG; +import static org.apache.asterix.graphix.runtime.pointable.SinglyLinkedListPointable.LIST_ITEM_LENGTH_SIZE; + +import java.io.DataOutput; +import java.io.IOException; + +import org.apache.asterix.graphix.function.GraphixFunctionIdentifiers; +import org.apache.asterix.runtime.evaluators.base.AbstractScalarFunctionDynamicDescriptor; +import org.apache.asterix.runtime.evaluators.functions.PointableHelper; +import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier; +import org.apache.hyracks.algebricks.runtime.base.IEvaluatorContext; +import org.apache.hyracks.algebricks.runtime.base.IScalarEvaluator; +import org.apache.hyracks.algebricks.runtime.base.IScalarEvaluatorFactory; +import org.apache.hyracks.api.exceptions.HyracksDataException; +import org.apache.hyracks.data.std.api.IPointable; +import org.apache.hyracks.data.std.primitive.IntegerPointable; +import org.apache.hyracks.data.std.primitive.VoidPointable; +import org.apache.hyracks.data.std.util.ArrayBackedValueStorage; +import org.apache.hyracks.dataflow.common.data.accessors.IFrameTupleReference; + +/** + * Given a vertex (our first argument), an edge (our second argument), and an existing path (our third argument), create + * a new path that includes our given vertex and edge. This action is append-only, so we do not peer inside the existing + * vertex or edge lists. + */ +public class AppendInternalPathDescriptor extends AbstractScalarFunctionDynamicDescriptor { + private static final long serialVersionUID = 1L; + + // We + private MaterializeInternalPathCallbackFactory materializeInternalPathCallbackFactory; + + @Override + public void setImmutableStates(Object... states) { + if (states != null) { + materializeInternalPathCallbackFactory = (MaterializeInternalPathCallbackFactory) states[0]; + } + } + + @Override + public IScalarEvaluatorFactory createEvaluatorFactory(IScalarEvaluatorFactory[] args) { + return new IScalarEvaluatorFactory() { + private static final long serialVersionUID = 1L; + + @Override + public IScalarEvaluator createScalarEvaluator(IEvaluatorContext ctx) throws HyracksDataException { + return new IScalarEvaluator() { + private final ArrayBackedValueStorage resultStorage = new ArrayBackedValueStorage(); + private final DataOutput dataOutput = resultStorage.getDataOutput(); + + private final IScalarEvaluator arg0Eval = args[0].createScalarEvaluator(ctx); + private final IScalarEvaluator arg1Eval = args[1].createScalarEvaluator(ctx); + private final IScalarEvaluator arg2Eval = args[2].createScalarEvaluator(ctx); + private final IPointable arg0Ptr = new VoidPointable(); + private final IPointable arg1Ptr = new VoidPointable(); + private final IPointable arg2Ptr = new VoidPointable(); + + @Override + public void evaluate(IFrameTupleReference tuple, IPointable result) throws HyracksDataException { + arg0Eval.evaluate(tuple, arg0Ptr); + arg1Eval.evaluate(tuple, arg1Ptr); + arg2Eval.evaluate(tuple, arg2Ptr); + if (PointableHelper.checkAndSetMissingOrNull(result, arg0Ptr, arg1Ptr, arg2Ptr)) { + return; + } + resultStorage.reset(); + + try { + // Build our path header. We start with our type tag. + dataOutput.writeByte(PATH_SERIALIZED_TYPE_TAG); + + // Write the end offset of our new vertex list. + int oldVertexLocalListEnd = IntegerPointable.getInteger(arg2Ptr.getByteArray(), + arg2Ptr.getStartOffset() + HEADER_VERTEX_LIST_END); + int vertexItemSize = arg0Ptr.getLength() + LIST_ITEM_LENGTH_SIZE; + dataOutput.writeInt(oldVertexLocalListEnd + vertexItemSize); + // materializeInternalPathCallbackFactory + + // Write the end offset of our new edge list. + int oldEdgeLocalListEnd = IntegerPointable.getInteger(arg2Ptr.getByteArray(), + arg2Ptr.getStartOffset() + HEADER_EDGE_LIST_END); + int edgeItemSize = arg1Ptr.getLength() + LIST_ITEM_LENGTH_SIZE; + dataOutput.writeInt(oldEdgeLocalListEnd + edgeItemSize); + + // Copy all of our old vertices. + int oldVertexAbsoluteListStart = arg2Ptr.getStartOffset() + PATH_HEADER_LENGTH; + dataOutput.write(arg2Ptr.getByteArray(), oldVertexAbsoluteListStart, + (oldVertexLocalListEnd + arg2Ptr.getStartOffset()) - oldVertexAbsoluteListStart); + + // Copy our new vertex. + dataOutput.writeInt(arg0Ptr.getLength()); + dataOutput.write(arg0Ptr.getByteArray(), arg0Ptr.getStartOffset(), arg0Ptr.getLength()); + + // Copy all of our new edges. + int oldEdgeAbsoluteListStart = oldVertexLocalListEnd + arg2Ptr.getStartOffset(); + dataOutput.write(arg2Ptr.getByteArray(), oldEdgeAbsoluteListStart, + (oldEdgeLocalListEnd + arg2Ptr.getStartOffset()) - oldEdgeAbsoluteListStart); + + // Copy our new edge. + dataOutput.writeInt(arg1Ptr.getLength()); + dataOutput.write(arg1Ptr.getByteArray(), arg1Ptr.getStartOffset(), arg1Ptr.getLength()); + + } catch (IOException e) { + throw HyracksDataException.create(e); + } + } + }; + } + }; + } + + @Override + public FunctionIdentifier getIdentifier() { + return GraphixFunctionIdentifiers.APPEND_INTERNAL_PATH; + } + + public static final class MaterializeInternalPathCallbackFactory { + // private Consumer<VoidPointable> + } +}
diff --git a/asterix-graphix/src/main/java/org/apache/asterix/graphix/runtime/evaluator/CreateInternalPathDescriptor.java b/asterix-graphix/src/main/java/org/apache/asterix/graphix/runtime/evaluator/CreateInternalPathDescriptor.java new file mode 100644 index 0000000..08f9c78 --- /dev/null +++ b/asterix-graphix/src/main/java/org/apache/asterix/graphix/runtime/evaluator/CreateInternalPathDescriptor.java
@@ -0,0 +1,93 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.asterix.graphix.runtime.evaluator; + +import java.io.DataOutput; +import java.io.IOException; + +import org.apache.asterix.graphix.function.GraphixFunctionIdentifiers; +import org.apache.asterix.graphix.runtime.pointable.InternalPathPointable; +import org.apache.asterix.graphix.runtime.pointable.SinglyLinkedListPointable; +import org.apache.asterix.runtime.evaluators.base.AbstractScalarFunctionDynamicDescriptor; +import org.apache.asterix.runtime.evaluators.functions.PointableHelper; +import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier; +import org.apache.hyracks.algebricks.runtime.base.IEvaluatorContext; +import org.apache.hyracks.algebricks.runtime.base.IScalarEvaluator; +import org.apache.hyracks.algebricks.runtime.base.IScalarEvaluatorFactory; +import org.apache.hyracks.api.exceptions.HyracksDataException; +import org.apache.hyracks.data.std.api.IPointable; +import org.apache.hyracks.data.std.primitive.VoidPointable; +import org.apache.hyracks.data.std.util.ArrayBackedValueStorage; +import org.apache.hyracks.dataflow.common.data.accessors.IFrameTupleReference; + +/** + * Create an initial path, which will consist of a single vertex (our argument) and zero edges. + */ +public class CreateInternalPathDescriptor extends AbstractScalarFunctionDynamicDescriptor { + private static final long serialVersionUID = 1L; + + @Override + public IScalarEvaluatorFactory createEvaluatorFactory(IScalarEvaluatorFactory[] args) { + return new IScalarEvaluatorFactory() { + private static final long serialVersionUID = 1L; + + @Override + public IScalarEvaluator createScalarEvaluator(IEvaluatorContext ctx) throws HyracksDataException { + return new IScalarEvaluator() { + private final ArrayBackedValueStorage resultStorage = new ArrayBackedValueStorage(); + private final DataOutput dataOutput = resultStorage.getDataOutput(); + private final IScalarEvaluator arg0Eval = args[0].createScalarEvaluator(ctx); + private final IPointable arg0Ptr = new VoidPointable(); + + @Override + public void evaluate(IFrameTupleReference tuple, IPointable result) throws HyracksDataException { + arg0Eval.evaluate(tuple, arg0Ptr); + if (PointableHelper.checkAndSetMissingOrNull(result, arg0Ptr)) { + return; + } + resultStorage.reset(); + + try { + // Build our path header. We start with our type tag. + dataOutput.writeByte(InternalPathPointable.PATH_SERIALIZED_TYPE_TAG); + + // Write the size of our vertex as a list item twice (to indicate we have no edges). + int vertexItemSize = arg0Ptr.getLength() + SinglyLinkedListPointable.LIST_ITEM_LENGTH_SIZE; + int startOfEdgeList = vertexItemSize + InternalPathPointable.PATH_HEADER_LENGTH; + dataOutput.writeInt(startOfEdgeList); + dataOutput.writeInt(startOfEdgeList); + + // Write our vertex item (size + the item itself). + dataOutput.writeInt(vertexItemSize); + dataOutput.write(arg0Ptr.getByteArray(), arg0Ptr.getStartOffset(), arg0Ptr.getLength()); + + } catch (IOException e) { + throw HyracksDataException.create(e); + } + } + }; + } + }; + } + + @Override + public FunctionIdentifier getIdentifier() { + return GraphixFunctionIdentifiers.CREATE_INTERNAL_PATH; + } +}
diff --git a/asterix-graphix/src/main/java/org/apache/asterix/graphix/runtime/evaluator/IsDistinctEdgeDescriptor.java b/asterix-graphix/src/main/java/org/apache/asterix/graphix/runtime/evaluator/IsDistinctEdgeDescriptor.java new file mode 100644 index 0000000..9c03938 --- /dev/null +++ b/asterix-graphix/src/main/java/org/apache/asterix/graphix/runtime/evaluator/IsDistinctEdgeDescriptor.java
@@ -0,0 +1,99 @@ +/* + * 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.graphix.runtime.evaluator; + +import org.apache.asterix.graphix.function.GraphixFunctionIdentifiers; +import org.apache.asterix.graphix.runtime.evaluator.common.AbstractElementCompareEvaluator; +import org.apache.asterix.graphix.runtime.pointable.InternalPathPointable; +import org.apache.asterix.graphix.runtime.pointable.SinglyLinkedListPointable; +import org.apache.asterix.runtime.evaluators.base.AbstractScalarFunctionDynamicDescriptor; +import org.apache.asterix.runtime.evaluators.functions.PointableHelper; +import org.apache.asterix.runtime.exceptions.TypeMismatchException; +import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier; +import org.apache.hyracks.algebricks.runtime.base.IEvaluatorContext; +import org.apache.hyracks.algebricks.runtime.base.IScalarEvaluator; +import org.apache.hyracks.algebricks.runtime.base.IScalarEvaluatorFactory; +import org.apache.hyracks.api.exceptions.HyracksDataException; +import org.apache.hyracks.data.std.api.IPointable; +import org.apache.hyracks.data.std.primitive.VoidPointable; +import org.apache.hyracks.dataflow.common.data.accessors.IFrameTupleReference; + +/** + * For a given tuple (edge, path) pair, return true if there are no duplicate edges in the given path (and false + * otherwise). This function is used internally to enforce no-repeated-edge navigation semantics. + */ +public class IsDistinctEdgeDescriptor extends AbstractScalarFunctionDynamicDescriptor { + private static final long serialVersionUID = 1L; + + @Override + public IScalarEvaluatorFactory createEvaluatorFactory(IScalarEvaluatorFactory[] args) { + return new IScalarEvaluatorFactory() { + private static final long serialVersionUID = 1L; + + @Override + public IScalarEvaluator createScalarEvaluator(IEvaluatorContext ctx) throws HyracksDataException { + return new AbstractElementCompareEvaluator() { + private final IScalarEvaluator arg0Eval = args[0].createScalarEvaluator(ctx); + private final IScalarEvaluator arg1Eval = args[1].createScalarEvaluator(ctx); + private final IPointable arg0Ptr = new VoidPointable(); + private final IPointable arg1Ptr = new VoidPointable(); + + @Override + protected boolean readTuple(IFrameTupleReference tuple, IPointable result) + throws HyracksDataException { + arg0Eval.evaluate(tuple, arg0Ptr); + arg1Eval.evaluate(tuple, arg1Ptr); + + // If our edge or path is NULL or MISSING, then our result is NULL / MISSING. + if (PointableHelper.checkAndSetMissingOrNull(result, arg0Ptr, arg1Ptr)) { + return false; + } + + // Ensure that we have a path (i.e. bit-array) as our second argument. + byte typeTagByte = arg1Ptr.getByteArray()[arg1Ptr.getStartOffset()]; + if (typeTagByte != InternalPathPointable.PATH_SERIALIZED_TYPE_TAG) { + throw new TypeMismatchException(sourceLoc, getIdentifier(), 1, typeTagByte, + InternalPathPointable.PATH_SERIALIZED_TYPE_TAG); + } + + edgeListItemCallback.getInputItemPtr().set(arg0Ptr); + pathPtr.set(arg1Ptr); + return true; + } + + @Override + protected boolean compare() { + SinglyLinkedListPointable<Boolean> edgeListPointable = pathPtr.getEdgeListPointable(); + while (edgeListPointable.hasNext()) { + if (edgeListPointable.next()) { + return true; + } + } + return false; + } + }; + } + }; + } + + @Override + public FunctionIdentifier getIdentifier() { + return GraphixFunctionIdentifiers.IS_DISTINCT_EDGE; + } +}
diff --git a/asterix-graphix/src/main/java/org/apache/asterix/graphix/runtime/evaluator/IsDistinctEverythingDescriptor.java b/asterix-graphix/src/main/java/org/apache/asterix/graphix/runtime/evaluator/IsDistinctEverythingDescriptor.java new file mode 100644 index 0000000..b3dda6e --- /dev/null +++ b/asterix-graphix/src/main/java/org/apache/asterix/graphix/runtime/evaluator/IsDistinctEverythingDescriptor.java
@@ -0,0 +1,109 @@ +/* + * 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.graphix.runtime.evaluator; + +import org.apache.asterix.graphix.function.GraphixFunctionIdentifiers; +import org.apache.asterix.graphix.runtime.evaluator.common.AbstractElementCompareEvaluator; +import org.apache.asterix.graphix.runtime.pointable.InternalPathPointable; +import org.apache.asterix.graphix.runtime.pointable.SinglyLinkedListPointable; +import org.apache.asterix.runtime.evaluators.base.AbstractScalarFunctionDynamicDescriptor; +import org.apache.asterix.runtime.evaluators.functions.PointableHelper; +import org.apache.asterix.runtime.exceptions.TypeMismatchException; +import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier; +import org.apache.hyracks.algebricks.runtime.base.IEvaluatorContext; +import org.apache.hyracks.algebricks.runtime.base.IScalarEvaluator; +import org.apache.hyracks.algebricks.runtime.base.IScalarEvaluatorFactory; +import org.apache.hyracks.api.exceptions.HyracksDataException; +import org.apache.hyracks.data.std.api.IPointable; +import org.apache.hyracks.data.std.primitive.VoidPointable; +import org.apache.hyracks.dataflow.common.data.accessors.IFrameTupleReference; + +/** + * For a given tuple (vertex, edge, path) triple, return true if there are no duplicate vertices or edges in the given + * path (and false otherwise). This function is used internally to enforce no-repeated-everything navigation semantics. + */ +public class IsDistinctEverythingDescriptor extends AbstractScalarFunctionDynamicDescriptor { + private static final long serialVersionUID = 1L; + + @Override + public IScalarEvaluatorFactory createEvaluatorFactory(IScalarEvaluatorFactory[] args) { + return new IScalarEvaluatorFactory() { + private static final long serialVersionUID = 1L; + + @Override + public IScalarEvaluator createScalarEvaluator(IEvaluatorContext ctx) throws HyracksDataException { + return new AbstractElementCompareEvaluator() { + private final IScalarEvaluator arg0Eval = args[0].createScalarEvaluator(ctx); + private final IScalarEvaluator arg1Eval = args[1].createScalarEvaluator(ctx); + private final IScalarEvaluator arg2Eval = args[2].createScalarEvaluator(ctx); + private final IPointable arg0Ptr = new VoidPointable(); + private final IPointable arg1Ptr = new VoidPointable(); + private final IPointable arg2Ptr = new VoidPointable(); + + @Override + protected boolean readTuple(IFrameTupleReference tuple, IPointable result) + throws HyracksDataException { + arg0Eval.evaluate(tuple, arg0Ptr); + arg1Eval.evaluate(tuple, arg1Ptr); + arg2Eval.evaluate(tuple, arg2Ptr); + + // If our edge, vertex, or path is NULL or MISSING, then our result is NULL / MISSING. + if (PointableHelper.checkAndSetMissingOrNull(result, arg0Ptr, arg1Ptr, arg2Ptr)) { + return false; + } + + // Ensure that we have a path (i.e. bit-array) as our third argument. + byte typeTagByte = arg2Ptr.getByteArray()[arg2Ptr.getStartOffset()]; + if (typeTagByte != InternalPathPointable.PATH_SERIALIZED_TYPE_TAG) { + throw new TypeMismatchException(sourceLoc, getIdentifier(), 2, typeTagByte, + InternalPathPointable.PATH_SERIALIZED_TYPE_TAG); + } + + vertexListItemCallback.getInputItemPtr().set(arg0Ptr); + edgeListItemCallback.getInputItemPtr().set(arg1Ptr); + pathPtr.set(arg2Ptr); + return true; + } + + @Override + protected boolean compare() { + SinglyLinkedListPointable<Boolean> vertexListPointable = pathPtr.getVertexListPointable(); + SinglyLinkedListPointable<Boolean> edgeListPointable = pathPtr.getEdgeListPointable(); + while (vertexListPointable.hasNext()) { + if (vertexListPointable.next()) { + return true; + } + } + while (edgeListPointable.hasNext()) { + if (edgeListPointable.next()) { + return true; + } + } + return false; + } + }; + } + }; + } + + @Override + public FunctionIdentifier getIdentifier() { + return GraphixFunctionIdentifiers.IS_DISTINCT_EVERYTHING; + } +}
diff --git a/asterix-graphix/src/main/java/org/apache/asterix/graphix/runtime/evaluator/IsDistinctVertexDescriptor.java b/asterix-graphix/src/main/java/org/apache/asterix/graphix/runtime/evaluator/IsDistinctVertexDescriptor.java new file mode 100644 index 0000000..160837e --- /dev/null +++ b/asterix-graphix/src/main/java/org/apache/asterix/graphix/runtime/evaluator/IsDistinctVertexDescriptor.java
@@ -0,0 +1,99 @@ +/* + * 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.graphix.runtime.evaluator; + +import org.apache.asterix.graphix.function.GraphixFunctionIdentifiers; +import org.apache.asterix.graphix.runtime.evaluator.common.AbstractElementCompareEvaluator; +import org.apache.asterix.graphix.runtime.pointable.InternalPathPointable; +import org.apache.asterix.graphix.runtime.pointable.SinglyLinkedListPointable; +import org.apache.asterix.runtime.evaluators.base.AbstractScalarFunctionDynamicDescriptor; +import org.apache.asterix.runtime.evaluators.functions.PointableHelper; +import org.apache.asterix.runtime.exceptions.TypeMismatchException; +import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier; +import org.apache.hyracks.algebricks.runtime.base.IEvaluatorContext; +import org.apache.hyracks.algebricks.runtime.base.IScalarEvaluator; +import org.apache.hyracks.algebricks.runtime.base.IScalarEvaluatorFactory; +import org.apache.hyracks.api.exceptions.HyracksDataException; +import org.apache.hyracks.data.std.api.IPointable; +import org.apache.hyracks.data.std.primitive.VoidPointable; +import org.apache.hyracks.dataflow.common.data.accessors.IFrameTupleReference; + +/** + * For a given tuple (vertex, path) pair, return true if there are no duplicate vertices in the given path (and false + * otherwise). This function is used internally to enforce no-repeated-vertex navigation semantics. + */ +public class IsDistinctVertexDescriptor extends AbstractScalarFunctionDynamicDescriptor { + private static final long serialVersionUID = 1L; + + @Override + public IScalarEvaluatorFactory createEvaluatorFactory(IScalarEvaluatorFactory[] args) { + return new IScalarEvaluatorFactory() { + private static final long serialVersionUID = 1L; + + @Override + public IScalarEvaluator createScalarEvaluator(IEvaluatorContext ctx) throws HyracksDataException { + return new AbstractElementCompareEvaluator() { + private final IScalarEvaluator arg0Eval = args[0].createScalarEvaluator(ctx); + private final IScalarEvaluator arg1Eval = args[1].createScalarEvaluator(ctx); + private final IPointable arg0Ptr = new VoidPointable(); + private final IPointable arg1Ptr = new VoidPointable(); + + @Override + protected boolean readTuple(IFrameTupleReference tuple, IPointable result) + throws HyracksDataException { + arg0Eval.evaluate(tuple, arg0Ptr); + arg1Eval.evaluate(tuple, arg1Ptr); + + // If our vertex or path is NULL or MISSING, then our result is NULL / MISSING. + if (PointableHelper.checkAndSetMissingOrNull(result, arg0Ptr, arg1Ptr)) { + return false; + } + + // Ensure that we have a path (i.e. bit-array) as our second argument. + byte typeTagByte = arg1Ptr.getByteArray()[arg1Ptr.getStartOffset()]; + if (typeTagByte != InternalPathPointable.PATH_SERIALIZED_TYPE_TAG) { + throw new TypeMismatchException(sourceLoc, getIdentifier(), 1, typeTagByte, + InternalPathPointable.PATH_SERIALIZED_TYPE_TAG); + } + + vertexListItemCallback.getInputItemPtr().set(arg0Ptr); + pathPtr.set(arg1Ptr); + return true; + } + + @Override + protected boolean compare() { + SinglyLinkedListPointable<Boolean> vertexListPointable = pathPtr.getVertexListPointable(); + while (vertexListPointable.hasNext()) { + if (vertexListPointable.next()) { + return true; + } + } + return false; + } + }; + } + }; + } + + @Override + public FunctionIdentifier getIdentifier() { + return GraphixFunctionIdentifiers.IS_DISTINCT_VERTEX; + } +}
diff --git a/asterix-graphix/src/main/java/org/apache/asterix/graphix/runtime/evaluator/common/AbstractElementCompareEvaluator.java b/asterix-graphix/src/main/java/org/apache/asterix/graphix/runtime/evaluator/common/AbstractElementCompareEvaluator.java new file mode 100644 index 0000000..7185b16 --- /dev/null +++ b/asterix-graphix/src/main/java/org/apache/asterix/graphix/runtime/evaluator/common/AbstractElementCompareEvaluator.java
@@ -0,0 +1,85 @@ +/* + * 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.graphix.runtime.evaluator.common; + +import java.io.DataOutput; +import java.util.function.Function; + +import org.apache.asterix.dataflow.data.nontagged.serde.AObjectSerializerDeserializer; +import org.apache.asterix.graphix.runtime.pointable.InternalPathPointable; +import org.apache.asterix.om.base.ABoolean; +import org.apache.hyracks.algebricks.runtime.base.IScalarEvaluator; +import org.apache.hyracks.api.exceptions.HyracksDataException; +import org.apache.hyracks.data.std.api.IPointable; +import org.apache.hyracks.data.std.primitive.VoidPointable; +import org.apache.hyracks.data.std.util.ArrayBackedValueStorage; +import org.apache.hyracks.data.std.util.DataUtils; +import org.apache.hyracks.dataflow.common.data.accessors.IFrameTupleReference; + +public abstract class AbstractElementCompareEvaluator implements IScalarEvaluator { + protected final AObjectSerializerDeserializer serde = AObjectSerializerDeserializer.INSTANCE; + protected final ArrayBackedValueStorage resultStorage = new ArrayBackedValueStorage(); + protected final DataOutput dataOutput = resultStorage.getDataOutput(); + + protected final ListItemCompareCallback vertexListItemCallback = new ListItemCompareCallback(); + protected final ListItemCompareCallback edgeListItemCallback = new ListItemCompareCallback(); + protected final InternalPathPointable<Boolean> pathPtr = + new InternalPathPointable<>(vertexListItemCallback, edgeListItemCallback); + + @Override + public void evaluate(IFrameTupleReference tuple, IPointable result) throws HyracksDataException { + if (!readTuple(tuple, result)) { + return; + } + + if (compare()) { + // We have found a duplicate item. Exit and return false. + resultStorage.reset(); + serde.serialize(ABoolean.FALSE, dataOutput); + result.set(resultStorage); + } + + // No duplicate items have been found. Return true. + resultStorage.reset(); + serde.serialize(ABoolean.TRUE, dataOutput); + result.set(resultStorage); + } + + /** + * @return True if we should proceed (and result has been set). False otherwise. + */ + protected abstract boolean readTuple(IFrameTupleReference tuple, IPointable result) throws HyracksDataException; + + protected abstract boolean compare() throws HyracksDataException; + + // We treat vertices / edges as black-boxes, we do not know their contents. We compare blindly. + public static final class ListItemCompareCallback implements Function<VoidPointable, Boolean> { + private final VoidPointable inputItemPtr = new VoidPointable(); + + public VoidPointable getInputItemPtr() { + return inputItemPtr; + } + + @Override + public Boolean apply(VoidPointable listItemPtr) { + return DataUtils.equals(inputItemPtr, listItemPtr); + } + } + +}
diff --git a/asterix-graphix/src/main/java/org/apache/asterix/graphix/runtime/function/GraphixFunctionInfoCollection.java b/asterix-graphix/src/main/java/org/apache/asterix/graphix/runtime/function/GraphixFunctionInfoCollection.java new file mode 100644 index 0000000..acdaf26 --- /dev/null +++ b/asterix-graphix/src/main/java/org/apache/asterix/graphix/runtime/function/GraphixFunctionInfoCollection.java
@@ -0,0 +1,79 @@ +/* + * 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.graphix.runtime.function; + +import static org.apache.asterix.graphix.function.GraphixFunctionIdentifiers.APPEND_INTERNAL_PATH; +import static org.apache.asterix.graphix.function.GraphixFunctionIdentifiers.CREATE_INTERNAL_PATH; +import static org.apache.asterix.graphix.function.GraphixFunctionIdentifiers.IS_DISTINCT_EDGE; +import static org.apache.asterix.graphix.function.GraphixFunctionIdentifiers.IS_DISTINCT_EVERYTHING; +import static org.apache.asterix.graphix.function.GraphixFunctionIdentifiers.IS_DISTINCT_VERTEX; +import static org.apache.asterix.graphix.function.GraphixFunctionIdentifiers.MATERIALIZE_PATH; + +import java.util.HashMap; +import java.util.Map; + +import org.apache.asterix.graphix.type.MaterializePathTypeComputer; +import org.apache.asterix.om.functions.FunctionInfo; +import org.apache.asterix.om.typecomputer.base.AbstractResultTypeComputer; +import org.apache.asterix.om.typecomputer.base.IResultTypeComputer; +import org.apache.asterix.om.typecomputer.impl.ABooleanTypeComputer; +import org.apache.asterix.om.types.BuiltinType; +import org.apache.asterix.om.types.IAType; +import org.apache.hyracks.algebricks.core.algebra.base.ILogicalExpression; +import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier; +import org.apache.hyracks.algebricks.core.algebra.functions.IFunctionInfo; + +public class GraphixFunctionInfoCollection { + private static final Map<FunctionIdentifier, IFunctionInfo> functionInfoMap = new HashMap<>(); + + static { + // The following functions yield boolean values. + final IResultTypeComputer booleanComputer = ABooleanTypeComputer.INSTANCE_NULLABLE; + functionInfoMap.put(IS_DISTINCT_VERTEX, new GraphixFunctionInfo(IS_DISTINCT_VERTEX, booleanComputer)); + functionInfoMap.put(IS_DISTINCT_EDGE, new GraphixFunctionInfo(IS_DISTINCT_EDGE, booleanComputer)); + functionInfoMap.put(IS_DISTINCT_EVERYTHING, new GraphixFunctionInfo(IS_DISTINCT_EVERYTHING, booleanComputer)); + + // The following functions yield raw path values (we hijack the bitarray type here). + final IResultTypeComputer rawPathComputer = new AbstractResultTypeComputer() { + @Override + protected IAType getResultType(ILogicalExpression expr, IAType... strippedInputTypes) { + return BuiltinType.ABITARRAY; + } + }; + functionInfoMap.put(CREATE_INTERNAL_PATH, new GraphixFunctionInfo(CREATE_INTERNAL_PATH, rawPathComputer)); + functionInfoMap.put(APPEND_INTERNAL_PATH, new GraphixFunctionInfo(APPEND_INTERNAL_PATH, rawPathComputer)); + + // The following function yields a closed record value. + final IResultTypeComputer pathComputer = MaterializePathTypeComputer.INSTANCE; + functionInfoMap.put(MATERIALIZE_PATH, new GraphixFunctionInfo(MATERIALIZE_PATH, pathComputer)); + } + + public static IFunctionInfo getFunctionInfo(FunctionIdentifier functionIdentifier) { + return functionInfoMap.get(functionIdentifier); + } + + public static class GraphixFunctionInfo extends FunctionInfo { + private static final long serialVersionUID = 1L; + + public GraphixFunctionInfo(FunctionIdentifier functionIdentifier, IResultTypeComputer typeComputer) { + // We only have functional functions. + super(functionIdentifier, typeComputer, true); + } + } +}
diff --git a/asterix-graphix/src/main/java/org/apache/asterix/graphix/runtime/function/GraphixFunctionRegistrant.java b/asterix-graphix/src/main/java/org/apache/asterix/graphix/runtime/function/GraphixFunctionRegistrant.java new file mode 100644 index 0000000..983165b --- /dev/null +++ b/asterix-graphix/src/main/java/org/apache/asterix/graphix/runtime/function/GraphixFunctionRegistrant.java
@@ -0,0 +1,59 @@ +/* + * 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.graphix.runtime.function; + +import org.apache.asterix.graphix.runtime.evaluator.AppendInternalPathDescriptor; +import org.apache.asterix.graphix.runtime.evaluator.CreateInternalPathDescriptor; +import org.apache.asterix.graphix.runtime.evaluator.IsDistinctEdgeDescriptor; +import org.apache.asterix.graphix.runtime.evaluator.IsDistinctEverythingDescriptor; +import org.apache.asterix.graphix.runtime.evaluator.IsDistinctVertexDescriptor; +import org.apache.asterix.om.functions.IFunctionCollection; +import org.apache.asterix.om.functions.IFunctionDescriptor; +import org.apache.asterix.om.functions.IFunctionDescriptorFactory; +import org.apache.asterix.om.functions.IFunctionRegistrant; +import org.apache.asterix.om.functions.IFunctionTypeInferer; +import org.apache.hyracks.algebricks.core.algebra.expressions.AbstractFunctionCallExpression; + +public class GraphixFunctionRegistrant implements IFunctionRegistrant { + private static final long serialVersionUID = 1L; + + @Override + public void register(IFunctionCollection fc) { + fc.add(IsDistinctVertexDescriptor::new); + fc.add(IsDistinctEdgeDescriptor::new); + fc.add(IsDistinctEverythingDescriptor::new); + fc.add(CreateInternalPathDescriptor::new); + + // We have a callback-factory we need to pass to our APPEND-INTERNAL-PATH evaluator-factory. + fc.add(new IFunctionDescriptorFactory() { + @Override + public IFunctionTypeInferer createFunctionTypeInferer() { + return (expr, fd, context, compilerProps) -> { + AbstractFunctionCallExpression funcCallExpr = (AbstractFunctionCallExpression) expr; + fd.setImmutableStates(funcCallExpr.getOpaqueParameters()[0]); + }; + } + + @Override + public IFunctionDescriptor createFunctionDescriptor() { + return new AppendInternalPathDescriptor(); + } + }); + } +}
diff --git a/asterix-graphix/src/main/java/org/apache/asterix/graphix/runtime/pointable/InternalPathPointable.java b/asterix-graphix/src/main/java/org/apache/asterix/graphix/runtime/pointable/InternalPathPointable.java new file mode 100644 index 0000000..2fcf96c --- /dev/null +++ b/asterix-graphix/src/main/java/org/apache/asterix/graphix/runtime/pointable/InternalPathPointable.java
@@ -0,0 +1,80 @@ +/* + * 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.graphix.runtime.pointable; + +import java.util.function.Function; + +import org.apache.asterix.om.types.ATypeTag; +import org.apache.hyracks.data.std.api.AbstractPointable; +import org.apache.hyracks.data.std.primitive.IntegerPointable; +import org.apache.hyracks.data.std.primitive.VoidPointable; + +/** + * A path consists of a header, a list of vertices, and a list of edges. + * <p> + * <ol> + * <li>A path header consists of 9 bytes, and is formatted as such: + * <pre> + * [bit-array type tag] -- 1 byte + * [vertex-list end offset] -- 4 bytes + * [edge-list end offset] -- 4 bytes + * </pre></li> + * <li>A path's list of vertices starts at 8 bytes and ends at [vertex-list end offset] bytes.</li> + * <li>A path's list of edges starts at [vertex-list end offset] bytes and ends at [edge-list end offset] bytes. All + * offsets are given from the start of the path itself (not absolute, from the containing byte array).</li> + * </ol> + */ +public class InternalPathPointable<T> extends AbstractPointable { + public static final int HEADER_VERTEX_LIST_END = 1; + public static final int HEADER_EDGE_LIST_END = 5; + public static final int PATH_HEADER_LENGTH = 9; + + // TODO (GLENN): Create a custom type tag specifically for extensions. + public static final byte PATH_SERIALIZED_TYPE_TAG = ATypeTag.BITARRAY.serialize(); + + // We will set the following on invocation of our "set". + private final SinglyLinkedListPointable<T> edgeListPointable; + private final SinglyLinkedListPointable<T> vertexListPointable; + + public InternalPathPointable(Function<VoidPointable, T> vertexListItemCallback, + Function<VoidPointable, T> edgeListItemCallback) { + this.vertexListPointable = new SinglyLinkedListPointable<>(vertexListItemCallback); + this.edgeListPointable = new SinglyLinkedListPointable<>(edgeListItemCallback); + } + + @Override + public void set(byte[] bytes, int start, int length) { + int absoluteStartOfVertices = start + PATH_HEADER_LENGTH; + int localEndOfVertices = IntegerPointable.getInteger(bytes, start + HEADER_VERTEX_LIST_END); + int localEndOfEdges = IntegerPointable.getInteger(bytes, start + HEADER_EDGE_LIST_END); + int absoluteEndOfVertices = start + localEndOfVertices; + int absoluteEndOfEdges = start + localEndOfEdges; + vertexListPointable.set(bytes, absoluteStartOfVertices, absoluteEndOfVertices - absoluteStartOfVertices); + edgeListPointable.set(bytes, absoluteEndOfVertices, absoluteEndOfEdges - absoluteEndOfVertices); + super.set(bytes, start, length); + } + + public SinglyLinkedListPointable<T> getEdgeListPointable() { + return edgeListPointable; + } + + public SinglyLinkedListPointable<T> getVertexListPointable() { + return vertexListPointable; + } +}
diff --git a/asterix-graphix/src/main/java/org/apache/asterix/graphix/runtime/pointable/SinglyLinkedListPointable.java b/asterix-graphix/src/main/java/org/apache/asterix/graphix/runtime/pointable/SinglyLinkedListPointable.java new file mode 100644 index 0000000..6ceb0ae --- /dev/null +++ b/asterix-graphix/src/main/java/org/apache/asterix/graphix/runtime/pointable/SinglyLinkedListPointable.java
@@ -0,0 +1,73 @@ +/* + * 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.graphix.runtime.pointable; + +import java.util.NoSuchElementException; +import java.util.function.Function; + +import org.apache.hyracks.data.std.api.AbstractPointable; +import org.apache.hyracks.data.std.primitive.IntegerPointable; +import org.apache.hyracks.data.std.primitive.VoidPointable; + +/** + * A lean representation of a singly-linked list, where each item in the list only has a 4-byte length. We can only + * access this SLL from start to finish, as we do not know the size of each item (nor the number of items) apriori. + */ +public class SinglyLinkedListPointable<T> extends AbstractPointable { + public static final int LIST_ITEM_LENGTH_SIZE = 4; + + private final Function<VoidPointable, T> listItemConsumer; + private final VoidPointable listItemPointable; + + // This is a **stateful** pointable, whose action is dictated by the given list-item callback. + private int currentPosition; + + public SinglyLinkedListPointable(Function<VoidPointable, T> listItemConsumer) { + this.listItemPointable = new VoidPointable(); + this.listItemConsumer = listItemConsumer; + } + + @Override + public void set(byte[] bytes, int start, int length) { + super.set(bytes, start, length); + currentPosition = 0; + } + + public boolean hasNext() { + return currentPosition < length; + } + + public T next() { + if (!hasNext()) { + throw new NoSuchElementException(); + } + + // Determine the length of our working item. + int itemLength = IntegerPointable.getInteger(bytes, start + currentPosition); + currentPosition = currentPosition + LIST_ITEM_LENGTH_SIZE; + + // Consume our list item. + listItemPointable.set(bytes, currentPosition, itemLength); + T result = listItemConsumer.apply(listItemPointable); + + // Advance our cursor. + currentPosition = currentPosition + itemLength; + return result; + } +}
diff --git a/asterix-graphix/src/main/java/org/apache/asterix/graphix/type/MaterializePathTypeComputer.java b/asterix-graphix/src/main/java/org/apache/asterix/graphix/type/MaterializePathTypeComputer.java new file mode 100644 index 0000000..52e3be5 --- /dev/null +++ b/asterix-graphix/src/main/java/org/apache/asterix/graphix/type/MaterializePathTypeComputer.java
@@ -0,0 +1,47 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.asterix.graphix.type; + +import org.apache.asterix.om.typecomputer.base.AbstractResultTypeComputer; +import org.apache.asterix.om.typecomputer.base.IResultTypeComputer; +import org.apache.asterix.om.types.AOrderedListType; +import org.apache.asterix.om.types.ARecordType; +import org.apache.asterix.om.types.IAType; +import org.apache.asterix.om.utils.RecordUtil; +import org.apache.hyracks.algebricks.core.algebra.base.ILogicalExpression; + +public class MaterializePathTypeComputer extends AbstractResultTypeComputer { + public static final String VERTICES_FIELD_NAME = "Vertices"; + public static final String EDGES_FIELD_NAME = "Edges"; + public static final ARecordType TYPE; + static { + String typeName = "materializedPathType"; + String[] fieldNames = new String[] { VERTICES_FIELD_NAME, EDGES_FIELD_NAME }; + AOrderedListType listType = new AOrderedListType(RecordUtil.FULLY_OPEN_RECORD_TYPE, null); + TYPE = new ARecordType(typeName, fieldNames, new IAType[] { listType, listType }, false); + } + + // Our type computer will always return the type above (while respecting NULL/MISSING IN -> OUT semantics). + public static final IResultTypeComputer INSTANCE = new MaterializePathTypeComputer(); + + @Override + protected IAType getResultType(ILogicalExpression expr, IAType... strippedInputTypes) { + return TYPE; + } +}
diff --git a/asterix-graphix/src/main/resources/META-INF/services/org.apache.asterix.om.functions.IFunctionRegistrant b/asterix-graphix/src/main/resources/META-INF/services/org.apache.asterix.om.functions.IFunctionRegistrant new file mode 100644 index 0000000..5ee5a83 --- /dev/null +++ b/asterix-graphix/src/main/resources/META-INF/services/org.apache.asterix.om.functions.IFunctionRegistrant
@@ -0,0 +1,20 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +org.apache.asterix.graphix.runtime.function.GraphixFunctionRegistrant \ No newline at end of file
diff --git a/asterix-graphix/src/main/resources/cc.conf b/asterix-graphix/src/main/resources/cc.conf index ae068ca..968c07c 100644 --- a/asterix-graphix/src/main/resources/cc.conf +++ b/asterix-graphix/src/main/resources/cc.conf
@@ -32,7 +32,13 @@ log.level=INFO [extension/org.apache.asterix.graphix.extension.GraphixQueryTranslatorExtension] -enabled=true +graphix.evaluation.default=expand-and-union +graphix.semantics.pattern=isomorphism +graphix.semantics.navigation=no-repeat-anything +graphix.schema-decorate.vertex=as-needed +graphix.schema-decorate.edge=as-needed + +; We use dummy keys for the extension sections below. [extension/org.apache.asterix.graphix.extension.GraphixLangExtension] enabled=true [extension/org.apache.asterix.graphix.extension.GraphixMetadataExtension]
diff --git a/asterix-graphix/src/main/resources/lang-extension/lang.txt b/asterix-graphix/src/main/resources/lang-extension/lang.txt index 99952bf..ca7aa66 100644 --- a/asterix-graphix/src/main/resources/lang-extension/lang.txt +++ b/asterix-graphix/src/main/resources/lang-extension/lang.txt
@@ -18,17 +18,19 @@ // import java.util.HashSet; +import java.util.Objects; import java.util.Set; -import org.apache.asterix.graphix.common.metadata.GraphElementIdentifier; +import org.apache.asterix.graphix.common.metadata.IElementIdentifier; +import org.apache.asterix.graphix.lang.annotation.ElementEvaluationAnnotation; import org.apache.asterix.graphix.lang.clause.FromGraphClause; -import org.apache.asterix.graphix.lang.clause.GraphSelectBlock; import org.apache.asterix.graphix.lang.clause.MatchClause; import org.apache.asterix.graphix.lang.expression.EdgePatternExpr; import org.apache.asterix.graphix.lang.expression.GraphConstructor; import org.apache.asterix.graphix.lang.expression.PathPatternExpr; import org.apache.asterix.graphix.lang.expression.VertexPatternExpr; import org.apache.asterix.graphix.lang.optype.MatchType; +import org.apache.asterix.graphix.lang.parser.GraphixParserHint; import org.apache.asterix.graphix.lang.statement.CreateGraphStatement; import org.apache.asterix.graphix.lang.statement.DeclareGraphStatement; import org.apache.asterix.graphix.lang.statement.GraphDropStatement; @@ -40,7 +42,7 @@ import org.apache.asterix.lang.sqlpp.parser.Token; @new_at_the_class_def -public GraphElementDeclaration parseGraphElementBody(GraphElementIdentifier identifier) throws CompilationException { +public GraphElementDeclaration parseGraphElementBody(IElementIdentifier identifier) throws CompilationException { return parseImpl(new ParseFunction<GraphElementDeclaration>() { @Override public GraphElementDeclaration parse() throws ParseException { @@ -58,6 +60,35 @@ }); } +@new_at_the_class_def +public GraphixParserHint fetchGraphixHint(Token token, GraphixParserHint[] expectedHints) { + if (token == null) { + return null; + } + Token hintToken = token.specialToken; + + // We have a hint. Pull this from our collector. + if (hintToken == null) { + return null; + } + SourceLocation sourceLoc = getSourceLocation(hintToken); + hintCollector.remove(sourceLoc); + + // Check for Graphix hints. + if (hintToken.hint != null) { + // A Graphix hint is not a SQLPP hint, so we shouldn't get anything in the "hint" field. + warnUnexpectedHint(hintToken.hint.getIdentifier(), sourceLoc, StringUtil.join(expectedHints, ", ", "\"")); + + } else if (hintToken.hintParams != null) { + for (GraphixParserHint graphixHint : expectedHints) { + if (graphixHint.getId().equals(hintToken.hintParams)) { + return graphixHint; + } + } + } + return null; +} + @override SelectBlock SelectBlock() throws ParseException: { @@ -146,8 +177,8 @@ return selectBlock; } else { - GraphSelectBlock selectBlock = new GraphSelectBlock(selectClause, fromGraphClause, - fromLetWhereClauses, groupbyClause, gbyLetHavingClauses); + SelectBlock selectBlock = new SelectBlock(selectClause, fromGraphClause, fromLetWhereClauses, + groupbyClause, gbyLetHavingClauses); selectBlock.setSourceLocation(startSrcLoc); return selectBlock; } @@ -389,11 +420,11 @@ Token beginPos = null, endPos = null; int positionOffset = 0; Expression vertexDefinitionExpr; - ElementLabel vertexLabel; + ElementLabel elementLabel; } { <VERTEX> - vertexLabel = GraphVertexDefinitionPattern() + elementLabel = GraphVertexDefinitionPattern() <PRIMARY> <KEY> <LEFTPAREN> primaryKeyFields = KeyFields() <RIGHTPAREN> <AS> { @@ -408,7 +439,7 @@ String vDef = extractFragment(beginPos.beginLine, beginPos.beginColumn + positionOffset, endPos.endLine, endPos.endColumn + 1); removeCurrentScope(); - GraphConstructor.VertexConstructor vertexConstructor = new GraphConstructor.VertexConstructor(vertexLabel, + GraphConstructor.VertexConstructor vertexConstructor = new GraphConstructor.VertexConstructor(elementLabel, primaryKeyFields.second, primaryKeyFields.first, vertexDefinitionExpr, vDef); return addSourceLocation(vertexConstructor, startStmtToken); } @@ -422,7 +453,7 @@ { <LEFTPAREN> <COLON> vertexName = Identifier() <RIGHTPAREN> { - return new ElementLabel(vertexName); + return new ElementLabel(vertexName, false); } } @@ -494,18 +525,18 @@ @new Pair<Triple<ElementLabel, ElementLabel, ElementLabel>, Boolean> GraphEdgeDefinitionPattern() throws ParseException: { - ElementLabel leftVertexLabel, rightVertexLabel; + ElementLabel leftElementLabel, rightElementLabel; boolean isDirectedLeft; String edgeName; } { - leftVertexLabel = GraphVertexDefinitionPattern() + leftElementLabel = GraphVertexDefinitionPattern() ( <MINUS> <LEFTBRACKET> <COLON> edgeName = Identifier() <RIGHTBRACKET> <MINUS> <GT> { isDirectedLeft = false; } | <LT> <MINUS> <LEFTBRACKET> <COLON> edgeName = Identifier() <RIGHTBRACKET> <MINUS> { isDirectedLeft = true; } ) - rightVertexLabel = GraphVertexDefinitionPattern() + rightElementLabel = GraphVertexDefinitionPattern() { Triple<ElementLabel, ElementLabel, ElementLabel> t = new Triple<ElementLabel, ElementLabel, ElementLabel>( - leftVertexLabel, new ElementLabel(edgeName), rightVertexLabel); + leftElementLabel, new ElementLabel(edgeName, false), rightElementLabel); return new Pair<Triple<ElementLabel, ElementLabel, ElementLabel>, Boolean>(t, isDirectedLeft); } } @@ -603,7 +634,7 @@ Token startToken = null, edgeStartToken = null; VertexPatternExpr vertexExpr = null; - EdgeDescriptor edgeDescriptor = null; + Pair<EdgeDescriptor, List<IExpressionAnnotation>> edgeDescriptorPair = null; } { vertexExpr = VertexPatternExpression() @@ -612,11 +643,16 @@ orderedVertexExpressions.add(vertexExpr); } ( - edgeDescriptor = EdgeDescriptor() { edgeStartToken = token; } + edgeDescriptorPair = EdgeDescriptorPair() { edgeStartToken = token; } vertexExpr = VertexPatternExpression() { VertexPatternExpr leftVertex = orderedVertexExpressions.get(orderedVertexExpressions.size() - 1); - EdgePatternExpr edgePattern = new EdgePatternExpr(leftVertex, vertexExpr, edgeDescriptor); + EdgePatternExpr edgePattern = new EdgePatternExpr(leftVertex, vertexExpr, edgeDescriptorPair.first); + if (!edgeDescriptorPair.second.isEmpty()) { + for (IExpressionAnnotation annotation : edgeDescriptorPair.second) { + edgePattern.addHint(annotation); + } + } orderedEdgeExpressions.add(addSourceLocation(edgePattern, edgeStartToken)); orderedVertexExpressions.add(vertexExpr); } @@ -630,33 +666,44 @@ @new VertexPatternExpr VertexPatternExpression() throws ParseException: { - Set<ElementLabel> vertexLabels = new HashSet<ElementLabel>(); + Set<String> vertexLabels = new HashSet<String>(); VariableExpr variableExpr = null; + Expression filterExpr = null; + boolean isNegatedLabelSet = false; Token startToken = null; String vertexLabelName; } { <LEFTPAREN> { startToken = token; } + ( variableExpr = Variable() )? ( - variableExpr = Variable() + <COLON> + ( <CARET> { isNegatedLabelSet = true; } )? + vertexLabels = ParenthesizedLabelValueSet() )? - ( - <COLON> vertexLabelName = Identifier() { vertexLabels.add(new ElementLabel(vertexLabelName)); } - ( <BAR> vertexLabelName = Identifier() { vertexLabels.add(new ElementLabel(vertexLabelName)); } )* - )? + ( <WHERE> filterExpr = Expression() )? <RIGHTPAREN> { - VertexPatternExpr vertexExpression = new VertexPatternExpr(variableExpr, vertexLabels); + // Construct our label set. + Set<ElementLabel> labels = new HashSet<ElementLabel>(); + for (String vertexLabelValue : vertexLabels) { + labels.add(new ElementLabel(vertexLabelValue, isNegatedLabelSet)); + } + VertexPatternExpr vertexExpression = new VertexPatternExpr(variableExpr, filterExpr, labels); return addSourceLocation(vertexExpression, startToken); } } @new -EdgeDescriptor EdgeDescriptor() throws ParseException: +Pair<EdgeDescriptor, List<IExpressionAnnotation>> EdgeDescriptorPair() throws ParseException: { - Pair<Set<ElementLabel>, Pair<Integer, Integer>> edgeDetail = null; - Token startToken = null; + GraphixParserHint[] expectedHints = { GraphixParserHint.EXPAND_AND_UNION_HINT, + GraphixParserHint.SWITCH_AND_CYCLE_HINT }; + List<IExpressionAnnotation> hintList = new ArrayList<IExpressionAnnotation>(); + Triple<Set<ElementLabel>, Pair<Integer, Integer>, Expression> edgeDetail = null; + Token startToken = null, hintToken = null; VariableExpr edgeVariable = null; + Expression filterExpr = null; // We default to undirected edges. EdgeDescriptor.EdgeDirection edgeDirection = EdgeDescriptor.EdgeDirection.UNDIRECTED; @@ -666,11 +713,9 @@ <MINUS> { startToken = token; } ( <LEFTBRACKET> - ( - edgeVariable = Variable() - )? - ( <COLON> edgeDetail = EdgeDetail() )? - <RIGHTBRACKET> <MINUS> + ( edgeVariable = Variable() )? + ( edgeDetail = EdgeDetail() )? + <RIGHTBRACKET> { hintToken = token; } <MINUS> )? ( <GT> { edgeDirection = EdgeDescriptor.EdgeDirection.LEFT_TO_RIGHT; } )? | @@ -681,14 +726,21 @@ <MINUS> ( <LEFTBRACKET> - ( - edgeVariable = Variable() - )? - ( <COLON> edgeDetail = EdgeDetail() )? - <RIGHTBRACKET> <MINUS> + ( edgeVariable = Variable() )? + ( edgeDetail = EdgeDetail() )? + <RIGHTBRACKET> { hintToken = token; } <MINUS> )? ) { + // See if we have an evaluation hint. + GraphixParserHint evalHint = fetchGraphixHint(hintToken, expectedHints); + if (Objects.equals(evalHint, GraphixParserHint.EXPAND_AND_UNION_HINT)) { + hintList.add(new ElementEvaluationAnnotation(ElementEvaluationAnnotation.Kind.EXPAND_AND_UNION)); + + } else if (Objects.equals(evalHint, GraphixParserHint.SWITCH_AND_CYCLE_HINT)) { + hintList.add(new ElementEvaluationAnnotation(ElementEvaluationAnnotation.Kind.SWITCH_AND_CYCLE)); + } + // Edges (by default) are of pattern type EDGE and are not sub-paths. EdgeDescriptor.PatternType patternType = EdgeDescriptor.PatternType.EDGE; Integer hopCountMin = 1; @@ -697,6 +749,7 @@ Set<ElementLabel> labels = new HashSet<ElementLabel>(); if (edgeDetail != null) { labels = edgeDetail.first; + filterExpr = edgeDetail.third; // We have explicitly specified "{" and "}". Use sub-path semantics. if (edgeDetail.second != null) { @@ -705,42 +758,71 @@ hopCountMax = edgeDetail.second.second; } } - - return new EdgeDescriptor(edgeDirection, patternType, labels, edgeVariable, hopCountMin, hopCountMax); + EdgeDescriptor edgeDescriptor = + new EdgeDescriptor(edgeDirection, patternType, labels, filterExpr, edgeVariable, hopCountMin, hopCountMax); + return new Pair<EdgeDescriptor, List<IExpressionAnnotation>>(edgeDescriptor, hintList); } } @new -Pair<Set<ElementLabel>, Pair<Integer, Integer>> EdgeDetail() throws ParseException: +Triple<Set<ElementLabel>, Pair<Integer, Integer>, Expression> EdgeDetail() throws ParseException: { - Set<ElementLabel> edgeLabels = new HashSet<ElementLabel>(); + Set<String> edgeLabels = new HashSet<String>(); Pair<Integer, Integer> repetitionQuantifier = null; + Expression filterExpr = null; + boolean isNegatedLabelSet = false; String labelName = null; } { ( - // Note: we want to forbid LABEL_1|LABEL_2{...}. - LOOKAHEAD(2, <BAR>) - ( - labelName = Identifier() { edgeLabels.add(new ElementLabel(labelName)); } - <BAR> labelName = Identifier() { edgeLabels.add(new ElementLabel(labelName)); } - ( <BAR> labelName = Identifier() { edgeLabels.add(new ElementLabel(labelName)); } )* - ) - | - ( - labelName = Identifier() { edgeLabels.add(new ElementLabel(labelName)); } - | - <LEFTPAREN> - labelName = Identifier() { edgeLabels.add(new ElementLabel(labelName)); } - ( <BAR> labelName = Identifier() { edgeLabels.add(new ElementLabel(labelName)); } )* - <RIGHTPAREN> - ) - ( repetitionQuantifier = EdgeRepetitionQuantifier() )? - | ( repetitionQuantifier = EdgeRepetitionQuantifier() ) + | + ( + <COLON> + ( <CARET> { isNegatedLabelSet = true; } )? + edgeLabels = ParenthesizedLabelValueSet() + ( LOOKAHEAD(2) + ( <WHERE> filterExpr = Expression() ) + | + ( repetitionQuantifier = EdgeRepetitionQuantifier() ) + )? + ) + | + ( <WHERE> filterExpr = Expression() ) ) { - return new Pair<Set<ElementLabel>, Pair<Integer, Integer>> (edgeLabels, repetitionQuantifier); + // Construct our label set. + Set<ElementLabel> labels = new HashSet<ElementLabel>(); + for (String edgeLabelValue : edgeLabels) { + labels.add(new ElementLabel(edgeLabelValue, isNegatedLabelSet)); + } + return new Triple<Set<ElementLabel>, Pair<Integer, Integer>, Expression> (labels, repetitionQuantifier, filterExpr); + } +} + +@new +Set<String> ParenthesizedLabelValueSet() throws ParseException: +{ + Set<String> labelSet = new HashSet<String>(); + Set<String> innerLabelSet = null; + String labelName = null; +} +{ + ( + ( labelName = Identifier() { labelSet.add(labelName); } ) + | + ( + <LEFTPAREN> + innerLabelSet = ParenthesizedLabelValueSet() { labelSet.addAll(innerLabelSet); } + ( + <BAR> + innerLabelSet = ParenthesizedLabelValueSet() { labelSet.addAll(innerLabelSet); } + )* + <RIGHTPAREN> + ) + ) + { + return labelSet; } } @@ -751,12 +833,17 @@ Integer hopCountMax = null; } { - <LEFTBRACE> - ( // Note: we forbid unbounded edge repetition. - ( <INTEGER_LITERAL> { hopCountMin = Integer.valueOf(token.image); } )? - <COMMA> <INTEGER_LITERAL> { hopCountMax = Integer.valueOf(token.image); } + ( + <PLUS> + | + ( + <LEFTBRACE> + ( <INTEGER_LITERAL> { hopCountMin = Integer.valueOf(token.image); } )? + <COMMA> + ( <INTEGER_LITERAL> { hopCountMax = Integer.valueOf(token.image); } )? + <RIGHTBRACE> + ) ) - <RIGHTBRACE> { return new Pair<Integer, Integer>(hopCountMin, hopCountMax); }
diff --git a/asterix-graphix/src/test/resources/runtimets/queries/graphix/scope-checking/scope-checking.1.ddl.sqlpp b/asterix-graphix/src/test/resources/runtimets/queries/graphix/correlated-vertex-join/correlated-vertex-join.1.ddl.sqlpp similarity index 100% copy from asterix-graphix/src/test/resources/runtimets/queries/graphix/scope-checking/scope-checking.1.ddl.sqlpp copy to asterix-graphix/src/test/resources/runtimets/queries/graphix/correlated-vertex-join/correlated-vertex-join.1.ddl.sqlpp
diff --git a/asterix-graphix/src/test/resources/runtimets/queries/graphix/scope-checking/scope-checking.2.update.sqlpp b/asterix-graphix/src/test/resources/runtimets/queries/graphix/correlated-vertex-join/correlated-vertex-join.2.update.sqlpp similarity index 100% copy from asterix-graphix/src/test/resources/runtimets/queries/graphix/scope-checking/scope-checking.2.update.sqlpp copy to asterix-graphix/src/test/resources/runtimets/queries/graphix/correlated-vertex-join/correlated-vertex-join.2.update.sqlpp
diff --git a/asterix-graphix/src/test/resources/runtimets/queries/graphix/scope-checking/scope-checking.3.query.sqlpp b/asterix-graphix/src/test/resources/runtimets/queries/graphix/correlated-vertex-join/correlated-vertex-join.3.query.sqlpp similarity index 95% rename from asterix-graphix/src/test/resources/runtimets/queries/graphix/scope-checking/scope-checking.3.query.sqlpp rename to asterix-graphix/src/test/resources/runtimets/queries/graphix/correlated-vertex-join/correlated-vertex-join.3.query.sqlpp index 8122ce1..9252f72 100644 --- a/asterix-graphix/src/test/resources/runtimets/queries/graphix/scope-checking/scope-checking.3.query.sqlpp +++ b/asterix-graphix/src/test/resources/runtimets/queries/graphix/correlated-vertex-join/correlated-vertex-join.3.query.sqlpp
@@ -17,7 +17,7 @@ * under the License. */ -// Subquery expressing anti-join of patterns. +// Subquery expressing anti-join of patterns from different graphs. FROM GRAPH VERTEX (:User) PRIMARY KEY (user_id) AS Yelp.Users,
diff --git a/asterix-graphix/src/test/resources/runtimets/queries/graphix/scope-checking/scope-checking.4.query.sqlpp b/asterix-graphix/src/test/resources/runtimets/queries/graphix/correlated-vertex-join/correlated-vertex-join.4.query.sqlpp similarity index 100% rename from asterix-graphix/src/test/resources/runtimets/queries/graphix/scope-checking/scope-checking.4.query.sqlpp rename to asterix-graphix/src/test/resources/runtimets/queries/graphix/correlated-vertex-join/correlated-vertex-join.4.query.sqlpp
diff --git a/asterix-graphix/src/test/resources/runtimets/queries/graphix/correlated-vertex-join/correlated-vertex-join.5.query.sqlpp b/asterix-graphix/src/test/resources/runtimets/queries/graphix/correlated-vertex-join/correlated-vertex-join.5.query.sqlpp new file mode 100644 index 0000000..f2d93cd --- /dev/null +++ b/asterix-graphix/src/test/resources/runtimets/queries/graphix/correlated-vertex-join/correlated-vertex-join.5.query.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. + */ + +// Implicit correlated vertex anti-JOIN. +DECLARE GRAPH YelpGraph AS +VERTEX (:User) + PRIMARY KEY (user_id) + AS Yelp.Users, +VERTEX (:Review) + PRIMARY KEY (review_id) + AS Yelp.Reviews, +EDGE (:Review)-[:MADE_BY]->(:User) + SOURCE KEY (review_id) + DESTINATION KEY (user_id) + AS ( FROM Yelp.Reviews R + SELECT R.review_id, R.user_id ); + +FROM GRAPH YelpGraph +MATCH (u:User)<-(r) +WHERE NOT EXISTS ( FROM GRAPH YelpGraph + MATCH (u) + WHERE u.user_id = 1 + SELECT VALUE 1 ) +SELECT u.user_id, r.review_id +ORDER BY u.user_id, r.review_id; \ No newline at end of file
diff --git a/asterix-graphix/src/test/resources/runtimets/queries/graphix/dangling-vertices/dangling-vertices.5.query.sqlpp b/asterix-graphix/src/test/resources/runtimets/queries/graphix/dangling-vertices/dangling-vertices.5.query.sqlpp index 78851df..f9ea1cd 100644 --- a/asterix-graphix/src/test/resources/runtimets/queries/graphix/dangling-vertices/dangling-vertices.5.query.sqlpp +++ b/asterix-graphix/src/test/resources/runtimets/queries/graphix/dangling-vertices/dangling-vertices.5.query.sqlpp
@@ -17,7 +17,7 @@ * under the License. */ --- param max-warnings:string=2 +-- param max-warnings:string=3 // There are two dangling vertices of different labels, and zero edges. FROM GRAPH Yelp.YelpGraph
diff --git a/asterix-graphix/src/test/resources/runtimets/queries/graphix/graph-isomorphism/graph-isomorphism.6.query.sqlpp b/asterix-graphix/src/test/resources/runtimets/queries/graphix/graph-isomorphism/graph-isomorphism.6.query.sqlpp index e3f1e7c..49e851b 100644 --- a/asterix-graphix/src/test/resources/runtimets/queries/graphix/graph-isomorphism/graph-isomorphism.6.query.sqlpp +++ b/asterix-graphix/src/test/resources/runtimets/queries/graphix/graph-isomorphism/graph-isomorphism.6.query.sqlpp
@@ -18,7 +18,7 @@ */ // There should be results where (u) = (v). -SET `graphix.match-evaluation` "homomorphism"; +SET `graphix.semantics.pattern` "homomorphism"; FROM GRAPH VERTEX (:User) PRIMARY KEY (user_id) AS Yelp.Users
diff --git a/asterix-graphix/src/test/resources/runtimets/queries/graphix/graph-isomorphism/graph-isomorphism.7.query.sqlpp b/asterix-graphix/src/test/resources/runtimets/queries/graphix/graph-isomorphism/graph-isomorphism.7.query.sqlpp index 0e2cbc9..8451d7d 100644 --- a/asterix-graphix/src/test/resources/runtimets/queries/graphix/graph-isomorphism/graph-isomorphism.7.query.sqlpp +++ b/asterix-graphix/src/test/resources/runtimets/queries/graphix/graph-isomorphism/graph-isomorphism.7.query.sqlpp
@@ -18,7 +18,7 @@ */ // There should be results where (u) = (w) (i.e. only edge adjacency should be preserved). -SET `graphix.match-evaluation` "homomorphism"; +SET `graphix.semantics.pattern` "homomorphism"; FROM GRAPH VERTEX (:User) PRIMARY KEY (user_id) AS Yelp.Users,
diff --git a/asterix-graphix/src/test/resources/runtimets/queries/graphix/graphix-functions/graphix-functions.3.query.sqlpp b/asterix-graphix/src/test/resources/runtimets/queries/graphix/graphix-functions/graphix-functions.3.query.sqlpp index 71e441c..7de7558 100644 --- a/asterix-graphix/src/test/resources/runtimets/queries/graphix/graphix-functions/graphix-functions.3.query.sqlpp +++ b/asterix-graphix/src/test/resources/runtimets/queries/graphix/graphix-functions/graphix-functions.3.query.sqlpp
@@ -25,6 +25,6 @@ PRIMARY KEY (review_id) AS Yelp.Reviews MATCH (n) -SELECT LABEL(n) AS vertexLabel, +SELECT LABEL(n) AS elementLabel, VERTEX_DETAIL(n) AS vertexDetail ORDER BY n; \ No newline at end of file
diff --git a/asterix-graphix/src/test/resources/runtimets/queries/graphix/graphix-functions/graphix-functions.4.query.sqlpp b/asterix-graphix/src/test/resources/runtimets/queries/graphix/graphix-functions/graphix-functions.4.query.sqlpp index 0283334..b468c26 100644 --- a/asterix-graphix/src/test/resources/runtimets/queries/graphix/graphix-functions/graphix-functions.4.query.sqlpp +++ b/asterix-graphix/src/test/resources/runtimets/queries/graphix/graphix-functions/graphix-functions.4.query.sqlpp
@@ -43,7 +43,7 @@ LABEL(e) AS edgeLabel, LABEL(n1) AS n1Label, LABEL(n2) AS n2Label, - LABEL(sourceVertex) AS sourceVertexLabel, - LABEL(destVertex) AS destVertexLabel, + LABEL(sourceVertex) AS sourceElementLabel, + LABEL(destVertex) AS destElementLabel, EDGE_DETAIL(e) AS edgeDetail ORDER BY direction, edgeLabel, n1Label, n2Label, edgeDetail; \ No newline at end of file
diff --git a/asterix-graphix/src/test/resources/runtimets/queries/graphix/graphix-functions/graphix-functions.5.query.sqlpp b/asterix-graphix/src/test/resources/runtimets/queries/graphix/graphix-functions/graphix-functions.5.query.sqlpp index a427758..08319e2 100644 --- a/asterix-graphix/src/test/resources/runtimets/queries/graphix/graphix-functions/graphix-functions.5.query.sqlpp +++ b/asterix-graphix/src/test/resources/runtimets/queries/graphix/graphix-functions/graphix-functions.5.query.sqlpp
@@ -27,7 +27,7 @@ AS ( FROM Yelp.Friends F SELECT F.user_id AS user_id, F.friend AS friend ) -MATCH (n1)-[e:{1,2}]->(n2)->(n3) AS p +MATCH (n1)-[e{1,2}]->(n2)->(n3) AS p SELECT EDGES(p) AS pEdges, EDGES(e) AS eEdges, HOP_COUNT(p) AS pHopCount,
diff --git a/asterix-graphix/src/test/resources/runtimets/queries/graphix/on-query-error/on-query-error.13.query.sqlpp b/asterix-graphix/src/test/resources/runtimets/queries/graphix/on-query-error/on-query-error.13.query.sqlpp new file mode 100644 index 0000000..a995d98 --- /dev/null +++ b/asterix-graphix/src/test/resources/runtimets/queries/graphix/on-query-error/on-query-error.13.query.sqlpp
@@ -0,0 +1,34 @@ +/* + * 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. + */ + +// Verify that a query that doesn't adhere to the graph schema returns an error. +USE TestDataverse; +FROM GRAPH VERTEX (:Vertex1) + PRIMARY KEY (_id) + AS GenericDataset, + VERTEX (:Vertex2) + PRIMARY KEY (_id) + AS GenericDataset, + EDGE (:Vertex1)-[:EDGE_1]->(:Vertex2) + SOURCE KEY (_id) + DESTINATION KEY (_to_id) + AS ( FROM GenericDataset GD + SELECT GD._id, GD._to_id ) +MATCH (v:Vertex1)<-[:EDGE_1]-(:Vertex2) +SELECT v;
diff --git a/asterix-graphix/src/test/resources/runtimets/queries/graphix/path-variable/path-variable.6.query.sqlpp b/asterix-graphix/src/test/resources/runtimets/queries/graphix/path-variable/path-variable.6.query.sqlpp index 595463c..3538605 100644 --- a/asterix-graphix/src/test/resources/runtimets/queries/graphix/path-variable/path-variable.6.query.sqlpp +++ b/asterix-graphix/src/test/resources/runtimets/queries/graphix/path-variable/path-variable.6.query.sqlpp
@@ -27,7 +27,7 @@ AS ( FROM Yelp.Friends F WHERE F.friend_group = "A" SELECT F.user_id, F.friend ) -MATCH (u)-[e1:{1,2}]-(v) AS p +MATCH (u)-[e1{1,2}]-(v) AS p SELECT LEN(p.Edges) AS pathLength, u.user_id AS u_user_id, v.user_id AS v_user_id
diff --git a/asterix-graphix/src/test/resources/runtimets/queries/graphix/scope-checking/scope-checking.1.ddl.sqlpp b/asterix-graphix/src/test/resources/runtimets/queries/graphix/same-edge-label/same-edge-label.1.ddl.sqlpp similarity index 100% rename from asterix-graphix/src/test/resources/runtimets/queries/graphix/scope-checking/scope-checking.1.ddl.sqlpp rename to asterix-graphix/src/test/resources/runtimets/queries/graphix/same-edge-label/same-edge-label.1.ddl.sqlpp
diff --git a/asterix-graphix/src/test/resources/runtimets/queries/graphix/scope-checking/scope-checking.2.update.sqlpp b/asterix-graphix/src/test/resources/runtimets/queries/graphix/same-edge-label/same-edge-label.2.update.sqlpp similarity index 100% rename from asterix-graphix/src/test/resources/runtimets/queries/graphix/scope-checking/scope-checking.2.update.sqlpp rename to asterix-graphix/src/test/resources/runtimets/queries/graphix/same-edge-label/same-edge-label.2.update.sqlpp
diff --git a/asterix-graphix/src/test/resources/runtimets/queries/graphix/same-edge-label/same-edge-label.3.query.sqlpp b/asterix-graphix/src/test/resources/runtimets/queries/graphix/same-edge-label/same-edge-label.3.query.sqlpp new file mode 100644 index 0000000..b11858b --- /dev/null +++ b/asterix-graphix/src/test/resources/runtimets/queries/graphix/same-edge-label/same-edge-label.3.query.sqlpp
@@ -0,0 +1,48 @@ +/* + * 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. + */ + +// Query with a schema edge of the same label. +DECLARE GRAPH YelpGraph AS +VERTEX (:User) + PRIMARY KEY (user_id) + AS Yelp.Users, +VERTEX (:Review) + PRIMARY KEY (review_id) + AS Yelp.Reviews, +EDGE (:Review)-[:RELATED_TO]->(:User) + SOURCE KEY (review_id) + DESTINATION KEY (user_id) + AS ( FROM Yelp.Reviews R + SELECT R.review_id, R.user_id ), +EDGE (:User)-[:RELATED_TO]->(:User) + SOURCE KEY (user_id) + DESTINATION KEY (friend) + AS ( FROM Yelp.Friends F + SELECT F.user_id, F.friend ); + +FROM GRAPH YelpGraph +MATCH (u)-[:RELATED_TO]->(v) +SELECT v.user_id AS v_user_id, + CASE + WHEN LABEL(u) = "User" + THEN { "user_id": u.user_id } + WHEN LABEL(u) = "Review" + THEN { "review_id": u.review_id } + END AS u_record +ORDER BY u, v; \ No newline at end of file
diff --git a/asterix-graphix/src/test/resources/runtimets/queries/graphix/inference-resolution/inference-resolution.1.ddl.sqlpp b/asterix-graphix/src/test/resources/runtimets/queries/graphix/schema-resolution/schema-resolution.1.ddl.sqlpp similarity index 100% rename from asterix-graphix/src/test/resources/runtimets/queries/graphix/inference-resolution/inference-resolution.1.ddl.sqlpp rename to asterix-graphix/src/test/resources/runtimets/queries/graphix/schema-resolution/schema-resolution.1.ddl.sqlpp
diff --git a/asterix-graphix/src/test/resources/runtimets/queries/graphix/inference-resolution/inference-resolution.2.update.sqlpp b/asterix-graphix/src/test/resources/runtimets/queries/graphix/schema-resolution/schema-resolution.2.update.sqlpp similarity index 100% rename from asterix-graphix/src/test/resources/runtimets/queries/graphix/inference-resolution/inference-resolution.2.update.sqlpp rename to asterix-graphix/src/test/resources/runtimets/queries/graphix/schema-resolution/schema-resolution.2.update.sqlpp
diff --git a/asterix-graphix/src/test/resources/runtimets/queries/graphix/inference-resolution/inference-resolution.3.query.sqlpp b/asterix-graphix/src/test/resources/runtimets/queries/graphix/schema-resolution/schema-resolution.3.query.sqlpp similarity index 96% rename from asterix-graphix/src/test/resources/runtimets/queries/graphix/inference-resolution/inference-resolution.3.query.sqlpp rename to asterix-graphix/src/test/resources/runtimets/queries/graphix/schema-resolution/schema-resolution.3.query.sqlpp index 700fe8f..f1e7af5 100644 --- a/asterix-graphix/src/test/resources/runtimets/queries/graphix/inference-resolution/inference-resolution.3.query.sqlpp +++ b/asterix-graphix/src/test/resources/runtimets/queries/graphix/schema-resolution/schema-resolution.3.query.sqlpp
@@ -20,7 +20,6 @@ -- param max-warnings:string=1 // We should be able to resolve both e and n, using the edge direction. -SET `graphix.resolver` "inference-based"; FROM GRAPH VERTEX (:User) PRIMARY KEY (user_id) AS ( FROM Yelp.Users U
diff --git a/asterix-graphix/src/test/resources/runtimets/queries/graphix/inference-resolution/inference-resolution.4.query.sqlpp b/asterix-graphix/src/test/resources/runtimets/queries/graphix/schema-resolution/schema-resolution.4.query.sqlpp similarity index 97% rename from asterix-graphix/src/test/resources/runtimets/queries/graphix/inference-resolution/inference-resolution.4.query.sqlpp rename to asterix-graphix/src/test/resources/runtimets/queries/graphix/schema-resolution/schema-resolution.4.query.sqlpp index 5533ede..1b4282a 100644 --- a/asterix-graphix/src/test/resources/runtimets/queries/graphix/inference-resolution/inference-resolution.4.query.sqlpp +++ b/asterix-graphix/src/test/resources/runtimets/queries/graphix/schema-resolution/schema-resolution.4.query.sqlpp
@@ -20,7 +20,6 @@ -- param max-warnings:string=1 // We should be able to resolve all elements (e, n, f, m), using the edge direction of the last edge in our pattern. -SET `graphix.resolver` "inference-based"; FROM GRAPH VERTEX (:User) PRIMARY KEY (user_id) AS ( FROM Yelp.Users U
diff --git a/asterix-graphix/src/test/resources/runtimets/queries/graphix/inference-resolution/inference-resolution.5.query.sqlpp b/asterix-graphix/src/test/resources/runtimets/queries/graphix/schema-resolution/schema-resolution.5.query.sqlpp similarity index 97% rename from asterix-graphix/src/test/resources/runtimets/queries/graphix/inference-resolution/inference-resolution.5.query.sqlpp rename to asterix-graphix/src/test/resources/runtimets/queries/graphix/schema-resolution/schema-resolution.5.query.sqlpp index df7e676..9202011 100644 --- a/asterix-graphix/src/test/resources/runtimets/queries/graphix/inference-resolution/inference-resolution.5.query.sqlpp +++ b/asterix-graphix/src/test/resources/runtimets/queries/graphix/schema-resolution/schema-resolution.5.query.sqlpp
@@ -20,7 +20,6 @@ -- param max-warnings:string=1 // We should be able to resolve all elements (e, n, f, last direction), using the edge direction of the first pattern. -SET `graphix.resolver` "inference-based"; FROM GRAPH VERTEX (:User) PRIMARY KEY (user_id) AS ( FROM Yelp.Users U
diff --git a/asterix-graphix/src/test/resources/runtimets/queries/graphix/inference-resolution/inference-resolution.6.query.sqlpp b/asterix-graphix/src/test/resources/runtimets/queries/graphix/schema-resolution/schema-resolution.6.query.sqlpp similarity index 94% rename from asterix-graphix/src/test/resources/runtimets/queries/graphix/inference-resolution/inference-resolution.6.query.sqlpp rename to asterix-graphix/src/test/resources/runtimets/queries/graphix/schema-resolution/schema-resolution.6.query.sqlpp index 30de634..fa256a5 100644 --- a/asterix-graphix/src/test/resources/runtimets/queries/graphix/inference-resolution/inference-resolution.6.query.sqlpp +++ b/asterix-graphix/src/test/resources/runtimets/queries/graphix/schema-resolution/schema-resolution.6.query.sqlpp
@@ -20,7 +20,6 @@ -- param max-warnings:string=1 // We should be able to resolve all elements (e, n, f, last direction), using the edge direction of the first pattern. -SET `graphix.resolver` "inference-based"; FROM GRAPH VERTEX (:User) PRIMARY KEY (user_id) AS ( FROM Yelp.Users U @@ -39,7 +38,7 @@ DESTINATION KEY (user_id) AS ( FROM Yelp.Reviews R SELECT R.review_id, R.user_id ) -MATCH (u:User)-[e:{2,2}]->(n) // (u:User)-[]->()-[]->(n) +MATCH (u:User)-[e{2,2}]->(n) // (u:User)-[]->()-[]->(n) UNNEST PATH_EDGES(e) AS ee SELECT DISTINCT LABEL(ee) AS e_label, LABEL(n) AS n_label,
diff --git a/asterix-graphix/src/test/resources/runtimets/queries/graphix/inference-resolution/inference-resolution.3.query.sqlpp b/asterix-graphix/src/test/resources/runtimets/queries/graphix/schema-resolution/schema-resolution.7.query.sqlpp similarity index 89% copy from asterix-graphix/src/test/resources/runtimets/queries/graphix/inference-resolution/inference-resolution.3.query.sqlpp copy to asterix-graphix/src/test/resources/runtimets/queries/graphix/schema-resolution/schema-resolution.7.query.sqlpp index 700fe8f..780f1a2 100644 --- a/asterix-graphix/src/test/resources/runtimets/queries/graphix/inference-resolution/inference-resolution.3.query.sqlpp +++ b/asterix-graphix/src/test/resources/runtimets/queries/graphix/schema-resolution/schema-resolution.7.query.sqlpp
@@ -19,8 +19,7 @@ -- param max-warnings:string=1 -// We should be able to resolve both e and n, using the edge direction. -SET `graphix.resolver` "inference-based"; +// We should be able to resolve both u and n, using the negated edge label. FROM GRAPH VERTEX (:User) PRIMARY KEY (user_id) AS ( FROM Yelp.Users U @@ -39,6 +38,6 @@ DESTINATION KEY (user_id) AS ( FROM Yelp.Reviews R SELECT R.review_id, R.user_id ) -MATCH (u:User)-[e]->(n) -SELECT DISTINCT LABEL(e) AS e_label, +MATCH (u)-[e:^MADE_BY]-(n) +SELECT DISTINCT LABEL(u) AS u_label, LABEL(n) AS n_label;
diff --git a/asterix-graphix/src/test/resources/runtimets/queries/graphix/schema-resolution/schema-resolution.8.query.sqlpp b/asterix-graphix/src/test/resources/runtimets/queries/graphix/schema-resolution/schema-resolution.8.query.sqlpp new file mode 100644 index 0000000..34b03d6 --- /dev/null +++ b/asterix-graphix/src/test/resources/runtimets/queries/graphix/schema-resolution/schema-resolution.8.query.sqlpp
@@ -0,0 +1,62 @@ +/* + * 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. + */ + +-- param max-warnings:string=1 + +// We should be able to resolve both subquery vertex JOINs in both directions. +DECLARE GRAPH YelpGraph AS +VERTEX (:User) + PRIMARY KEY (user_id) + AS ( FROM Yelp.Users U + SELECT U.user_id ), +VERTEX (:Review) + PRIMARY KEY (review_id) + AS ( FROM Yelp.Reviews R + SELECT R.review_id ), +EDGE (:User)-[:FRIENDS_WITH]->(:User) + SOURCE KEY (user_id) + DESTINATION KEY (friend) + AS ( FROM Yelp.Friends F + SELECT F.user_id, F.friend ), +EDGE (:Review)-[:MADE_BY]->(:User) + SOURCE KEY (review_id) + DESTINATION KEY (user_id) + AS ( FROM Yelp.Reviews R + SELECT R.review_id, R.user_id ); + +FROM GRAPH YelpGraph +MATCH (u)->(r) +WHERE EXISTS ( FROM GRAPH YelpGraph + MATCH (u:User) + WHERE u.user_id = 1 + SELECT VALUE 1 ) +SELECT DISTINCT LABEL(u) AS u_label, + LABEL(r) AS r_label + +UNION ALL + +FROM GRAPH YelpGraph +MATCH (u:User)->(r) +WHERE EXISTS ( FROM GRAPH YelpGraph + MATCH (u) + WHERE u.user_id = 1 + SELECT VALUE 1 ) +SELECT DISTINCT LABEL(u) AS u_label, + LABEL(r) AS r_label; +
diff --git a/asterix-graphix/src/test/resources/runtimets/queries/graphix/scope-checking/scope-checking.1.ddl.sqlpp b/asterix-graphix/src/test/resources/runtimets/queries/graphix/scope-validation/scope-validation.1.ddl.sqlpp similarity index 100% copy from asterix-graphix/src/test/resources/runtimets/queries/graphix/scope-checking/scope-checking.1.ddl.sqlpp copy to asterix-graphix/src/test/resources/runtimets/queries/graphix/scope-validation/scope-validation.1.ddl.sqlpp
diff --git a/asterix-graphix/src/test/resources/runtimets/queries/graphix/scope-checking/scope-checking.2.update.sqlpp b/asterix-graphix/src/test/resources/runtimets/queries/graphix/scope-validation/scope-validation.2.update.sqlpp similarity index 100% copy from asterix-graphix/src/test/resources/runtimets/queries/graphix/scope-checking/scope-checking.2.update.sqlpp copy to asterix-graphix/src/test/resources/runtimets/queries/graphix/scope-validation/scope-validation.2.update.sqlpp
diff --git a/asterix-graphix/src/test/resources/runtimets/queries/graphix/scope-checking/scope-checking.7.query.sqlpp b/asterix-graphix/src/test/resources/runtimets/queries/graphix/scope-validation/scope-validation.3.query.sqlpp similarity index 100% rename from asterix-graphix/src/test/resources/runtimets/queries/graphix/scope-checking/scope-checking.7.query.sqlpp rename to asterix-graphix/src/test/resources/runtimets/queries/graphix/scope-validation/scope-validation.3.query.sqlpp
diff --git a/asterix-graphix/src/test/resources/runtimets/queries/graphix/scope-checking/scope-checking.8.query.sqlpp b/asterix-graphix/src/test/resources/runtimets/queries/graphix/scope-validation/scope-validation.4.query.sqlpp similarity index 100% rename from asterix-graphix/src/test/resources/runtimets/queries/graphix/scope-checking/scope-checking.8.query.sqlpp rename to asterix-graphix/src/test/resources/runtimets/queries/graphix/scope-validation/scope-validation.4.query.sqlpp
diff --git a/asterix-graphix/src/test/resources/runtimets/queries/graphix/scope-checking/scope-checking.3.query.sqlpp b/asterix-graphix/src/test/resources/runtimets/queries/graphix/simple-1-edge/simple-1-edge.6.query.sqlpp similarity index 63% copy from asterix-graphix/src/test/resources/runtimets/queries/graphix/scope-checking/scope-checking.3.query.sqlpp copy to asterix-graphix/src/test/resources/runtimets/queries/graphix/simple-1-edge/simple-1-edge.6.query.sqlpp index 8122ce1..ecec02d 100644 --- a/asterix-graphix/src/test/resources/runtimets/queries/graphix/scope-checking/scope-checking.3.query.sqlpp +++ b/asterix-graphix/src/test/resources/runtimets/queries/graphix/simple-1-edge/simple-1-edge.6.query.sqlpp
@@ -17,7 +17,8 @@ * under the License. */ -// Subquery expressing anti-join of patterns. +// We have a single edge with a filter expression on the User vertex. +LET startingUserID = 1 FROM GRAPH VERTEX (:User) PRIMARY KEY (user_id) AS Yelp.Users, @@ -26,17 +27,11 @@ AS Yelp.Reviews, EDGE (:Review)-[:MADE_BY]->(:User) SOURCE KEY (review_id) - DESTINATION KEY (user_id) + DESTINATION KEY (review_user_id) AS ( FROM Yelp.Reviews R - SELECT R.review_id, R.user_id ) -MATCH (u:User)<-(r) -WHERE NOT EXISTS ( FROM GRAPH VERTEX (:User) - PRIMARY KEY (user_id) - AS ( FROM Yelp.Users U - WHERE U.user_id = 1 - SELECT VALUE U ) - MATCH (innerU:User) - WHERE innerU = u - SELECT VALUE 1 ) -SELECT u.user_id -ORDER BY u.user_id; \ No newline at end of file + SELECT R.review_user_id, R.review_id ) +MATCH (u:User WHERE u.user_id = startingUserID)<-[:MADE_BY]-(r:Review) +SELECT u.user_id, + r.review_id +ORDER BY u.user_id, + r.review_id;
diff --git a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/lower/transform/ISequenceTransformer.java b/asterix-graphix/src/test/resources/runtimets/queries/graphix/variable-sub-path/variable-sub-path.5.update.sqlpp similarity index 69% copy from asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/lower/transform/ISequenceTransformer.java copy to asterix-graphix/src/test/resources/runtimets/queries/graphix/variable-sub-path/variable-sub-path.5.update.sqlpp index 4509792..fdddf8d 100644 --- a/asterix-graphix/src/main/java/org/apache/asterix/graphix/lang/rewrites/lower/transform/ISequenceTransformer.java +++ b/asterix-graphix/src/test/resources/runtimets/queries/graphix/variable-sub-path/variable-sub-path.5.update.sqlpp
@@ -16,11 +16,22 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.asterix.graphix.lang.rewrites.lower.transform; -import org.apache.asterix.common.exceptions.CompilationException; +USE Yelp; -@FunctionalInterface -public interface ISequenceTransformer { - void accept(CorrelatedClauseSequence clauseSequence) throws CompilationException; -} +DELETE FROM Users; +DELETE FROM Friends; + +INSERT INTO Users [ + { "user_id": 1 }, + { "user_id": 2 }, + { "user_id": 3 }, + { "user_id": 4 } +]; + +INSERT INTO Friends [ + { "user_id": 1, "friend": 2 }, + { "user_id": 2, "friend": 3 }, + { "user_id": 3, "friend": 4 }, + { "user_id": 1, "friend": 3 } +];
diff --git a/asterix-graphix/src/test/resources/runtimets/queries/graphix/scope-checking/scope-checking.5.query.sqlpp b/asterix-graphix/src/test/resources/runtimets/queries/graphix/variable-sub-path/variable-sub-path.6.query.sqlpp similarity index 97% rename from asterix-graphix/src/test/resources/runtimets/queries/graphix/scope-checking/scope-checking.5.query.sqlpp rename to asterix-graphix/src/test/resources/runtimets/queries/graphix/variable-sub-path/variable-sub-path.6.query.sqlpp index c0c8d48..91bbdfa 100644 --- a/asterix-graphix/src/test/resources/runtimets/queries/graphix/scope-checking/scope-checking.5.query.sqlpp +++ b/asterix-graphix/src/test/resources/runtimets/queries/graphix/variable-sub-path/variable-sub-path.6.query.sqlpp
@@ -26,7 +26,7 @@ DESTINATION KEY (friend) AS ( FROM Yelp.Friends F SELECT VALUE F ) -MATCH (u1)-[:{1,3}]->(u2) AS p +MATCH (u1)-[{1,3}]->(u2) AS p GROUP BY u1, u2 GROUP AS g LET shortestPath = ( FROM g AS gi
diff --git a/asterix-graphix/src/test/resources/runtimets/queries/graphix/scope-checking/scope-checking.6.query.sqlpp b/asterix-graphix/src/test/resources/runtimets/queries/graphix/variable-sub-path/variable-sub-path.7.query.sqlpp similarity index 94% rename from asterix-graphix/src/test/resources/runtimets/queries/graphix/scope-checking/scope-checking.6.query.sqlpp rename to asterix-graphix/src/test/resources/runtimets/queries/graphix/variable-sub-path/variable-sub-path.7.query.sqlpp index 62e6a83..99c8c02 100644 --- a/asterix-graphix/src/test/resources/runtimets/queries/graphix/scope-checking/scope-checking.6.query.sqlpp +++ b/asterix-graphix/src/test/resources/runtimets/queries/graphix/variable-sub-path/variable-sub-path.7.query.sqlpp
@@ -17,6 +17,7 @@ * under the License. */ +// Subquery + GROUP BY expressing (bounded) shortest-path. // Subquery + GROUP BY expressing (bounded) shortest-path, using aliases. FROM GRAPH VERTEX (:User) PRIMARY KEY (user_id) @@ -26,7 +27,7 @@ DESTINATION KEY (friend) AS ( FROM Yelp.Friends F SELECT VALUE F ) -MATCH (u1)-[:{1,3}]->(u2) AS p +MATCH (u1)-[{1,3}]->(u2) AS p LET pathHopCount = PATH_HOP_COUNT(p), pathVertices = PATH_VERTICES(p), myUser1 = u1,
diff --git a/asterix-graphix/src/test/resources/runtimets/results/graphix/scope-checking/scope-checking.3.adm b/asterix-graphix/src/test/resources/runtimets/results/graphix/correlated-vertex-join/correlated-vertex-join.3.adm similarity index 100% rename from asterix-graphix/src/test/resources/runtimets/results/graphix/scope-checking/scope-checking.3.adm rename to asterix-graphix/src/test/resources/runtimets/results/graphix/correlated-vertex-join/correlated-vertex-join.3.adm
diff --git a/asterix-graphix/src/test/resources/runtimets/results/graphix/scope-checking/scope-checking.4.adm b/asterix-graphix/src/test/resources/runtimets/results/graphix/correlated-vertex-join/correlated-vertex-join.4.adm similarity index 100% rename from asterix-graphix/src/test/resources/runtimets/results/graphix/scope-checking/scope-checking.4.adm rename to asterix-graphix/src/test/resources/runtimets/results/graphix/correlated-vertex-join/correlated-vertex-join.4.adm
diff --git a/asterix-graphix/src/test/resources/runtimets/results/graphix/scope-checking/scope-checking.4.adm b/asterix-graphix/src/test/resources/runtimets/results/graphix/correlated-vertex-join/correlated-vertex-join.5.adm similarity index 100% copy from asterix-graphix/src/test/resources/runtimets/results/graphix/scope-checking/scope-checking.4.adm copy to asterix-graphix/src/test/resources/runtimets/results/graphix/correlated-vertex-join/correlated-vertex-join.5.adm
diff --git a/asterix-graphix/src/test/resources/runtimets/results/graphix/graphix-functions/graphix-functions.3.adm b/asterix-graphix/src/test/resources/runtimets/results/graphix/graphix-functions/graphix-functions.3.adm index c514419..47003ca 100644 --- a/asterix-graphix/src/test/resources/runtimets/results/graphix/graphix-functions/graphix-functions.3.adm +++ b/asterix-graphix/src/test/resources/runtimets/results/graphix/graphix-functions/graphix-functions.3.adm
@@ -1,8 +1,8 @@ -{ "vertexLabel": "Review", "vertexDetail": { "ElementLabel": "Review", "VertexKey": [ "A" ] } } -{ "vertexLabel": "Review", "vertexDetail": { "ElementLabel": "Review", "VertexKey": [ "B" ] } } -{ "vertexLabel": "User", "vertexDetail": { "ElementLabel": "User", "VertexKey": [ 1 ] } } -{ "vertexLabel": "User", "vertexDetail": { "ElementLabel": "User", "VertexKey": [ 2 ] } } -{ "vertexLabel": "User", "vertexDetail": { "ElementLabel": "User", "VertexKey": [ 3 ] } } -{ "vertexLabel": "User", "vertexDetail": { "ElementLabel": "User", "VertexKey": [ 4 ] } } -{ "vertexLabel": "User", "vertexDetail": { "ElementLabel": "User", "VertexKey": [ 5 ] } } -{ "vertexLabel": "User", "vertexDetail": { "ElementLabel": "User", "VertexKey": [ 6 ] } } \ No newline at end of file +{ "elementLabel": "Review", "vertexDetail": { "ElementLabel": "Review", "VertexKey": [ "A" ] } } +{ "elementLabel": "Review", "vertexDetail": { "ElementLabel": "Review", "VertexKey": [ "B" ] } } +{ "elementLabel": "User", "vertexDetail": { "ElementLabel": "User", "VertexKey": [ 1 ] } } +{ "elementLabel": "User", "vertexDetail": { "ElementLabel": "User", "VertexKey": [ 2 ] } } +{ "elementLabel": "User", "vertexDetail": { "ElementLabel": "User", "VertexKey": [ 3 ] } } +{ "elementLabel": "User", "vertexDetail": { "ElementLabel": "User", "VertexKey": [ 4 ] } } +{ "elementLabel": "User", "vertexDetail": { "ElementLabel": "User", "VertexKey": [ 5 ] } } +{ "elementLabel": "User", "vertexDetail": { "ElementLabel": "User", "VertexKey": [ 6 ] } } \ No newline at end of file
diff --git a/asterix-graphix/src/test/resources/runtimets/results/graphix/graphix-functions/graphix-functions.4.adm b/asterix-graphix/src/test/resources/runtimets/results/graphix/graphix-functions/graphix-functions.4.adm index c81b251..bfe59dc 100644 --- a/asterix-graphix/src/test/resources/runtimets/results/graphix/graphix-functions/graphix-functions.4.adm +++ b/asterix-graphix/src/test/resources/runtimets/results/graphix/graphix-functions/graphix-functions.4.adm
@@ -1,14 +1,14 @@ -{ "direction": "LEFT_TO_RIGHT", "edgeLabel": "FRIENDS_WITH", "edgeDetail": { "ElementLabel": "FRIENDS_WITH", "EdgeDirection": "LEFT_TO_RIGHT", "SourceKey": [ 1 ], "DestinationKey": [ 2 ] }, "n1Label": "User", "n2Label": "User", "sourceVertexLabel": "User", "destVertexLabel": "User" } -{ "direction": "LEFT_TO_RIGHT", "edgeLabel": "FRIENDS_WITH", "edgeDetail": { "ElementLabel": "FRIENDS_WITH", "EdgeDirection": "LEFT_TO_RIGHT", "SourceKey": [ 2 ], "DestinationKey": [ 3 ] }, "n1Label": "User", "n2Label": "User", "sourceVertexLabel": "User", "destVertexLabel": "User" } -{ "direction": "LEFT_TO_RIGHT", "edgeLabel": "FRIENDS_WITH", "edgeDetail": { "ElementLabel": "FRIENDS_WITH", "EdgeDirection": "LEFT_TO_RIGHT", "SourceKey": [ 3 ], "DestinationKey": [ 4 ] }, "n1Label": "User", "n2Label": "User", "sourceVertexLabel": "User", "destVertexLabel": "User" } -{ "direction": "LEFT_TO_RIGHT", "edgeLabel": "FRIENDS_WITH", "edgeDetail": { "ElementLabel": "FRIENDS_WITH", "EdgeDirection": "LEFT_TO_RIGHT", "SourceKey": [ 4 ], "DestinationKey": [ 5 ] }, "n1Label": "User", "n2Label": "User", "sourceVertexLabel": "User", "destVertexLabel": "User" } -{ "direction": "LEFT_TO_RIGHT", "edgeLabel": "FRIENDS_WITH", "edgeDetail": { "ElementLabel": "FRIENDS_WITH", "EdgeDirection": "LEFT_TO_RIGHT", "SourceKey": [ 5 ], "DestinationKey": [ 6 ] }, "n1Label": "User", "n2Label": "User", "sourceVertexLabel": "User", "destVertexLabel": "User" } -{ "direction": "LEFT_TO_RIGHT", "edgeLabel": "MADE_BY", "edgeDetail": { "ElementLabel": "MADE_BY", "EdgeDirection": "LEFT_TO_RIGHT", "SourceKey": [ "A" ], "DestinationKey": [ 1 ] }, "n1Label": "Review", "n2Label": "User", "sourceVertexLabel": "Review", "destVertexLabel": "User" } -{ "direction": "LEFT_TO_RIGHT", "edgeLabel": "MADE_BY", "edgeDetail": { "ElementLabel": "MADE_BY", "EdgeDirection": "LEFT_TO_RIGHT", "SourceKey": [ "B" ], "DestinationKey": [ 2 ] }, "n1Label": "Review", "n2Label": "User", "sourceVertexLabel": "Review", "destVertexLabel": "User" } -{ "direction": "RIGHT_TO_LEFT", "edgeLabel": "FRIENDS_WITH", "edgeDetail": { "ElementLabel": "FRIENDS_WITH", "EdgeDirection": "RIGHT_TO_LEFT", "SourceKey": [ 1 ], "DestinationKey": [ 2 ] }, "n1Label": "User", "n2Label": "User", "sourceVertexLabel": "User", "destVertexLabel": "User" } -{ "direction": "RIGHT_TO_LEFT", "edgeLabel": "FRIENDS_WITH", "edgeDetail": { "ElementLabel": "FRIENDS_WITH", "EdgeDirection": "RIGHT_TO_LEFT", "SourceKey": [ 2 ], "DestinationKey": [ 3 ] }, "n1Label": "User", "n2Label": "User", "sourceVertexLabel": "User", "destVertexLabel": "User" } -{ "direction": "RIGHT_TO_LEFT", "edgeLabel": "FRIENDS_WITH", "edgeDetail": { "ElementLabel": "FRIENDS_WITH", "EdgeDirection": "RIGHT_TO_LEFT", "SourceKey": [ 3 ], "DestinationKey": [ 4 ] }, "n1Label": "User", "n2Label": "User", "sourceVertexLabel": "User", "destVertexLabel": "User" } -{ "direction": "RIGHT_TO_LEFT", "edgeLabel": "FRIENDS_WITH", "edgeDetail": { "ElementLabel": "FRIENDS_WITH", "EdgeDirection": "RIGHT_TO_LEFT", "SourceKey": [ 4 ], "DestinationKey": [ 5 ] }, "n1Label": "User", "n2Label": "User", "sourceVertexLabel": "User", "destVertexLabel": "User" } -{ "direction": "RIGHT_TO_LEFT", "edgeLabel": "FRIENDS_WITH", "edgeDetail": { "ElementLabel": "FRIENDS_WITH", "EdgeDirection": "RIGHT_TO_LEFT", "SourceKey": [ 5 ], "DestinationKey": [ 6 ] }, "n1Label": "User", "n2Label": "User", "sourceVertexLabel": "User", "destVertexLabel": "User" } -{ "direction": "RIGHT_TO_LEFT", "edgeLabel": "MADE_BY", "edgeDetail": { "ElementLabel": "MADE_BY", "EdgeDirection": "RIGHT_TO_LEFT", "SourceKey": [ "A" ], "DestinationKey": [ 1 ] }, "n1Label": "User", "n2Label": "Review", "sourceVertexLabel": "Review", "destVertexLabel": "User" } -{ "direction": "RIGHT_TO_LEFT", "edgeLabel": "MADE_BY", "edgeDetail": { "ElementLabel": "MADE_BY", "EdgeDirection": "RIGHT_TO_LEFT", "SourceKey": [ "B" ], "DestinationKey": [ 2 ] }, "n1Label": "User", "n2Label": "Review", "sourceVertexLabel": "Review", "destVertexLabel": "User" } \ No newline at end of file +{ "direction": "LEFT_TO_RIGHT", "edgeLabel": "FRIENDS_WITH", "edgeDetail": { "ElementLabel": "FRIENDS_WITH", "EdgeDirection": "LEFT_TO_RIGHT", "SourceKey": [ 1 ], "DestinationKey": [ 2 ] }, "n1Label": "User", "n2Label": "User", "sourceElementLabel": "User", "destElementLabel": "User" } +{ "direction": "LEFT_TO_RIGHT", "edgeLabel": "FRIENDS_WITH", "edgeDetail": { "ElementLabel": "FRIENDS_WITH", "EdgeDirection": "LEFT_TO_RIGHT", "SourceKey": [ 2 ], "DestinationKey": [ 3 ] }, "n1Label": "User", "n2Label": "User", "sourceElementLabel": "User", "destElementLabel": "User" } +{ "direction": "LEFT_TO_RIGHT", "edgeLabel": "FRIENDS_WITH", "edgeDetail": { "ElementLabel": "FRIENDS_WITH", "EdgeDirection": "LEFT_TO_RIGHT", "SourceKey": [ 3 ], "DestinationKey": [ 4 ] }, "n1Label": "User", "n2Label": "User", "sourceElementLabel": "User", "destElementLabel": "User" } +{ "direction": "LEFT_TO_RIGHT", "edgeLabel": "FRIENDS_WITH", "edgeDetail": { "ElementLabel": "FRIENDS_WITH", "EdgeDirection": "LEFT_TO_RIGHT", "SourceKey": [ 4 ], "DestinationKey": [ 5 ] }, "n1Label": "User", "n2Label": "User", "sourceElementLabel": "User", "destElementLabel": "User" } +{ "direction": "LEFT_TO_RIGHT", "edgeLabel": "FRIENDS_WITH", "edgeDetail": { "ElementLabel": "FRIENDS_WITH", "EdgeDirection": "LEFT_TO_RIGHT", "SourceKey": [ 5 ], "DestinationKey": [ 6 ] }, "n1Label": "User", "n2Label": "User", "sourceElementLabel": "User", "destElementLabel": "User" } +{ "direction": "LEFT_TO_RIGHT", "edgeLabel": "MADE_BY", "edgeDetail": { "ElementLabel": "MADE_BY", "EdgeDirection": "LEFT_TO_RIGHT", "SourceKey": [ "A" ], "DestinationKey": [ 1 ] }, "n1Label": "Review", "n2Label": "User", "sourceElementLabel": "Review", "destElementLabel": "User" } +{ "direction": "LEFT_TO_RIGHT", "edgeLabel": "MADE_BY", "edgeDetail": { "ElementLabel": "MADE_BY", "EdgeDirection": "LEFT_TO_RIGHT", "SourceKey": [ "B" ], "DestinationKey": [ 2 ] }, "n1Label": "Review", "n2Label": "User", "sourceElementLabel": "Review", "destElementLabel": "User" } +{ "direction": "RIGHT_TO_LEFT", "edgeLabel": "FRIENDS_WITH", "edgeDetail": { "ElementLabel": "FRIENDS_WITH", "EdgeDirection": "RIGHT_TO_LEFT", "SourceKey": [ 1 ], "DestinationKey": [ 2 ] }, "n1Label": "User", "n2Label": "User", "sourceElementLabel": "User", "destElementLabel": "User" } +{ "direction": "RIGHT_TO_LEFT", "edgeLabel": "FRIENDS_WITH", "edgeDetail": { "ElementLabel": "FRIENDS_WITH", "EdgeDirection": "RIGHT_TO_LEFT", "SourceKey": [ 2 ], "DestinationKey": [ 3 ] }, "n1Label": "User", "n2Label": "User", "sourceElementLabel": "User", "destElementLabel": "User" } +{ "direction": "RIGHT_TO_LEFT", "edgeLabel": "FRIENDS_WITH", "edgeDetail": { "ElementLabel": "FRIENDS_WITH", "EdgeDirection": "RIGHT_TO_LEFT", "SourceKey": [ 3 ], "DestinationKey": [ 4 ] }, "n1Label": "User", "n2Label": "User", "sourceElementLabel": "User", "destElementLabel": "User" } +{ "direction": "RIGHT_TO_LEFT", "edgeLabel": "FRIENDS_WITH", "edgeDetail": { "ElementLabel": "FRIENDS_WITH", "EdgeDirection": "RIGHT_TO_LEFT", "SourceKey": [ 4 ], "DestinationKey": [ 5 ] }, "n1Label": "User", "n2Label": "User", "sourceElementLabel": "User", "destElementLabel": "User" } +{ "direction": "RIGHT_TO_LEFT", "edgeLabel": "FRIENDS_WITH", "edgeDetail": { "ElementLabel": "FRIENDS_WITH", "EdgeDirection": "RIGHT_TO_LEFT", "SourceKey": [ 5 ], "DestinationKey": [ 6 ] }, "n1Label": "User", "n2Label": "User", "sourceElementLabel": "User", "destElementLabel": "User" } +{ "direction": "RIGHT_TO_LEFT", "edgeLabel": "MADE_BY", "edgeDetail": { "ElementLabel": "MADE_BY", "EdgeDirection": "RIGHT_TO_LEFT", "SourceKey": [ "A" ], "DestinationKey": [ 1 ] }, "n1Label": "User", "n2Label": "Review", "sourceElementLabel": "Review", "destElementLabel": "User" } +{ "direction": "RIGHT_TO_LEFT", "edgeLabel": "MADE_BY", "edgeDetail": { "ElementLabel": "MADE_BY", "EdgeDirection": "RIGHT_TO_LEFT", "SourceKey": [ "B" ], "DestinationKey": [ 2 ] }, "n1Label": "User", "n2Label": "Review", "sourceElementLabel": "Review", "destElementLabel": "User" } \ No newline at end of file
diff --git a/asterix-graphix/src/test/resources/runtimets/results/graphix/same-edge-label/same-edge-label.3.adm b/asterix-graphix/src/test/resources/runtimets/results/graphix/same-edge-label/same-edge-label.3.adm new file mode 100644 index 0000000..42acc6b --- /dev/null +++ b/asterix-graphix/src/test/resources/runtimets/results/graphix/same-edge-label/same-edge-label.3.adm
@@ -0,0 +1,10 @@ +{ "u_record": { "review_id": "A" }, "v_user_id": 1 } +{ "u_record": { "review_id": "B" }, "v_user_id": 1 } +{ "u_record": { "review_id": "C" }, "v_user_id": 1 } +{ "u_record": { "review_id": "D" }, "v_user_id": 2 } +{ "u_record": { "review_id": "E" }, "v_user_id": 3 } +{ "u_record": { "review_id": "F" }, "v_user_id": 4 } +{ "u_record": { "user_id": 1 }, "v_user_id": 2 } +{ "u_record": { "user_id": 1 }, "v_user_id": 3 } +{ "u_record": { "user_id": 2 }, "v_user_id": 3 } +{ "u_record": { "user_id": 3 }, "v_user_id": 4 } \ No newline at end of file
diff --git a/asterix-graphix/src/test/resources/runtimets/results/graphix/inference-resolution/inference-resolution.3.adm b/asterix-graphix/src/test/resources/runtimets/results/graphix/schema-resolution/schema-resolution.3.adm similarity index 100% rename from asterix-graphix/src/test/resources/runtimets/results/graphix/inference-resolution/inference-resolution.3.adm rename to asterix-graphix/src/test/resources/runtimets/results/graphix/schema-resolution/schema-resolution.3.adm
diff --git a/asterix-graphix/src/test/resources/runtimets/results/graphix/inference-resolution/inference-resolution.4.adm b/asterix-graphix/src/test/resources/runtimets/results/graphix/schema-resolution/schema-resolution.4.adm similarity index 100% rename from asterix-graphix/src/test/resources/runtimets/results/graphix/inference-resolution/inference-resolution.4.adm rename to asterix-graphix/src/test/resources/runtimets/results/graphix/schema-resolution/schema-resolution.4.adm
diff --git a/asterix-graphix/src/test/resources/runtimets/results/graphix/inference-resolution/inference-resolution.5.adm b/asterix-graphix/src/test/resources/runtimets/results/graphix/schema-resolution/schema-resolution.5.adm similarity index 100% rename from asterix-graphix/src/test/resources/runtimets/results/graphix/inference-resolution/inference-resolution.5.adm rename to asterix-graphix/src/test/resources/runtimets/results/graphix/schema-resolution/schema-resolution.5.adm
diff --git a/asterix-graphix/src/test/resources/runtimets/results/graphix/inference-resolution/inference-resolution.6.adm b/asterix-graphix/src/test/resources/runtimets/results/graphix/schema-resolution/schema-resolution.6.adm similarity index 100% rename from asterix-graphix/src/test/resources/runtimets/results/graphix/inference-resolution/inference-resolution.6.adm rename to asterix-graphix/src/test/resources/runtimets/results/graphix/schema-resolution/schema-resolution.6.adm
diff --git a/asterix-graphix/src/test/resources/runtimets/results/graphix/schema-resolution/schema-resolution.7.adm b/asterix-graphix/src/test/resources/runtimets/results/graphix/schema-resolution/schema-resolution.7.adm new file mode 100644 index 0000000..1588f9f --- /dev/null +++ b/asterix-graphix/src/test/resources/runtimets/results/graphix/schema-resolution/schema-resolution.7.adm
@@ -0,0 +1 @@ +{ "u_label": "User", "n_label": "User" } \ No newline at end of file
diff --git a/asterix-graphix/src/test/resources/runtimets/results/graphix/schema-resolution/schema-resolution.8.adm b/asterix-graphix/src/test/resources/runtimets/results/graphix/schema-resolution/schema-resolution.8.adm new file mode 100644 index 0000000..2677371 --- /dev/null +++ b/asterix-graphix/src/test/resources/runtimets/results/graphix/schema-resolution/schema-resolution.8.adm
@@ -0,0 +1,2 @@ +{ "u_label": "User", "r_label": "User" } +{ "u_label": "User", "r_label": "User" } \ No newline at end of file
diff --git a/asterix-graphix/src/test/resources/runtimets/results/graphix/scope-checking/scope-checking.8.adm b/asterix-graphix/src/test/resources/runtimets/results/graphix/scope-checking/scope-checking.8.adm deleted file mode 100644 index 9b24dd0..0000000 --- a/asterix-graphix/src/test/resources/runtimets/results/graphix/scope-checking/scope-checking.8.adm +++ /dev/null
@@ -1,6 +0,0 @@ -{ "user_id": 1, "review_id": "A" } -{ "user_id": 1, "review_id": "B" } -{ "user_id": 1, "review_id": "C" } -{ "user_id": 2, "review_id": "D" } -{ "user_id": 3, "review_id": "E" } -{ "user_id": 4, "review_id": "F" } \ No newline at end of file
diff --git a/asterix-graphix/src/test/resources/runtimets/results/graphix/scope-checking/scope-checking.7.adm b/asterix-graphix/src/test/resources/runtimets/results/graphix/scope-validation/scope-validation.3.adm similarity index 100% rename from asterix-graphix/src/test/resources/runtimets/results/graphix/scope-checking/scope-checking.7.adm rename to asterix-graphix/src/test/resources/runtimets/results/graphix/scope-validation/scope-validation.3.adm
diff --git a/asterix-graphix/src/test/resources/runtimets/results/graphix/scope-checking/scope-checking.7.adm b/asterix-graphix/src/test/resources/runtimets/results/graphix/scope-validation/scope-validation.4.adm similarity index 100% copy from asterix-graphix/src/test/resources/runtimets/results/graphix/scope-checking/scope-checking.7.adm copy to asterix-graphix/src/test/resources/runtimets/results/graphix/scope-validation/scope-validation.4.adm
diff --git a/asterix-graphix/src/test/resources/runtimets/results/graphix/simple-1-edge/simple-1-edge.6.adm b/asterix-graphix/src/test/resources/runtimets/results/graphix/simple-1-edge/simple-1-edge.6.adm new file mode 100644 index 0000000..2a5d87f --- /dev/null +++ b/asterix-graphix/src/test/resources/runtimets/results/graphix/simple-1-edge/simple-1-edge.6.adm
@@ -0,0 +1,2 @@ +{ "user_id": 1, "review_id": "A" } +{ "user_id": 1, "review_id": "B" } \ No newline at end of file
diff --git a/asterix-graphix/src/test/resources/runtimets/results/graphix/scope-checking/scope-checking.6.adm b/asterix-graphix/src/test/resources/runtimets/results/graphix/variable-sub-path/variable-sub-path.6.adm similarity index 100% rename from asterix-graphix/src/test/resources/runtimets/results/graphix/scope-checking/scope-checking.6.adm rename to asterix-graphix/src/test/resources/runtimets/results/graphix/variable-sub-path/variable-sub-path.6.adm
diff --git a/asterix-graphix/src/test/resources/runtimets/results/graphix/scope-checking/scope-checking.5.adm b/asterix-graphix/src/test/resources/runtimets/results/graphix/variable-sub-path/variable-sub-path.7.adm similarity index 100% rename from asterix-graphix/src/test/resources/runtimets/results/graphix/scope-checking/scope-checking.5.adm rename to asterix-graphix/src/test/resources/runtimets/results/graphix/variable-sub-path/variable-sub-path.7.adm
diff --git a/asterix-graphix/src/test/resources/runtimets/testsuite.xml b/asterix-graphix/src/test/resources/runtimets/testsuite.xml index a5ea9fc..50abf0c 100644 --- a/asterix-graphix/src/test/resources/runtimets/testsuite.xml +++ b/asterix-graphix/src/test/resources/runtimets/testsuite.xml
@@ -20,6 +20,13 @@ ResultOffsetPath="results" QueryOffsetPath="queries" QueryFileExtension=".sqlpp"> + <test-group name="correlated-vertex-join"> + <test-case FilePath="graphix"> + <compilation-unit name="correlated-vertex-join"> + <output-dir compare="Text">correlated-vertex-join</output-dir> + </compilation-unit> + </test-case> + </test-group> <test-group name="create-drop-error"> <test-case FilePath="graphix"> <compilation-unit name="create-drop-error"> @@ -42,10 +49,12 @@ </test-case> </test-group> <test-group name="dangling-vertices"> - <test-case FilePath="graphix" check-warnings="true"> + <!--<test-case FilePath="graphix" check-warnings="true">--> + <test-case FilePath="graphix" check-warnings="false"> <compilation-unit name="dangling-vertices"> <output-dir compare="Text">dangling-vertices</output-dir> - <expected-warn>Potential disconnected pattern encountered! A CROSS-JOIN has been introduced.</expected-warn> + <!--<expected-warn>Disconnected pattern encountered! A CROSS-JOIN may been introduced.</expected-warn>--> + <!--<expected-warn>Encountered a cross product join</expected-warn>--> </compilation-unit> </test-case> </test-group> @@ -70,13 +79,6 @@ </compilation-unit> </test-case> </test-group> - <test-group name="inference-resolution"> - <test-case FilePath="graphix" check-warnings="true"> - <compilation-unit name="inference-resolution"> - <output-dir compare="Text">inference-resolution</output-dir> - </compilation-unit> - </test-case> - </test-group> <test-group name="left-match"> <test-case FilePath="graphix"> <compilation-unit name="left-match"> @@ -99,6 +101,7 @@ <expected-error>Cannot resolve alias reference for undefined identifier invalidVariable</expected-error> <expected-error>Cannot resolve alias reference for undefined identifier invalidVariable</expected-error> <expected-error>Cannot resolve alias reference for undefined identifier invalidVariable</expected-error> + <expected-error>Encountered graph element that does not conform the queried graph schema!</expected-error> </compilation-unit> </test-case> </test-group> @@ -109,10 +112,17 @@ </compilation-unit> </test-case> </test-group> - <test-group name="scope-checking"> + <test-group name="schema-resolution"> + <test-case FilePath="graphix" check-warnings="true"> + <compilation-unit name="schema-resolution"> + <output-dir compare="Text">schema-resolution</output-dir> + </compilation-unit> + </test-case> + </test-group> + <test-group name="scope-validation"> <test-case FilePath="graphix"> - <compilation-unit name="scope-checking"> - <output-dir compare="Text">scope-checking</output-dir> + <compilation-unit name="scope-validation"> + <output-dir compare="Text">scope-validation</output-dir> </compilation-unit> </test-case> </test-group>