From 5a2cd027dcb57ccf7c3c8515907df5ba91604dac Mon Sep 17 00:00:00 2001 From: Tobias Weber Date: Wed, 10 Apr 2024 22:50:24 +0200 Subject: [PATCH] Add support for converting from PolyAlg to various ParamTypes and AlgNodes --- core/src/main/codegen/PolyAlgParser.jj | 14 +- .../polypheny/db/algebra/AbstractAlgNode.java | 21 +- .../logical/relational/LogicalRelFilter.java | 14 +- .../relational/LogicalRelIntersect.java | 5 + .../logical/relational/LogicalRelJoin.java | 19 +- .../logical/relational/LogicalRelMinus.java | 5 + .../logical/relational/LogicalRelProject.java | 9 +- .../logical/relational/LogicalRelScan.java | 6 + .../logical/relational/LogicalRelSort.java | 11 +- .../logical/relational/LogicalRelUnion.java | 5 + .../algebra/polyalg/PolyAlgDeclaration.java | 25 +- .../db/algebra/polyalg/PolyAlgRegistry.java | 37 +-- .../db/algebra/polyalg/PolyAlgUtils.java | 13 +- .../algebra/polyalg/arguments/BooleanArg.java | 13 +- .../polyalg/arguments/CollationArg.java | 20 +- .../polyalg/arguments/CorrelationArg.java | 10 +- .../algebra/polyalg/arguments/EntityArg.java | 10 +- .../db/algebra/polyalg/arguments/EnumArg.java | 6 +- .../db/algebra/polyalg/arguments/ListArg.java | 10 + .../polyalg/arguments/PolyAlgArgs.java | 28 ++ .../db/algebra/polyalg/arguments/RexArg.java | 1 + .../polyalg/parser/PolyAlgToAlgConverter.java | 247 +++++++++++++----- .../parser/nodes/PolyAlgExpression.java | 58 +++- .../polyalg/parser/nodes/PolyAlgLiteral.java | 54 +++- .../org/polypheny/db/plan/AlgOptUtil.java | 29 -- .../org/polypheny/db/tools/AlgBuilder.java | 4 + .../db/processing/AbstractQueryProcessor.java | 54 ++++ 27 files changed, 550 insertions(+), 178 deletions(-) diff --git a/core/src/main/codegen/PolyAlgParser.jj b/core/src/main/codegen/PolyAlgParser.jj index 677f96bae2..02d2bf29d7 100644 --- a/core/src/main/codegen/PolyAlgParser.jj +++ b/core/src/main/codegen/PolyAlgParser.jj @@ -134,6 +134,9 @@ TOKEN : | | +| | )> +| <#TRUE: "TRUE"> +| <#FALSE: "FALSE"> | | ) ( | | )*> | )+ (".")? ()*> | <#LETTER: ["_","a"-"z","A"-"Z","ö", "Ö", "ä", "Ä", "ü", "Ü", "à", "À", "ç","Ç", "á", "Á", "è", "È","í","Í", "î", "Î","ó","Ó","ò", "ô", "Ô", "Ò" , "í", "Í", "ë", "Ë", "â", "Â", "ï", "Ï", "é", "É", "ñ", "Ñ", "ß"] > @@ -312,6 +315,8 @@ PolyAlgLiteral Literal() : { Token t; boolean isNumber = false; + boolean isBoolean = false; + boolean isQuoted = false; } { @@ -322,11 +327,13 @@ PolyAlgLiteral Literal() : | t = | - t = + t = {isQuoted = true;} | t = + | + t = {isBoolean = true;} ) - {return new PolyAlgLiteral(t.image, isNumber, getPos());} + {return new PolyAlgLiteral(t.image, isNumber, isQuoted, isBoolean, getPos());} } @@ -350,8 +357,7 @@ String AliasName() : ( name = OpName() | - t = - {name = t.image;} + t = {name = t.image;} ) {return name;} } diff --git a/core/src/main/java/org/polypheny/db/algebra/AbstractAlgNode.java b/core/src/main/java/org/polypheny/db/algebra/AbstractAlgNode.java index 3de4150fd1..0a440dd430 100644 --- a/core/src/main/java/org/polypheny/db/algebra/AbstractAlgNode.java +++ b/core/src/main/java/org/polypheny/db/algebra/AbstractAlgNode.java @@ -40,18 +40,15 @@ import java.io.StringWriter; import java.util.ArrayList; 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.concurrent.atomic.AtomicInteger; import lombok.Getter; import lombok.Setter; import lombok.experimental.SuperBuilder; -import org.checkerframework.checker.units.qual.A; import org.polypheny.db.algebra.constant.ExplainLevel; import org.polypheny.db.algebra.core.CorrelationId; +import org.polypheny.db.algebra.core.SetOp; import org.polypheny.db.algebra.externalize.AlgWriterImpl; import org.polypheny.db.algebra.logical.relational.LogicalRelProject; import org.polypheny.db.algebra.metadata.AlgMetadataQuery; @@ -62,7 +59,6 @@ import org.polypheny.db.algebra.polyalg.PolyAlgUtils; import org.polypheny.db.algebra.polyalg.arguments.PolyAlgArgs; import org.polypheny.db.algebra.type.AlgDataType; -import org.polypheny.db.algebra.type.AlgDataTypeField; import org.polypheny.db.catalog.entity.Entity; import org.polypheny.db.plan.AlgCluster; import org.polypheny.db.plan.AlgOptCost; @@ -360,7 +356,10 @@ public AlgWriter explainTerms( AlgWriter pw ) { public void buildPolyAlgebra( StringBuilder sb, String prefix ) { final String INDENT = " "; String nextPrefix = prefix == null ? null : prefix + INDENT; - List inputFieldNames = PolyAlgUtils.uniquifiedInputFieldNames( this ); + boolean makeFieldsUnique = !(this instanceof SetOp); // set operations like UNION require duplicate field names + List inputFieldNames = makeFieldsUnique ? + PolyAlgUtils.uniquifiedInputFieldNames( this ) : + PolyAlgUtils.getInputFieldNamesList( this ); PolyAlgDeclaration decl = getPolyAlgDeclaration(); sb.append( prefix == null ? "" : prefix ).append( decl.opName ); @@ -374,7 +373,9 @@ public void buildPolyAlgebra( StringBuilder sb, String prefix ) { sb.append( "(\n" ); int inputIdx = 0; for ( AlgNode child : getInputs() ) { - List projections = PolyAlgUtils.getAuxProjections( child, inputFieldNames, inputIdx ); + List projections = makeFieldsUnique ? + PolyAlgUtils.getAuxProjections( child, inputFieldNames, inputIdx ) : + List.of(); inputIdx += child.getTupleType().getFieldCount(); if ( projections.isEmpty() ) { @@ -382,8 +383,8 @@ public void buildPolyAlgebra( StringBuilder sb, String prefix ) { } else { sb.append( nextPrefix ) .append( PolyAlgRegistry.getDeclaration( LogicalRelProject.class ).opName ).append( "#" ) // TODO: select Project depending on data model, logical / physical - .append( "[" ).append( PolyAlgUtils.joinMultiValued( projections, true ) ).append( "]") - .append("(\n" ); + .append( "[" ).append( PolyAlgUtils.joinMultiValued( projections, true ) ).append( "]" ) + .append( "(\n" ); child.buildPolyAlgebra( sb, nextPrefix == null ? null : nextPrefix + INDENT ); sb.append( ")" ); } @@ -406,7 +407,7 @@ public void buildPolyAlgebra( StringBuilder sb, String prefix ) { */ @Override public PolyAlgDeclaration getPolyAlgDeclaration() { - return PolyAlgRegistry.getDeclaration( getClass(), getInputs().size() ); + return PolyAlgRegistry.getDeclaration( getClass(), getModel(), getInputs().size() ); } diff --git a/core/src/main/java/org/polypheny/db/algebra/logical/relational/LogicalRelFilter.java b/core/src/main/java/org/polypheny/db/algebra/logical/relational/LogicalRelFilter.java index abcbe32fe7..9b91b3991b 100644 --- a/core/src/main/java/org/polypheny/db/algebra/logical/relational/LogicalRelFilter.java +++ b/core/src/main/java/org/polypheny/db/algebra/logical/relational/LogicalRelFilter.java @@ -35,6 +35,7 @@ import com.google.common.collect.ImmutableSet; +import java.util.List; import java.util.Objects; import lombok.Getter; import org.polypheny.db.algebra.AlgCollationTraitDef; @@ -48,6 +49,8 @@ import org.polypheny.db.algebra.metadata.AlgMdCollation; import org.polypheny.db.algebra.metadata.AlgMdDistribution; import org.polypheny.db.algebra.metadata.AlgMetadataQuery; +import org.polypheny.db.algebra.polyalg.arguments.CorrelationArg; +import org.polypheny.db.algebra.polyalg.arguments.ListArg; import org.polypheny.db.algebra.polyalg.arguments.PolyAlgArgs; import org.polypheny.db.algebra.polyalg.arguments.RexArg; import org.polypheny.db.plan.AlgCluster; @@ -104,6 +107,13 @@ public static LogicalRelFilter create( final AlgNode input, RexNode condition, I } + public static LogicalRelFilter create( PolyAlgArgs args, List children, AlgCluster cluster ) { + RexArg condition = args.getArg( "condition", RexArg.class ); + List variables = args.getListArg( "variables", CorrelationArg.class ).map( CorrelationArg::getCorrId ); + return create( children.get( 0 ), condition.getNode(), ImmutableSet.copyOf( variables ) ); + } + + @Override public ImmutableSet getVariablesSet() { return variablesSet; @@ -132,8 +142,8 @@ public AlgWriter explainTerms( AlgWriter pw ) { @Override public PolyAlgArgs collectAttributes() { PolyAlgArgs args = new PolyAlgArgs( getPolyAlgDeclaration() ); - - args.put( 0, new RexArg( condition ) ); + args.put( 0, new RexArg( condition ) ) + .put( "variables", new ListArg<>( variablesSet.asList(), CorrelationArg::new ) ); return args; } diff --git a/core/src/main/java/org/polypheny/db/algebra/logical/relational/LogicalRelIntersect.java b/core/src/main/java/org/polypheny/db/algebra/logical/relational/LogicalRelIntersect.java index eba2016986..4f64446321 100644 --- a/core/src/main/java/org/polypheny/db/algebra/logical/relational/LogicalRelIntersect.java +++ b/core/src/main/java/org/polypheny/db/algebra/logical/relational/LogicalRelIntersect.java @@ -71,6 +71,11 @@ public static LogicalRelIntersect create( List inputs, boolean all ) { } + public static LogicalRelIntersect create( PolyAlgArgs args, List children, AlgCluster cluster ) { + return create( children, args.getArg( "all", BooleanArg.class ).toBool() ); + } + + @Override public LogicalRelIntersect copy( AlgTraitSet traitSet, List inputs, boolean all ) { return new LogicalRelIntersect( getCluster(), traitSet, inputs, all ); diff --git a/core/src/main/java/org/polypheny/db/algebra/logical/relational/LogicalRelJoin.java b/core/src/main/java/org/polypheny/db/algebra/logical/relational/LogicalRelJoin.java index 837a0a52ce..efed15c4ca 100644 --- a/core/src/main/java/org/polypheny/db/algebra/logical/relational/LogicalRelJoin.java +++ b/core/src/main/java/org/polypheny/db/algebra/logical/relational/LogicalRelJoin.java @@ -34,6 +34,8 @@ package org.polypheny.db.algebra.logical.relational; +import java.util.HashSet; +import java.util.List; import java.util.Set; import org.polypheny.db.algebra.AlgNode; import org.polypheny.db.algebra.AlgShuttle; @@ -44,7 +46,9 @@ import org.polypheny.db.algebra.core.relational.RelAlg; import org.polypheny.db.algebra.polyalg.PolyAlgDeclaration.ParamType; import org.polypheny.db.algebra.polyalg.arguments.BooleanArg; +import org.polypheny.db.algebra.polyalg.arguments.CorrelationArg; import org.polypheny.db.algebra.polyalg.arguments.EnumArg; +import org.polypheny.db.algebra.polyalg.arguments.ListArg; import org.polypheny.db.algebra.polyalg.arguments.PolyAlgArgs; import org.polypheny.db.algebra.polyalg.arguments.RexArg; import org.polypheny.db.plan.AlgCluster; @@ -70,7 +74,6 @@ public final class LogicalRelJoin extends Join implements RelAlg { private final boolean semiJoinDone; - /** * Creates a LogicalJoin. * @@ -122,6 +125,15 @@ public static LogicalRelJoin create( AlgNode left, AlgNode right, RexNode condit } + public static LogicalRelJoin create( PolyAlgArgs args, List children, AlgCluster cluster ) { + RexArg condition = args.getArg( "condition", RexArg.class ); + EnumArg type = args.getEnumArg( "type", JoinAlgType.class ); + List variables = args.getListArg( "variables", CorrelationArg.class ).map( CorrelationArg::getCorrId ); + BooleanArg semiJoinDone = args.getArg( "semiJoinDone", BooleanArg.class ); + return create( children.get( 0 ), children.get( 1 ), condition.getNode(), new HashSet<>( variables ), type.getArg(), semiJoinDone.toBool() ); + } + + @Override public LogicalRelJoin copy( AlgTraitSet traitSet, RexNode conditionExpr, AlgNode left, AlgNode right, JoinAlgType joinType, boolean semiJoinDone ) { assert traitSet.containsIfApplicable( Convention.NONE ); @@ -147,14 +159,17 @@ public boolean isSemiJoinDone() { return semiJoinDone; } + @Override public PolyAlgArgs collectAttributes() { PolyAlgArgs args = new PolyAlgArgs( getPolyAlgDeclaration() ); - args.put( 0, new RexArg( condition, true ) ) + args.put( 0, new RexArg( condition ) ) .put( "type", new EnumArg<>( joinType, ParamType.JOIN_TYPE_ENUM ) ) + .put( "variables", new ListArg<>( variablesSet.asList(), CorrelationArg::new ) ) .put( "semiJoinDone", new BooleanArg( semiJoinDone ) ); return args; } + } diff --git a/core/src/main/java/org/polypheny/db/algebra/logical/relational/LogicalRelMinus.java b/core/src/main/java/org/polypheny/db/algebra/logical/relational/LogicalRelMinus.java index 657d1d9bc8..e2ae3e9504 100644 --- a/core/src/main/java/org/polypheny/db/algebra/logical/relational/LogicalRelMinus.java +++ b/core/src/main/java/org/polypheny/db/algebra/logical/relational/LogicalRelMinus.java @@ -80,6 +80,11 @@ public static LogicalRelMinus create( List inputs, boolean all ) { } + public static LogicalRelMinus create( PolyAlgArgs args, List children, AlgCluster cluster ) { + return create( children, args.getArg( "all", BooleanArg.class ).toBool() ); + } + + @Override public LogicalRelMinus copy( AlgTraitSet traitSet, List inputs, boolean all ) { assert traitSet.containsIfApplicable( Convention.NONE ); diff --git a/core/src/main/java/org/polypheny/db/algebra/logical/relational/LogicalRelProject.java b/core/src/main/java/org/polypheny/db/algebra/logical/relational/LogicalRelProject.java index 2f2a7b6827..c1a340bc90 100644 --- a/core/src/main/java/org/polypheny/db/algebra/logical/relational/LogicalRelProject.java +++ b/core/src/main/java/org/polypheny/db/algebra/logical/relational/LogicalRelProject.java @@ -104,12 +104,9 @@ public static LogicalRelProject create( final AlgNode input, final List children) { - System.out.println("Creating AlgNode from LogicalRelProject"); - ListArg projects= args.getArg( 0, ListArg.class ); - - - return create( children.get( 0 ), projects.getArgs().stream().map( RexArg::getNode ).toList(), projects.getAliases() ); + public static LogicalRelProject create( PolyAlgArgs args, List children, AlgCluster cluster ) { + ListArg projects = args.getListArg( 0, RexArg.class ); + return create( children.get( 0 ), projects.map( RexArg::getNode ), projects.map( RexArg::getAlias ) ); } diff --git a/core/src/main/java/org/polypheny/db/algebra/logical/relational/LogicalRelScan.java b/core/src/main/java/org/polypheny/db/algebra/logical/relational/LogicalRelScan.java index ad2897ec90..8f422ce20b 100644 --- a/core/src/main/java/org/polypheny/db/algebra/logical/relational/LogicalRelScan.java +++ b/core/src/main/java/org/polypheny/db/algebra/logical/relational/LogicalRelScan.java @@ -41,6 +41,8 @@ import org.polypheny.db.algebra.AlgNode; import org.polypheny.db.algebra.AlgShuttle; import org.polypheny.db.algebra.core.relational.RelScan; +import org.polypheny.db.algebra.polyalg.arguments.EntityArg; +import org.polypheny.db.algebra.polyalg.arguments.PolyAlgArgs; import org.polypheny.db.catalog.entity.Entity; import org.polypheny.db.plan.AlgCluster; import org.polypheny.db.plan.AlgTraitSet; @@ -126,6 +128,10 @@ public static LogicalRelScan create( AlgCluster cluster, final Entity entity ) { return new LogicalRelScan( cluster, traitSet, entity ); } + public static LogicalRelScan create( PolyAlgArgs args, List children, AlgCluster cluster) { + return create( cluster, args.getArg( 0, EntityArg.class ).getEntity()); + } + } diff --git a/core/src/main/java/org/polypheny/db/algebra/logical/relational/LogicalRelSort.java b/core/src/main/java/org/polypheny/db/algebra/logical/relational/LogicalRelSort.java index e4ec2f57e8..1061873fc3 100644 --- a/core/src/main/java/org/polypheny/db/algebra/logical/relational/LogicalRelSort.java +++ b/core/src/main/java/org/polypheny/db/algebra/logical/relational/LogicalRelSort.java @@ -38,6 +38,7 @@ import org.jetbrains.annotations.Nullable; import org.polypheny.db.algebra.AlgCollation; import org.polypheny.db.algebra.AlgCollationTraitDef; +import org.polypheny.db.algebra.AlgCollations; import org.polypheny.db.algebra.AlgNode; import org.polypheny.db.algebra.AlgShuttle; import org.polypheny.db.algebra.core.Sort; @@ -87,6 +88,14 @@ public static AlgNode create( AlgNode input, List fieldExps, AlgCollati } + public static LogicalRelSort create( PolyAlgArgs args, List children, AlgCluster cluster ) { + ListArg collations = args.getListArg( "sort", CollationArg.class ); + RexArg limit = args.getArg( "limit", RexArg.class ); + RexArg offset = args.getArg( "offset", RexArg.class ); + return create( children.get( 0 ), AlgCollations.of( collations.map( CollationArg::getColl ) ), offset.getNode(), limit.getNode() ); + } + + @Override public Sort copy( AlgTraitSet traitSet, AlgNode newInput, AlgCollation newCollation, ImmutableList fieldExps, RexNode offset, RexNode fetch ) { return new LogicalRelSort( getCluster(), traitSet, newInput, newCollation, fieldExps, offset, fetch ); @@ -105,7 +114,7 @@ public PolyAlgArgs collectAttributes() { PolyAlgArg collArg = new ListArg<>( collation.getFieldCollations(), - c -> new CollationArg( c ), + CollationArg::new, args.getDecl().canUnpackValues() ); args.put( "sort", collArg ) diff --git a/core/src/main/java/org/polypheny/db/algebra/logical/relational/LogicalRelUnion.java b/core/src/main/java/org/polypheny/db/algebra/logical/relational/LogicalRelUnion.java index 773269b445..f4bd95b7c7 100644 --- a/core/src/main/java/org/polypheny/db/algebra/logical/relational/LogicalRelUnion.java +++ b/core/src/main/java/org/polypheny/db/algebra/logical/relational/LogicalRelUnion.java @@ -80,6 +80,11 @@ public static LogicalRelUnion create( List inputs, boolean all ) { } + public static LogicalRelUnion create( PolyAlgArgs args, List children, AlgCluster cluster ) { + return create( children, args.getArg( "all", BooleanArg.class ).toBool() ); + } + + @Override public LogicalRelUnion copy( AlgTraitSet traitSet, List inputs, boolean all ) { assert traitSet.containsIfApplicable( Convention.NONE ); diff --git a/core/src/main/java/org/polypheny/db/algebra/polyalg/PolyAlgDeclaration.java b/core/src/main/java/org/polypheny/db/algebra/polyalg/PolyAlgDeclaration.java index 460bec0ba7..9713161f87 100644 --- a/core/src/main/java/org/polypheny/db/algebra/polyalg/PolyAlgDeclaration.java +++ b/core/src/main/java/org/polypheny/db/algebra/polyalg/PolyAlgDeclaration.java @@ -21,13 +21,13 @@ import java.util.HashSet; import java.util.List; import java.util.Set; -import java.util.function.BiFunction; import java.util.stream.Collectors; import lombok.Builder; import lombok.Data; import lombok.Getter; import lombok.NonNull; import lombok.Singular; +import org.apache.commons.lang3.function.TriFunction; import org.polypheny.db.algebra.AlgNode; import org.polypheny.db.algebra.polyalg.arguments.AggArg; import org.polypheny.db.algebra.polyalg.arguments.AnyArg; @@ -42,32 +42,37 @@ import org.polypheny.db.algebra.polyalg.arguments.PolyAlgArg; import org.polypheny.db.algebra.polyalg.arguments.PolyAlgArgs; import org.polypheny.db.algebra.polyalg.arguments.RexArg; +import org.polypheny.db.catalog.logistic.DataModel; +import org.polypheny.db.plan.AlgCluster; public class PolyAlgDeclaration { public final String opName; public final ImmutableList opAliases; - public final int numInputs; // -1 if arbitrary amount is allowed + public final DataModel model; + private final int numInputs; // -1 if arbitrary amount is allowed public final ImmutableList opTags; public final ImmutableList posParams; public final ImmutableList kwParams; private final ImmutableMap paramLookup; - private final BiFunction, AlgNode> creator; + private final TriFunction, AlgCluster, AlgNode> creator; @Builder public PolyAlgDeclaration( @NonNull String opName, @Singular ImmutableList opAliases, - BiFunction, AlgNode> creator, + @NonNull DataModel model, + TriFunction, AlgCluster, AlgNode> creator, @Singular ImmutableList opTags, int numInputs, @Singular ImmutableList params ) { this.opName = opName; this.opAliases = (opAliases != null) ? opAliases : ImmutableList.of(); + this.model = model; this.creator = creator; this.numInputs = numInputs; this.opTags = (opTags != null) ? opTags : ImmutableList.of(); @@ -96,8 +101,8 @@ public PolyAlgDeclaration( } - public AlgNode createNode( PolyAlgArgs args, List children ) { - return creator.apply( args, children ); + public AlgNode createNode( PolyAlgArgs args, List children, AlgCluster cluster ) { + return creator.apply( args, children, cluster ); } @@ -162,6 +167,11 @@ public boolean containsParam( Parameter p ) { } + public boolean supportsNumberOfChildren( int n ) { + return numInputs == -1 || numInputs == n; + } + + /** * Depending on whether a defaultValue is specified, a Parameter can result in two types of corresponding arguments: *
    @@ -279,9 +289,6 @@ public enum ParamType { public enum OperatorTag { - REL, - DOC, - LPG, LOGICAL, PHYSICAL, ALLOCATION, diff --git a/core/src/main/java/org/polypheny/db/algebra/polyalg/PolyAlgRegistry.java b/core/src/main/java/org/polypheny/db/algebra/polyalg/PolyAlgRegistry.java index c3fd028a01..90f53b1f3e 100644 --- a/core/src/main/java/org/polypheny/db/algebra/polyalg/PolyAlgRegistry.java +++ b/core/src/main/java/org/polypheny/db/algebra/polyalg/PolyAlgRegistry.java @@ -28,10 +28,10 @@ import org.polypheny.db.algebra.polyalg.PolyAlgDeclaration.ParamType; import org.polypheny.db.algebra.polyalg.PolyAlgDeclaration.Parameter; import org.polypheny.db.algebra.polyalg.arguments.BooleanArg; -import org.polypheny.db.algebra.polyalg.arguments.CollationArg; import org.polypheny.db.algebra.polyalg.arguments.EnumArg; import org.polypheny.db.algebra.polyalg.arguments.ListArg; import org.polypheny.db.algebra.polyalg.arguments.RexArg; +import org.polypheny.db.catalog.logistic.DataModel; public class PolyAlgRegistry { @@ -46,25 +46,29 @@ public class PolyAlgRegistry { private static void populateDeclarationsMap() { - ImmutableList logRelTags = ImmutableList.of( OperatorTag.LOGICAL, OperatorTag.REL ); + ImmutableList logRelTags = ImmutableList.of( OperatorTag.LOGICAL ); declarations.put( LogicalRelProject.class, PolyAlgDeclaration.builder() - .creator( LogicalRelProject::create ) - .opName( "PROJECT" ).opAlias( "P" ).numInputs( 1 ).opTags( logRelTags ) + .creator( LogicalRelProject::create ).model( DataModel.RELATIONAL ) + .opName( "PROJECT" ).opAliases( List.of( "P", "PROJECT#" ) ).numInputs( 1 ).opTags( logRelTags ) .param( Parameter.builder().name( "projects" ).isMultiValued( true ).type( ParamType.SIMPLE_REX ).build() ) .build() ); declarations.put( RelScan.class, PolyAlgDeclaration.builder() + .creator( LogicalRelScan::create ).model( DataModel.RELATIONAL ) .opName( "SCAN" ).numInputs( 0 ).opTags( logRelTags ) .param( Parameter.builder().name( "entity" ).type( ParamType.ENTITY ).build() ) .build() ); declarations.put( LogicalRelFilter.class, PolyAlgDeclaration.builder() + .creator( LogicalRelFilter::create ).model( DataModel.RELATIONAL ) .opName( "FILTER" ).numInputs( 1 ).opTags( logRelTags ) .param( Parameter.builder().name( "condition" ).type( ParamType.SIMPLE_REX ).build() ) + .param( Parameter.builder().name( "variables" ).type( ParamType.CORR_ID ).isMultiValued( true ).defaultValue( ListArg.EMPTY ).build() ) .build() ); declarations.put( LogicalRelAggregate.class, PolyAlgDeclaration.builder() + .model( DataModel.RELATIONAL ) .opName( "AGG" ).numInputs( 1 ).opTags( logRelTags ) .param( Parameter.builder().name( "group" ).type( ParamType.FIELD ).isMultiValued( true ).defaultValue( ListArg.EMPTY ).build() ) // select count(*) has no group .param( Parameter.builder().name( "groups" ).type( ParamType.ANY ).isMultiValued( true ).defaultValue( ListArg.EMPTY ).build() ) @@ -72,48 +76,48 @@ private static void populateDeclarationsMap() { .build() ); declarations.put( LogicalRelMinus.class, PolyAlgDeclaration.builder() + .creator( LogicalRelMinus::create ).model( DataModel.RELATIONAL ) .opName( "MINUS" ).numInputs( 2 ).opTags( logRelTags ) .param( Parameter.builder().name( "all" ).type( ParamType.BOOLEAN ).defaultValue( BooleanArg.FALSE ).build() ) .build() ); declarations.put( LogicalRelUnion.class, PolyAlgDeclaration.builder() + .creator( LogicalRelUnion::create ).model( DataModel.RELATIONAL ) .opName( "UNION" ).numInputs( 2 ).opTags( logRelTags ) .param( Parameter.builder().name( "all" ).type( ParamType.BOOLEAN ).defaultValue( BooleanArg.FALSE ).build() ) .build() ); declarations.put( LogicalRelIntersect.class, PolyAlgDeclaration.builder() + .creator( LogicalRelIntersect::create ).model( DataModel.RELATIONAL ) .opName( "INTERSECT" ).numInputs( 2 ).opTags( logRelTags ) .param( Parameter.builder().name( "all" ).type( ParamType.BOOLEAN ).defaultValue( BooleanArg.FALSE ).build() ) .build() ); declarations.put( LogicalRelSort.class, PolyAlgDeclaration.builder() + .creator( LogicalRelSort::create ).model( DataModel.RELATIONAL ) .opName( "SORT" ).numInputs( 1 ).opTags( logRelTags ) - .param( Parameter.builder().name( "sort" ).aliases( List.of( "collation", "order" ) ).type( ParamType.COLLATION ).defaultValue( CollationArg.NULL ).build() ) + .param( Parameter.builder().name( "sort" ).aliases( List.of( "collation", "order" ) ).type( ParamType.COLLATION ).isMultiValued( true ).defaultValue( ListArg.EMPTY ).build() ) .param( Parameter.builder().name( "limit" ).alias( "fetch" ).type( ParamType.SIMPLE_REX ).defaultValue( RexArg.NULL ).build() ) .param( Parameter.builder().name( "offset" ).type( ParamType.SIMPLE_REX ).defaultValue( RexArg.NULL ).build() ) .build() ); declarations.put( LogicalRelJoin.class, PolyAlgDeclaration.builder() + .creator( LogicalRelJoin::create ).model( DataModel.RELATIONAL ) .opName( "JOIN" ).numInputs( 2 ).opTags( logRelTags ) - .param( Parameter.builder().name( "condition" ).type( ParamType.SIMPLE_REX ).build() ) + .param( Parameter.builder().name( "condition" ).alias( "on" ).type( ParamType.SIMPLE_REX ).build() ) .param( Parameter.builder().name( "type" ).type( ParamType.JOIN_TYPE_ENUM ).defaultValue( new EnumArg<>( JoinAlgType.INNER, ParamType.JOIN_TYPE_ENUM ) ).build() ) + .param( Parameter.builder().name( "variables" ).type( ParamType.CORR_ID ).isMultiValued( true ).defaultValue( ListArg.EMPTY ).build() ) .param( Parameter.builder().name( "semiJoinDone" ).type( ParamType.BOOLEAN ).defaultValue( BooleanArg.FALSE ).build() ) .build() ); declarations.put( LogicalCalc.class, PolyAlgDeclaration.builder() + .model( DataModel.RELATIONAL ) .opName( "CALC" ).numInputs( 1 ).opTags( logRelTags ) .param( Parameter.builder().name( "exps" ).type( ParamType.SIMPLE_REX ).isMultiValued( true ).build() ) .param( Parameter.builder().name( "projects" ).type( ParamType.SIMPLE_REX ).isMultiValued( true ).build() ) .param( Parameter.builder().name( "condition" ).type( ParamType.SIMPLE_REX ).defaultValue( RexArg.NULL ).build() ) .build() ); - /* - declarations.put( .class, PolyAlgDeclaration.builder() - .opName( "" ).numInputs( ).opTags( logRelTags ) - .param( Parameter.builder().name( "" ).type( ParamType. ).build() ) - .build() ); - */ - } @@ -133,7 +137,7 @@ private static void populateClassesMap() { public static PolyAlgDeclaration getDeclaration( Class clazz ) { - return getDeclaration( clazz, 0 ); + return getDeclaration( clazz, DataModel.getDefault(), 0 ); } @@ -142,14 +146,15 @@ public static PolyAlgDeclaration getDeclaration( Class clazz * or returns a default PolyAlgDeclaration if none is found. * * @param clazz The class for which the PolyAlgDeclaration is being retrieved + * @param model the default DataModel to be used if the class is not registered * @param numInputs The number of inputs associated with the PolyAlgDeclaration if a new one is created. * @return The PolyAlgDeclaration associated with the specified class if found in the map, * or a new PolyAlgDeclaration initialized with the class name and the specified number of inputs. */ - public static PolyAlgDeclaration getDeclaration( Class clazz, int numInputs ) { + public static PolyAlgDeclaration getDeclaration( Class clazz, DataModel model, int numInputs ) { return declarations.getOrDefault( clazz, - PolyAlgDeclaration.builder().opName( clazz.getSimpleName() ).numInputs( numInputs ).build() ); + PolyAlgDeclaration.builder().opName( clazz.getSimpleName() ).model( model ).numInputs( numInputs ).build() ); } diff --git a/core/src/main/java/org/polypheny/db/algebra/polyalg/PolyAlgUtils.java b/core/src/main/java/org/polypheny/db/algebra/polyalg/PolyAlgUtils.java index 9c02e47399..a2d4870dda 100644 --- a/core/src/main/java/org/polypheny/db/algebra/polyalg/PolyAlgUtils.java +++ b/core/src/main/java/org/polypheny/db/algebra/polyalg/PolyAlgUtils.java @@ -125,13 +125,18 @@ public static List getAuxProjections( AlgNode child, List inputF } - public static List uniquifiedInputFieldNames( AlgNode context ) { + public static List getInputFieldNamesList( AlgNode context ) { if ( context == null ) { return List.of(); } - List names = context.getInputs().stream() + return context.getInputs().stream() .flatMap( node -> node.getTupleType().getFieldNames().stream() ) .toList(); + } + + + public static List uniquifiedInputFieldNames( AlgNode context ) { + List names = getInputFieldNamesList( context ); return ValidatorUtil.uniquify( names, ValidatorUtil.ATTEMPT_SUGGESTER, true ); } @@ -250,7 +255,7 @@ public String visitSubQuery( RexSubQuery subQuery ) { sb.append( "{\n" ); subQuery.alg.buildPolyAlgebra( sb ); sb.append( "})" ); - return "subQuery: " + sb.toString(); + return "subQuery: " + sb; } @@ -310,7 +315,7 @@ private String visitRexWindow( RexWindow window ) { StringWriter sw = new StringWriter(); PrintWriter pw = new PrintWriter( sw ); int clauseCount = 0; - if ( window.partitionKeys.size() > 0 ) { + if ( !window.partitionKeys.isEmpty() ) { if ( clauseCount++ > 0 ) { pw.print( ' ' ); } diff --git a/core/src/main/java/org/polypheny/db/algebra/polyalg/arguments/BooleanArg.java b/core/src/main/java/org/polypheny/db/algebra/polyalg/arguments/BooleanArg.java index 0a0a765524..cd3df5917e 100644 --- a/core/src/main/java/org/polypheny/db/algebra/polyalg/arguments/BooleanArg.java +++ b/core/src/main/java/org/polypheny/db/algebra/polyalg/arguments/BooleanArg.java @@ -26,11 +26,16 @@ public class BooleanArg implements PolyAlgArg { public static final BooleanArg FALSE = new BooleanArg( false ); public static final BooleanArg TRUE = new BooleanArg( true ); - private final boolean arg; + private final boolean bool; - public BooleanArg( boolean arg ) { - this.arg = arg; + public BooleanArg( boolean bool ) { + this.bool = bool; + } + + + public boolean toBool() { + return bool; } @@ -42,7 +47,7 @@ public ParamType getType() { @Override public String toPolyAlg( AlgNode context, @NonNull List inputFieldNames ) { - return Boolean.toString( arg ); + return Boolean.toString( bool ); } } diff --git a/core/src/main/java/org/polypheny/db/algebra/polyalg/arguments/CollationArg.java b/core/src/main/java/org/polypheny/db/algebra/polyalg/arguments/CollationArg.java index 8ad8d98242..0192b510ee 100644 --- a/core/src/main/java/org/polypheny/db/algebra/polyalg/arguments/CollationArg.java +++ b/core/src/main/java/org/polypheny/db/algebra/polyalg/arguments/CollationArg.java @@ -17,6 +17,7 @@ package org.polypheny.db.algebra.polyalg.arguments; import java.util.List; +import lombok.Getter; import lombok.NonNull; import org.polypheny.db.algebra.AlgFieldCollation; import org.polypheny.db.algebra.AlgNode; @@ -26,11 +27,12 @@ public class CollationArg implements PolyAlgArg { public static final CollationArg NULL = new CollationArg( null ); - private final AlgFieldCollation arg; + @Getter + private final AlgFieldCollation coll; - public CollationArg( AlgFieldCollation arg ) { - this.arg = arg; + public CollationArg( AlgFieldCollation coll ) { + this.coll = coll; } @@ -42,13 +44,17 @@ public ParamType getType() { @Override public String toPolyAlg( AlgNode context, @NonNull List inputFieldNames ) { - if ( arg == null ) { + if ( coll == null ) { return ""; } - int idx = arg.getFieldIndex(); + int idx = coll.getFieldIndex(); String str = inputFieldNames.size() > idx ? inputFieldNames.get( idx ) : Integer.toString( idx ); - if ( arg.direction != AlgFieldCollation.Direction.ASCENDING || arg.nullDirection != arg.direction.defaultNullDirection() ) { - str += " " + arg.shortString(); + boolean notDefaultNullDir = coll.nullDirection != coll.direction.defaultNullDirection(); + if ( coll.direction != AlgFieldCollation.Direction.ASCENDING || notDefaultNullDir ) { + str += " " + coll.direction.shortString; + if ( notDefaultNullDir ) { + str += " " + coll.nullDirection.toString(); + } } return str; } diff --git a/core/src/main/java/org/polypheny/db/algebra/polyalg/arguments/CorrelationArg.java b/core/src/main/java/org/polypheny/db/algebra/polyalg/arguments/CorrelationArg.java index 9c4449531a..d03ee5a692 100644 --- a/core/src/main/java/org/polypheny/db/algebra/polyalg/arguments/CorrelationArg.java +++ b/core/src/main/java/org/polypheny/db/algebra/polyalg/arguments/CorrelationArg.java @@ -17,6 +17,7 @@ package org.polypheny.db.algebra.polyalg.arguments; import java.util.List; +import lombok.Getter; import lombok.NonNull; import org.polypheny.db.algebra.AlgNode; import org.polypheny.db.algebra.core.CorrelationId; @@ -24,11 +25,12 @@ public class CorrelationArg implements PolyAlgArg { - private final CorrelationId arg; + @Getter + private final CorrelationId corrId; - public CorrelationArg( CorrelationId arg ) { - this.arg = arg; + public CorrelationArg( CorrelationId corrId ) { + this.corrId = corrId; } @@ -40,7 +42,7 @@ public ParamType getType() { @Override public String toPolyAlg( AlgNode context, @NonNull List inputFieldNames ) { - return String.valueOf( arg.getId() ); + return String.valueOf( corrId.getId() ); } } diff --git a/core/src/main/java/org/polypheny/db/algebra/polyalg/arguments/EntityArg.java b/core/src/main/java/org/polypheny/db/algebra/polyalg/arguments/EntityArg.java index 24392a57cb..234b2231ef 100644 --- a/core/src/main/java/org/polypheny/db/algebra/polyalg/arguments/EntityArg.java +++ b/core/src/main/java/org/polypheny/db/algebra/polyalg/arguments/EntityArg.java @@ -17,6 +17,7 @@ package org.polypheny.db.algebra.polyalg.arguments; import java.util.List; +import lombok.Getter; import lombok.NonNull; import org.polypheny.db.algebra.AlgNode; import org.polypheny.db.algebra.polyalg.PolyAlgDeclaration.ParamType; @@ -24,11 +25,12 @@ public class EntityArg implements PolyAlgArg { - private final Entity e; + @Getter + private final Entity entity; - public EntityArg( Entity e ) { - this.e = e; + public EntityArg( Entity entity ) { + this.entity = entity; } @@ -40,7 +42,7 @@ public ParamType getType() { @Override public String toPolyAlg( AlgNode context, @NonNull List inputFieldNames ) { - return e.getNamespaceName() + "." + e.name; + return entity.getNamespaceName() + "." + entity.name; } diff --git a/core/src/main/java/org/polypheny/db/algebra/polyalg/arguments/EnumArg.java b/core/src/main/java/org/polypheny/db/algebra/polyalg/arguments/EnumArg.java index 95818c7ff1..9f753f1c29 100644 --- a/core/src/main/java/org/polypheny/db/algebra/polyalg/arguments/EnumArg.java +++ b/core/src/main/java/org/polypheny/db/algebra/polyalg/arguments/EnumArg.java @@ -17,15 +17,18 @@ package org.polypheny.db.algebra.polyalg.arguments; import java.util.List; +import lombok.Getter; import lombok.NonNull; import org.polypheny.db.algebra.AlgNode; import org.polypheny.db.algebra.polyalg.PolyAlgDeclaration.ParamType; -public class EnumArg> implements PolyAlgArg{ +public class EnumArg> implements PolyAlgArg { + @Getter private final E arg; private final ParamType type; + public EnumArg( E arg, ParamType enumType ) { assert enumType.isEnum(); @@ -33,6 +36,7 @@ public EnumArg( E arg, ParamType enumType ) { this.type = enumType; } + @Override public ParamType getType() { return type; diff --git a/core/src/main/java/org/polypheny/db/algebra/polyalg/arguments/ListArg.java b/core/src/main/java/org/polypheny/db/algebra/polyalg/arguments/ListArg.java index caa51c0527..ca3e88ca7e 100644 --- a/core/src/main/java/org/polypheny/db/algebra/polyalg/arguments/ListArg.java +++ b/core/src/main/java/org/polypheny/db/algebra/polyalg/arguments/ListArg.java @@ -91,4 +91,14 @@ public String toPolyAlg( AlgNode context, @NonNull List inputFieldNames return PolyAlgUtils.joinMultiValued( strArgs, unpackValues ); } + + public List map( Function mapper ) { + return args.stream().map( mapper ).toList(); + } + + + public boolean isEmpty() { + return args.isEmpty(); + } + } diff --git a/core/src/main/java/org/polypheny/db/algebra/polyalg/arguments/PolyAlgArgs.java b/core/src/main/java/org/polypheny/db/algebra/polyalg/arguments/PolyAlgArgs.java index 95a3273218..430b3a24f9 100644 --- a/core/src/main/java/org/polypheny/db/algebra/polyalg/arguments/PolyAlgArgs.java +++ b/core/src/main/java/org/polypheny/db/algebra/polyalg/arguments/PolyAlgArgs.java @@ -149,6 +149,34 @@ public T getArg( int pos, Class type ) { } + public ListArg getListArg( int pos, Class type ) { + ListArg listArg = getArg( pos, ListArg.class ); + assert listArg.isEmpty() || type.isInstance( listArg.getArgs().get( 0 ) ); // an empty ListArg is of type EMPTY_LIST + return (ListArg) listArg; + } + + + public ListArg getListArg( String name, Class type ) { + ListArg listArg = getArg( name, ListArg.class ); + assert listArg.isEmpty() || type.isInstance( listArg.getArgs().get( 0 ) ); // an empty ListArg is of type EMPTY_LIST + return (ListArg) listArg; + } + + + public > EnumArg getEnumArg( int pos, Class type ) { + EnumArg enumArg = getArg( pos, EnumArg.class ); + assert type.isInstance( enumArg.getArg() ); + return (EnumArg) enumArg; + } + + + public > EnumArg getEnumArg( String name, Class type ) { + EnumArg enumArg = getArg( name, EnumArg.class ); + assert type.isInstance( enumArg.getArg() ); + return (EnumArg) enumArg; + } + + private T getArg( Parameter p, Class type ) { PolyAlgArg arg = getArg( p ); assert type.isInstance( arg ); diff --git a/core/src/main/java/org/polypheny/db/algebra/polyalg/arguments/RexArg.java b/core/src/main/java/org/polypheny/db/algebra/polyalg/arguments/RexArg.java index 565931c78d..20c0c4f8ee 100644 --- a/core/src/main/java/org/polypheny/db/algebra/polyalg/arguments/RexArg.java +++ b/core/src/main/java/org/polypheny/db/algebra/polyalg/arguments/RexArg.java @@ -30,6 +30,7 @@ public class RexArg implements PolyAlgArg { @Getter private final RexNode node; + @Getter private final String alias; private boolean omitTrue = false; diff --git a/core/src/main/java/org/polypheny/db/algebra/polyalg/parser/PolyAlgToAlgConverter.java b/core/src/main/java/org/polypheny/db/algebra/polyalg/parser/PolyAlgToAlgConverter.java index 95500bf0f3..20c8ba84bd 100644 --- a/core/src/main/java/org/polypheny/db/algebra/polyalg/parser/PolyAlgToAlgConverter.java +++ b/core/src/main/java/org/polypheny/db/algebra/polyalg/parser/PolyAlgToAlgConverter.java @@ -17,26 +17,31 @@ package org.polypheny.db.algebra.polyalg.parser; 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 lombok.NonNull; +import java.util.stream.IntStream; +import org.polypheny.db.algebra.AlgCollation; +import org.polypheny.db.algebra.AlgCollations; +import org.polypheny.db.algebra.AlgFieldCollation; import org.polypheny.db.algebra.AlgNode; import org.polypheny.db.algebra.AlgRoot; import org.polypheny.db.algebra.constant.JoinType; +import org.polypheny.db.algebra.constant.Kind; import org.polypheny.db.algebra.core.CorrelationId; +import org.polypheny.db.algebra.core.Sort; import org.polypheny.db.algebra.polyalg.PolyAlgDeclaration; import org.polypheny.db.algebra.polyalg.PolyAlgDeclaration.ParamType; import org.polypheny.db.algebra.polyalg.PolyAlgDeclaration.Parameter; import org.polypheny.db.algebra.polyalg.PolyAlgRegistry; import org.polypheny.db.algebra.polyalg.arguments.AnyArg; import org.polypheny.db.algebra.polyalg.arguments.BooleanArg; +import org.polypheny.db.algebra.polyalg.arguments.CollationArg; import org.polypheny.db.algebra.polyalg.arguments.CorrelationArg; import org.polypheny.db.algebra.polyalg.arguments.EntityArg; import org.polypheny.db.algebra.polyalg.arguments.EnumArg; +import org.polypheny.db.algebra.polyalg.arguments.FieldArg; import org.polypheny.db.algebra.polyalg.arguments.IntArg; import org.polypheny.db.algebra.polyalg.arguments.ListArg; import org.polypheny.db.algebra.polyalg.arguments.PolyAlgArg; @@ -44,50 +49,74 @@ import org.polypheny.db.algebra.polyalg.arguments.RexArg; import org.polypheny.db.algebra.polyalg.parser.nodes.PolyAlgAliasedArgument; import org.polypheny.db.algebra.polyalg.parser.nodes.PolyAlgExpression; +import org.polypheny.db.algebra.polyalg.parser.nodes.PolyAlgLiteral; import org.polypheny.db.algebra.polyalg.parser.nodes.PolyAlgNamedArgument; import org.polypheny.db.algebra.polyalg.parser.nodes.PolyAlgNode; import org.polypheny.db.algebra.polyalg.parser.nodes.PolyAlgNodeList; import org.polypheny.db.algebra.polyalg.parser.nodes.PolyAlgOperator; +import org.polypheny.db.algebra.type.AlgDataType; +import org.polypheny.db.algebra.type.AlgDataTypeField; +import org.polypheny.db.catalog.Catalog; import org.polypheny.db.catalog.entity.Entity; import org.polypheny.db.catalog.exceptions.GenericRuntimeException; +import org.polypheny.db.catalog.logistic.DataModel; +import org.polypheny.db.catalog.snapshot.Snapshot; +import org.polypheny.db.nodes.Operator; import org.polypheny.db.plan.AlgCluster; +import org.polypheny.db.rex.RexBuilder; +import org.polypheny.db.rex.RexIndexRef; import org.polypheny.db.rex.RexNode; +import org.polypheny.db.tools.AlgBuilder; +import org.polypheny.db.util.Pair; /** * Converter class, which transforms PolyAlg in its PolyAlgNode form to an equal AlgNode */ public class PolyAlgToAlgConverter { + private final Snapshot snapshot; private final AlgCluster cluster; + private final RexBuilder builder; - public PolyAlgToAlgConverter( @NonNull AlgCluster cluster ) { + public PolyAlgToAlgConverter( Snapshot snapshot, AlgCluster cluster ) { + this.snapshot = snapshot; this.cluster = cluster; + this.builder = cluster.getRexBuilder(); } public AlgRoot convert( PolyAlgNode root ) { AlgNode node = buildNode( (PolyAlgOperator) root ); - return null; + + // Wrap {@link AlgNode} into a RelRoot + final AlgDataType tupleType = node.getTupleType(); + final List> fields = Pair.zip( IntStream.range( 0, tupleType.getFieldCount() ).boxed().toList(), tupleType.getFieldNames() ); + final AlgCollation collation = + node instanceof Sort + ? ((Sort) node).collation + : AlgCollations.EMPTY; + return new AlgRoot( node, tupleType, Kind.SELECT, fields, collation ); } - public static AlgNode buildNode( PolyAlgOperator operator ) { - PolyAlgDeclaration declaration = Objects.requireNonNull( PolyAlgRegistry.getDeclaration( operator.getOpName() ) ); - System.out.println( declaration.opName + "[ " + operator.getChildren() + " ](" ); + private AlgNode buildNode( PolyAlgOperator operator ) { + PolyAlgDeclaration decl = Objects.requireNonNull( PolyAlgRegistry.getDeclaration( operator.getOpName() ) ); List children = operator.getChildren().stream() - .map( PolyAlgToAlgConverter::buildNode ) + .map( this::buildNode ) .toList(); + if ( !decl.supportsNumberOfChildren( children.size() ) ) { + throw new GenericRuntimeException( "Invalid number of children for '" + decl.opName + "'" ); + } - return declaration.createNode( buildArgs( declaration, operator.getArguments() ), children ); + PolyAlgArgs args = buildArgs( decl, operator.getArguments(), new Context( children, decl.model ) ); + return decl.createNode( args, children, cluster ); } - private static PolyAlgArgs buildArgs( PolyAlgDeclaration decl, List namedArgs ) { + private PolyAlgArgs buildArgs( PolyAlgDeclaration decl, List namedArgs, Context ctx ) { PolyAlgArgs converted = new PolyAlgArgs( decl ); - Map args = new HashMap<>(); - List keyArgs = new ArrayList<>(); boolean noMorePosArgs = false; Set usedParams = new HashSet<>(); @@ -116,18 +145,22 @@ private static PolyAlgArgs buildArgs( PolyAlgDeclaration decl, List args = new ArrayList<>(); for ( PolyAlgNode node : listArg.getPolyAlgList() ) { PolyAlgAliasedArgument aliasedArg = (PolyAlgAliasedArgument) node; - args.add( convertArg( p.getType(), aliasedArg ) ); + args.add( convertArg( p, aliasedArg, ctx ) ); } // We do not specify aliases for the entire list. Instead, this should happen on an element level (if necessary). return new ListArg<>( args ); } - private static PolyAlgArg convertArg( ParamType type, PolyAlgAliasedArgument aliasedArg ) { + private PolyAlgArg convertArg( Parameter p, PolyAlgAliasedArgument aliasedArg, Context ctx ) { if ( aliasedArg.getArg() instanceof PolyAlgExpression ) { - return convertExpression( type, (PolyAlgExpression) aliasedArg.getArg(), aliasedArg.getAlias() ); + return convertExpression( p, (PolyAlgExpression) aliasedArg.getArg(), aliasedArg.getAlias(), ctx ); } else { throw new GenericRuntimeException( "Nested PolyAlgNodeLists are currently not supported" ); } } - private static PolyAlgArg convertExpression( ParamType type, PolyAlgExpression exp, String alias ) { - - switch ( type ) { - case ANY -> { - // Actions for ParamType.ANY - return new AnyArg( exp.toString() ); - } - case INTEGER -> { - return new IntArg( exp.toInt() ); - } - case BOOLEAN -> { - return new BooleanArg( exp.toBoolean() ); - } + private PolyAlgArg convertExpression( Parameter p, PolyAlgExpression exp, String alias, Context ctx ) { + //System.out.println( "PolyAlgExpression: " + exp.toString() + " AS " + alias); + return switch ( p.getType() ) { + case ANY -> new AnyArg( exp.toString() ); + case INTEGER -> new IntArg( exp.toInt() ); + case BOOLEAN -> new BooleanArg( exp.toBoolean() ); case SIMPLE_REX -> { - return new RexArg( convertRexNode( exp ), alias ); + RexNode node = convertRexNode( exp, ctx ); + yield new RexArg( node, alias == null ? exp.toString() : alias ); } case AGGREGATE -> { // AggregateCall agg = new AggregateCall(); // return new AggArg( agg ); + throw new GenericRuntimeException( "Aggregate argument not yet implemented" ); } - case ENTITY -> { - return new EntityArg( convertEntity( exp ) ); - } - case JOIN_TYPE_ENUM -> { - return new EnumArg<>( exp.toEnum( JoinType.class ), ParamType.JOIN_TYPE_ENUM ); - } - case FIELD -> { - // Actions for ParamType.FIELD - } - case EMPTY_LIST -> { - return ListArg.EMPTY; - } - case COLLATION -> { - // Actions for ParamType.COLLATION - } - case CORR_ID -> { - return new CorrelationArg( new CorrelationId( exp.toString() ) ); + case ENTITY -> new EntityArg( convertEntity( exp, ctx.dataModel ) ); + case JOIN_TYPE_ENUM -> new EnumArg<>( exp.toEnum( JoinType.class ), ParamType.JOIN_TYPE_ENUM ); + case FIELD -> new FieldArg( ctx.getFieldOrdinal( exp.toIdentifier() ) ); + case EMPTY_LIST -> ListArg.EMPTY; + case COLLATION -> new CollationArg( convertCollation( exp, ctx ) ); + case CORR_ID -> new CorrelationArg( new CorrelationId( exp.toString() ) ); + default -> throw new IllegalStateException( "Unexpected value: " + p.getType() ); + }; + } + + + private RexNode convertRexNode( PolyAlgExpression exp, Context ctx ) { + if ( exp.isCall() ) { + Operator operator = exp.getOperator(); + List operands = exp.getChildExps().stream().map( e -> convertRexNode( e, ctx ) ).toList(); + return builder.makeCall( operator, operands ); + } else if ( exp.isSingleLiteral() ) { + PolyAlgLiteral literal = exp.getLiterals().get( 0 ); + + // TODO: handle all cases of non-call RexNodes + if ( literal.isQuoted() ) { + return builder.makeLiteral( literal.toUnquotedString() ); + } else if ( literal.isNumber() ) { + return AlgBuilder.literal( literal.toNumber(), builder ); + } else if ( literal.isBoolean() ) { + return AlgBuilder.literal( literal.toBoolean(), builder ); + } else { + String str = literal.toString(); + int idx = ctx.getFieldOrdinal( str ); + return RexIndexRef.of( idx, ctx.fields ); } - default -> throw new IllegalStateException( "Unexpected value: " + type ); + } - return null; + throw new GenericRuntimeException( "Invalid RexNode: " + exp ); + } - private static RexNode convertRexNode( PolyAlgExpression exp ) { - return null; + private Entity convertEntity( PolyAlgExpression exp, DataModel dataModel ) { + String[] names = exp.toIdentifier().split( "\\.", 3 ); + GenericRuntimeException exception = new GenericRuntimeException( "Invalid entity name: " + String.join( ".", names ) ); + String namespaceName; + String entityName; + if ( names.length == 2 ) { + namespaceName = names[0]; + entityName = names[1]; + } else if ( names.length == 1 ) { + namespaceName = Catalog.DEFAULT_NAMESPACE_NAME; + entityName = names[0]; + } else { + throw exception; + } + + return switch ( dataModel ) { + case RELATIONAL -> snapshot.rel().getTable( namespaceName, entityName ).orElseThrow( () -> exception ); + case DOCUMENT -> snapshot.doc().getCollection( + snapshot.getNamespace( namespaceName ).orElseThrow( () -> exception ).id, + entityName ) + .orElseThrow( () -> exception ); + case GRAPH -> snapshot.graph().getGraph( + snapshot.getNamespace( namespaceName ).orElseThrow( () -> exception ).id ) + .orElseThrow( () -> exception ); + }; } - private static Entity convertEntity( PolyAlgExpression exp ) { - List literals = exp.getLiteralsAsStrings(); - if ( literals.size() == 3 && literals.get( 1 ).equals( "." ) ) { - String namespaceName = literals.get( 0 ); - String entityName = literals.get( 2 ); + private AlgFieldCollation convertCollation( PolyAlgExpression exp, Context ctx ) { + List literals = exp.getLiterals(); + int size = literals.size(); + int fieldIndex = ctx.getFieldOrdinal( literals.get( 0 ).toString() ); + return switch ( size ) { + case 1 -> new AlgFieldCollation( fieldIndex ); + case 2 -> new AlgFieldCollation( fieldIndex, literals.get( 1 ).toDirection() ); + case 3 -> new AlgFieldCollation( + fieldIndex, + literals.get( 1 ).toDirection(), + literals.get( 2 ).toNullDirection() ); + default -> throw new GenericRuntimeException( "Too many values for AlgFieldCollation" ); + }; + } + + + private static final class Context { + + private final List children; + private final List fieldNames; + private final List fields; + private final DataModel dataModel; + + + private Context( List children, DataModel dataModel ) { + this.children = children; + this.fieldNames = children.stream() + .flatMap( node -> node.getTupleType().getFieldNames().stream() ) + .toList(); + this.fields = children.stream() + .flatMap( node -> node.getTupleType().getFields().stream() ) + .toList(); + this.dataModel = dataModel; + } - return null; - } else if ( literals.size() == 1 ) { - String entityName = literals.get( 0 ); - return null; + private AlgNode getChildFromFieldOrdinal( int ord ) { + int offset = 0; + for ( AlgNode child : children ) { + int count = child.getTupleType().getFieldCount(); + if ( ord < offset + count ) { + return child; + } + offset += count; + } + throw new GenericRuntimeException( "Invalid field index" ); } - throw new GenericRuntimeException( "Invalid entity name: " + String.join( "", literals ) ); + + + private int getFieldOrdinal( String fieldName ) { + int idx = fieldNames.indexOf( fieldName ); + if ( idx < 0 ) { + throw new GenericRuntimeException( "Invalid field name" ); + } + return idx; + } + } } diff --git a/core/src/main/java/org/polypheny/db/algebra/polyalg/parser/nodes/PolyAlgExpression.java b/core/src/main/java/org/polypheny/db/algebra/polyalg/parser/nodes/PolyAlgExpression.java index ff1225dfc2..89a08bb9a8 100644 --- a/core/src/main/java/org/polypheny/db/algebra/polyalg/parser/nodes/PolyAlgExpression.java +++ b/core/src/main/java/org/polypheny/db/algebra/polyalg/parser/nodes/PolyAlgExpression.java @@ -17,11 +17,15 @@ package org.polypheny.db.algebra.polyalg.parser.nodes; import java.util.List; +import java.util.Locale; import java.util.StringJoiner; import lombok.Getter; import lombok.NonNull; +import org.polypheny.db.algebra.operators.OperatorName; import org.polypheny.db.catalog.exceptions.GenericRuntimeException; +import org.polypheny.db.languages.OperatorRegistry; import org.polypheny.db.languages.ParserPos; +import org.polypheny.db.nodes.Operator; /** * One-size-fits-all class for any RexNodes or even literals not wrapped in a RexNode @@ -37,7 +41,9 @@ public class PolyAlgExpression extends PolyAlgNode { public PolyAlgExpression( @NonNull List literals, List childExps, String cast, ParserPos pos ) { super( pos ); - assert !literals.isEmpty(); + if ( literals.isEmpty() ) { + throw new GenericRuntimeException( "Expression must have at least one literal" ); + } this.literals = literals; this.childExps = childExps; @@ -48,7 +54,7 @@ public PolyAlgExpression( @NonNull List literals, List> T toEnum( Class enumClass ) { if ( !isSingleLiteral() ) { - throw new GenericRuntimeException( "Not a valid integer" ); + throw new GenericRuntimeException( "Not a valid integer: " + this ); } return Enum.valueOf( enumClass, literals.get( 0 ).toString() ); } + public String toIdentifier() { + if ( !isSingleLiteral() ) { + throw new GenericRuntimeException( "Not a valid identifier: " + this ); + } + return literals.get( 0 ).toUnquotedString(); + } + + public List getLiteralsAsStrings() { return literals.stream().map( PolyAlgLiteral::toString ).toList(); } + public String getLiteralsAsString() { + return String.join( "", getLiteralsAsStrings() ); + } + + @Override public String toString() { StringBuilder sb = new StringBuilder(); @@ -110,4 +137,25 @@ public String toString() { return sb.toString(); } + + public Operator getOperator() { + String str = getLiteralsAsString(); + return switch ( str.toUpperCase( Locale.ROOT ) ) { + case "+" -> OperatorRegistry.get( OperatorName.PLUS ); + case "-" -> OperatorRegistry.get( OperatorName.MINUS ); + case "*" -> OperatorRegistry.get( OperatorName.MULTIPLY ); + case "/" -> OperatorRegistry.get( OperatorName.DIVIDE ); + case "=" -> OperatorRegistry.get( OperatorName.EQUALS ); + case "!=", "<>" -> OperatorRegistry.get( OperatorName.NOT_EQUALS ); + case ">" -> OperatorRegistry.get( OperatorName.GREATER_THAN ); + case ">=" -> OperatorRegistry.get( OperatorName.GREATER_THAN_OR_EQUAL ); + case "<" -> OperatorRegistry.get( OperatorName.LESS_THAN ); + case "<=" -> OperatorRegistry.get( OperatorName.LESS_THAN_OR_EQUAL ); + case "AND" -> OperatorRegistry.get( OperatorName.AND ); + case "OR" -> OperatorRegistry.get( OperatorName.OR ); + case "NOT" -> OperatorRegistry.get( OperatorName.NOT ); + default -> throw new IllegalArgumentException( "Operator '" + str + "' is not yet supported" ); + }; + } + } diff --git a/core/src/main/java/org/polypheny/db/algebra/polyalg/parser/nodes/PolyAlgLiteral.java b/core/src/main/java/org/polypheny/db/algebra/polyalg/parser/nodes/PolyAlgLiteral.java index 05338a795e..2d5ade024d 100644 --- a/core/src/main/java/org/polypheny/db/algebra/polyalg/parser/nodes/PolyAlgLiteral.java +++ b/core/src/main/java/org/polypheny/db/algebra/polyalg/parser/nodes/PolyAlgLiteral.java @@ -16,20 +16,32 @@ package org.polypheny.db.algebra.polyalg.parser.nodes; +import java.util.Locale; +import lombok.Getter; +import org.polypheny.db.algebra.AlgFieldCollation; +import org.polypheny.db.algebra.AlgFieldCollation.Direction; +import org.polypheny.db.algebra.AlgFieldCollation.NullDirection; import org.polypheny.db.catalog.exceptions.GenericRuntimeException; import org.polypheny.db.languages.ParserPos; public class PolyAlgLiteral extends PolyAlgNode { private final String str; + @Getter private final boolean isNumber; + @Getter + private final boolean isQuoted; + @Getter + private final boolean isBoolean; - public PolyAlgLiteral( String str, boolean isNumber, ParserPos pos ) { + public PolyAlgLiteral( String str, boolean isNumber, boolean isQuoted, boolean isBoolean, ParserPos pos ) { super( pos ); this.str = str; this.isNumber = isNumber; + this.isQuoted = isQuoted; + this.isBoolean = isBoolean; } @@ -46,9 +58,49 @@ public boolean toBoolean() { } + public Number toNumber() { + if ( !this.isNumber ) { + throw new GenericRuntimeException( "Not a valid number" ); + } + + Number num; + double dbl = Double.parseDouble( str ); + num = dbl; + if ( dbl % 1 == 0 ) { + num = Integer.parseInt( str ); + } + return num; + } + + + public AlgFieldCollation.Direction toDirection() { + return switch ( str.toUpperCase( Locale.ROOT ) ) { + case "ASC" -> Direction.ASCENDING; + case "DESC" -> Direction.DESCENDING; + case "SASC" -> Direction.STRICTLY_ASCENDING; + case "SDESC" -> Direction.STRICTLY_DESCENDING; + case "CLU" -> Direction.CLUSTERED; + default -> throw new IllegalArgumentException( "'" + str + "' is not a valid direction" ); + }; + } + + + public AlgFieldCollation.NullDirection toNullDirection() { + return NullDirection.valueOf( str.toUpperCase( Locale.ROOT ) ); + } + + @Override public String toString() { return str; } + + public String toUnquotedString() { + if ( isQuoted ) { + return str.substring( 1, str.length() - 1 ); + } + return str; + } + } diff --git a/core/src/main/java/org/polypheny/db/plan/AlgOptUtil.java b/core/src/main/java/org/polypheny/db/plan/AlgOptUtil.java index 5fe5004df8..5669972a3c 100644 --- a/core/src/main/java/org/polypheny/db/plan/AlgOptUtil.java +++ b/core/src/main/java/org/polypheny/db/plan/AlgOptUtil.java @@ -87,10 +87,6 @@ import org.polypheny.db.algebra.logical.relational.LogicalRelProject; import org.polypheny.db.algebra.metadata.AlgMetadataQuery; import org.polypheny.db.algebra.operators.OperatorName; -import org.polypheny.db.algebra.polyalg.parser.PolyAlgToAlgConverter; -import org.polypheny.db.algebra.polyalg.parser.nodes.PolyAlgNode; -import org.polypheny.db.algebra.polyalg.parser.PolyAlgParser; -import org.polypheny.db.algebra.polyalg.parser.nodes.PolyAlgOperator; import org.polypheny.db.algebra.rules.AggregateProjectPullUpConstantsRule; import org.polypheny.db.algebra.rules.DateRangeRules; import org.polypheny.db.algebra.rules.FilterMergeRule; @@ -1366,31 +1362,6 @@ public static void registerAbstractAlgs( AlgPlanner planner ) { * @return Plan */ public static String dumpPlan( String header, AlgNode alg, ExplainFormat format, ExplainLevel detailLevel ) { - //TODO: Delete lines for testing PolyAlg serialization - if ( !header.startsWith( "Routed" ) ) { - StringBuilder sb = new StringBuilder(); - alg.buildPolyAlgebra( sb, "" ); - System.out.println( "===== " + header + " =====" ); - System.out.println( sb ); - System.out.print( "----> " ); - - try { - PolyAlgParser parser = PolyAlgParser.create( sb.toString() ); - PolyAlgNode node = (PolyAlgNode) parser.parseQuery(); - System.out.println( "Successfully parsed input!" ); - - if ( !header.startsWith( "Physical" ) ) { - //PolyAlgToAlgConverter.buildNode( (PolyAlgOperator) node ); - } - } catch ( Exception e ) { - System.out.println( "Could not parse input correctly:" ); - System.out.println( e.getMessage() ); - e.printStackTrace(); - } - - System.out.println(); - } - StringWriter sw = new StringWriter(); PrintWriter pw = new PrintWriter( sw ); if ( !header.equals( "" ) ) { diff --git a/core/src/main/java/org/polypheny/db/tools/AlgBuilder.java b/core/src/main/java/org/polypheny/db/tools/AlgBuilder.java index 5d547d8447..83bd2edb23 100644 --- a/core/src/main/java/org/polypheny/db/tools/AlgBuilder.java +++ b/core/src/main/java/org/polypheny/db/tools/AlgBuilder.java @@ -430,6 +430,10 @@ private int inputOffset( int inputCount, int inputOrdinal ) { */ public RexNode literal( Object value ) { final RexBuilder rexBuilder = cluster.getRexBuilder(); + return literal( value, rexBuilder ); + } + + public static RexNode literal( Object value, RexBuilder rexBuilder) { if ( value == null ) { return rexBuilder.constantNull(); } else if ( value instanceof Boolean ) { diff --git a/dbms/src/main/java/org/polypheny/db/processing/AbstractQueryProcessor.java b/dbms/src/main/java/org/polypheny/db/processing/AbstractQueryProcessor.java index fa45693679..14b6aeb533 100644 --- a/dbms/src/main/java/org/polypheny/db/processing/AbstractQueryProcessor.java +++ b/dbms/src/main/java/org/polypheny/db/processing/AbstractQueryProcessor.java @@ -74,7 +74,11 @@ import org.polypheny.db.algebra.logical.relational.LogicalRelProject; import org.polypheny.db.algebra.logical.relational.LogicalRelScan; import org.polypheny.db.algebra.logical.relational.LogicalRelValues; +import org.polypheny.db.algebra.polyalg.parser.PolyAlgParser; +import org.polypheny.db.algebra.polyalg.parser.PolyAlgToAlgConverter; +import org.polypheny.db.algebra.polyalg.parser.nodes.PolyAlgNode; import org.polypheny.db.algebra.type.AlgDataType; +import org.polypheny.db.algebra.type.AlgDataTypeFactory; import org.polypheny.db.algebra.type.AlgDataTypeField; import org.polypheny.db.catalog.Catalog; import org.polypheny.db.catalog.entity.Entity; @@ -82,6 +86,7 @@ import org.polypheny.db.catalog.entity.logical.LogicalTable; import org.polypheny.db.catalog.exceptions.GenericRuntimeException; import org.polypheny.db.catalog.logistic.DataModel; +import org.polypheny.db.catalog.snapshot.Snapshot; import org.polypheny.db.config.RuntimeConfig; import org.polypheny.db.information.Information; import org.polypheny.db.information.InformationCode; @@ -97,10 +102,12 @@ import org.polypheny.db.monitoring.events.StatementEvent; import org.polypheny.db.partition.PartitionManagerFactory; import org.polypheny.db.partition.properties.PartitionProperty; +import org.polypheny.db.plan.AlgCluster; import org.polypheny.db.plan.AlgOptCost; import org.polypheny.db.plan.AlgOptUtil; import org.polypheny.db.plan.AlgTraitSet; import org.polypheny.db.plan.Convention; +import org.polypheny.db.plan.volcano.VolcanoPlanner; import org.polypheny.db.prepare.Prepare.PreparedResult; import org.polypheny.db.prepare.Prepare.PreparedResultImpl; import org.polypheny.db.processing.caching.ImplementationCache; @@ -244,6 +251,53 @@ private void attachQueryPlans( AlgRoot logicalRoot ) { group, AlgOptUtil.dumpPlan( "Logical Query Plan", logicalRoot.alg, ExplainFormat.JSON, ExplainLevel.ALL_ATTRIBUTES ) ); queryAnalyzer.registerInformation( informationQueryPlan ); + + testPolyAlgParserDuringDevelopment(logicalRoot.alg); // TODO: Delete as soon as PolyAlgParser is working + } + + private void testPolyAlgParserDuringDevelopment(AlgNode alg) { + /*TransactionManager tm = TransactionManagerImpl.getInstance(); + Transaction transaction = tm.startTransaction( statement.getTransaction().getUser().getId(), false, "PolyAlg Tester");*/ + AlgDataTypeFactory factory = AlgDataTypeFactory.DEFAULT; + AlgCluster cluster = AlgCluster.create( new VolcanoPlanner(), new RexBuilder( factory ), null, null ); + Snapshot snapshot = Catalog.snapshot(); + + + StringBuilder sb = new StringBuilder(); + alg.buildPolyAlgebra( sb, "" ); + + String polyAlg = sb.toString(); + System.out.println( "===== Logical Query Plan =====" ); + System.out.println( polyAlg ); + System.out.print( "----> " ); + try { + PolyAlgParser parser = PolyAlgParser.create( polyAlg ); + PolyAlgNode node = (PolyAlgNode) parser.parseQuery(); + System.out.println( "Successfully parsed input!\n" ); + + PolyAlgToAlgConverter converter = new PolyAlgToAlgConverter(snapshot, cluster); + + AlgRoot root = converter.convert( node ); + + System.out.println( "Successfully built AlgNode Tree!\n" ); + + sb.setLength( 0 ); // reset StringBuilder + root.alg.buildPolyAlgebra( sb, "" ); + String roundTripPolyAlg = sb.toString(); + System.out.println( ">>>>> Logical Query Plan (parsed) <<<<<" ); + System.out.println( sb ); + if (polyAlg.replace( "PROJECT#", "PROJECT" ).equals( roundTripPolyAlg )) { + System.out.println("----> Plans are equal!"); + } else { + log.warn( "Plans are not equal" ); + } + System.out.println(); + + } catch ( Exception e ) { + System.out.println( "Could not parse input correctly:" ); + e.printStackTrace(); + } + }