diff --git a/interactive_engine/compiler/src/main/antlr4/CypherGS.g4 b/interactive_engine/compiler/src/main/antlr4/CypherGS.g4 index f041655c01b0..0f527ba58d93 100644 --- a/interactive_engine/compiler/src/main/antlr4/CypherGS.g4 +++ b/interactive_engine/compiler/src/main/antlr4/CypherGS.g4 @@ -52,7 +52,15 @@ CALL : ( 'C' | 'c' ) ( 'A' | 'a' ) ( 'L' | 'l' ) ( 'L' | 'l' ) ; YIELD : ( 'Y' | 'y' ) ( 'I' | 'i' ) ( 'E' | 'e' ) ( 'L' | 'l' ) ( 'D' | 'd' ) ; oC_RegularQuery - : oC_Match ( SP? ( oC_Match | oC_With | oC_Unwind | oC_UnionCallSubQuery ) )* ( SP oC_Return ) ; + : ( oC_ReadingClause SP? )* SP? oC_ReadingClause ( SP oC_Return ) + ; + +oC_ReadingClause + : oC_Match + | oC_Unwind + | oC_With + | oC_UnionCallSubQuery + ; oC_SubQuery : ( ( oC_Match | oC_With | oC_Unwind ) SP? )* ( SP? oC_Return ) ; diff --git a/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/common/ir/planner/GraphRelOptimizer.java b/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/common/ir/planner/GraphRelOptimizer.java index ae3039bd132f..739eef95538a 100644 --- a/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/common/ir/planner/GraphRelOptimizer.java +++ b/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/common/ir/planner/GraphRelOptimizer.java @@ -21,6 +21,8 @@ import com.alibaba.graphscope.common.ir.meta.IrMeta; import com.alibaba.graphscope.common.ir.meta.glogue.calcite.GraphRelMetadataQuery; import com.alibaba.graphscope.common.ir.meta.glogue.calcite.handler.GraphMetadataHandlerProvider; +import com.alibaba.graphscope.common.ir.meta.schema.CommonOptTable; +import com.alibaba.graphscope.common.ir.rel.CommonTableScan; import com.alibaba.graphscope.common.ir.rel.GraphShuttle; import com.alibaba.graphscope.common.ir.rel.graph.GraphLogicalSource; import com.alibaba.graphscope.common.ir.rel.graph.match.AbstractLogicalMatch; @@ -32,6 +34,7 @@ import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; import com.google.common.collect.Lists; +import com.google.common.collect.Maps; import org.apache.calcite.plan.GraphOptCluster; import org.apache.calcite.plan.RelOptPlanner; @@ -45,6 +48,7 @@ import org.checkerframework.checker.nullness.qual.Nullable; import java.util.List; +import java.util.Map; import java.util.concurrent.atomic.AtomicBoolean; /** @@ -100,10 +104,26 @@ public RelNode optimize(RelNode before, GraphIOProcessor ioProcessor) { public static class MatchOptimizer extends GraphShuttle { private final GraphIOProcessor ioProcessor; private final RelOptPlanner matchPlanner; + // record the common rel(s) which has been optimized + private final Map commonTableToOpt; public MatchOptimizer(GraphIOProcessor ioProcessor, RelOptPlanner matchPlanner) { this.ioProcessor = ioProcessor; this.matchPlanner = matchPlanner; + this.commonTableToOpt = Maps.newHashMap(); + } + + @Override + public RelNode visit(CommonTableScan tableScan) { + CommonOptTable optTable = (CommonOptTable) tableScan.getTable(); + String tableName = optTable.getQualifiedName().get(0); + RelNode commonOpt = commonTableToOpt.get(tableName); + if (commonOpt == null) { + commonOpt = optTable.getCommon().accept(this); + commonTableToOpt.put(tableName, commonOpt); + } + return new CommonTableScan( + tableScan.getCluster(), tableScan.getTraitSet(), new CommonOptTable(commonOpt)); } @Override diff --git a/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/common/ir/planner/rules/FlatJoinToExpandRule.java b/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/common/ir/planner/rules/FlatJoinToExpandRule.java index 193d3d86cb0f..7c3527cbeddf 100644 --- a/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/common/ir/planner/rules/FlatJoinToExpandRule.java +++ b/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/common/ir/planner/rules/FlatJoinToExpandRule.java @@ -24,6 +24,7 @@ import com.alibaba.graphscope.common.ir.rel.type.AliasNameWithId; import com.alibaba.graphscope.common.ir.rex.RexGraphVariable; import com.google.common.collect.Lists; + import org.apache.calcite.rel.RelNode; import org.apache.calcite.rel.core.JoinRelType; import org.apache.calcite.rel.logical.LogicalFilter; diff --git a/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/common/ir/rel/graph/GraphLogicalExpand.java b/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/common/ir/rel/graph/GraphLogicalExpand.java index 4b10f028779b..d3ced787374a 100644 --- a/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/common/ir/rel/graph/GraphLogicalExpand.java +++ b/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/common/ir/rel/graph/GraphLogicalExpand.java @@ -23,6 +23,7 @@ import com.alibaba.graphscope.common.ir.tools.config.GraphOpt; import com.alibaba.graphscope.common.ir.type.GraphSchemaType; import com.google.common.collect.ImmutableList; + import org.apache.calcite.plan.GraphOptCluster; import org.apache.calcite.plan.RelTraitSet; import org.apache.calcite.rel.RelNode; diff --git a/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/common/ir/rel/graph/GraphLogicalGetV.java b/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/common/ir/rel/graph/GraphLogicalGetV.java index cbebf7388fb0..52d6035a9d9d 100644 --- a/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/common/ir/rel/graph/GraphLogicalGetV.java +++ b/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/common/ir/rel/graph/GraphLogicalGetV.java @@ -22,6 +22,7 @@ import com.alibaba.graphscope.common.ir.rel.type.TableConfig; import com.alibaba.graphscope.common.ir.tools.config.GraphOpt; import com.google.common.collect.ImmutableList; + import org.apache.calcite.plan.GraphOptCluster; import org.apache.calcite.plan.RelTraitSet; import org.apache.calcite.rel.RelNode; diff --git a/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/common/ir/rel/graph/GraphLogicalSource.java b/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/common/ir/rel/graph/GraphLogicalSource.java index 70c6a3d9f8bc..3c1d41bc5f79 100644 --- a/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/common/ir/rel/graph/GraphLogicalSource.java +++ b/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/common/ir/rel/graph/GraphLogicalSource.java @@ -20,6 +20,7 @@ import com.alibaba.graphscope.common.ir.rel.type.TableConfig; import com.alibaba.graphscope.common.ir.tools.config.GraphOpt; import com.google.common.collect.ImmutableList; + import org.apache.calcite.plan.GraphOptCluster; import org.apache.calcite.rel.RelNode; import org.apache.calcite.rel.RelShuttle; diff --git a/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/cypher/antlr4/visitor/CallSubQueryVisitor.java b/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/cypher/antlr4/visitor/CallSubQueryVisitor.java index b627a9858e2b..83f9dc89a2d7 100644 --- a/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/cypher/antlr4/visitor/CallSubQueryVisitor.java +++ b/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/cypher/antlr4/visitor/CallSubQueryVisitor.java @@ -23,6 +23,7 @@ import com.alibaba.graphscope.common.ir.tools.GraphBuilder; import com.alibaba.graphscope.grammar.CypherGSBaseVisitor; import com.alibaba.graphscope.grammar.CypherGSParser; + import org.apache.calcite.plan.GraphOptCluster; import org.apache.calcite.plan.RelOptTable; import org.apache.calcite.rel.RelNode; diff --git a/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/cypher/antlr4/visitor/GraphBuilderVisitor.java b/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/cypher/antlr4/visitor/GraphBuilderVisitor.java index cc22c7f8de53..59f82c4ea4ae 100644 --- a/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/cypher/antlr4/visitor/GraphBuilderVisitor.java +++ b/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/cypher/antlr4/visitor/GraphBuilderVisitor.java @@ -64,8 +64,7 @@ public GraphBuilder visitOC_UnionCallSubQuery(CypherGSParser.OC_UnionCallSubQuer for (int i = 0; i < ctx.oC_CallSubQuery().size(); ++i) { CypherGSParser.OC_CallSubQueryContext callSubQuery = ctx.oC_CallSubQuery(i); if (callSubQuery != null) { - branches.add( - new CallSubQueryVisitor(this.builder).visit(callSubQuery)); + branches.add(new CallSubQueryVisitor(this.builder).visit(callSubQuery)); } } Preconditions.checkArgument( diff --git a/interactive_engine/compiler/src/test/java/com/alibaba/graphscope/common/ir/planner/cbo/LdbcTest.java b/interactive_engine/compiler/src/test/java/com/alibaba/graphscope/common/ir/planner/cbo/LdbcTest.java index 8f4c380f8ca7..d94969f4c6e6 100644 --- a/interactive_engine/compiler/src/test/java/com/alibaba/graphscope/common/ir/planner/cbo/LdbcTest.java +++ b/interactive_engine/compiler/src/test/java/com/alibaba/graphscope/common/ir/planner/cbo/LdbcTest.java @@ -28,8 +28,9 @@ public static void beforeClass() { "graph.planner.opt", "CBO", "graph.planner.rules", - "NotMatchToAntiJoinRule, FilterIntoJoinRule, FilterMatchRule, FlatJoinToExpandRule," - + " ExtendIntersectRule, ExpandGetVFusionRule")); + "NotMatchToAntiJoinRule, FilterIntoJoinRule, FilterMatchRule," + + " FlatJoinToExpandRule, ExtendIntersectRule," + + " ExpandGetVFusionRule")); optimizer = new GraphRelOptimizer(configs); irMeta = Utils.mockIrMeta( @@ -43,92 +44,108 @@ public void ldbc1_test() { GraphBuilder builder = Utils.mockGraphBuilder(optimizer, irMeta); RelNode before = com.alibaba.graphscope.cypher.antlr4.Utils.eval( - "MATCH shortestPath(p: PERSON {id : $personId})" - + " -[k:KNOWS*1..4]-(f:PERSON {firstName : $firstName})\n" - + "where f <> p\n" - + "WITH f, length(k) as distance\n" + "MATCH (p: PERSON{id: $personId}) -[k:KNOWS*1..4]-(f: PERSON" + + " {firstName: $firstName})\n" + + "OPTIONAL MATCH (f:" + + " PERSON)-[workAt:WORKAT]->(company:ORGANISATION)-[:ISLOCATEDIN]->(country:PLACE)\n" + + "OPTIONAL MATCH (f:" + + " PERSON)-[studyAt:STUDYAT]->(university)-[:ISLOCATEDIN]->(universityCity:PLACE)\n" + + "MATCH (f:PERSON)-[:ISLOCATEDIN]->(locationCity:PLACE)\n" + + "WHERE p <> f\n" + + "with f AS f, company, university, workAt, country, studyAt," + + " universityCity, locationCity, length(k) as len\n" + + "with f AS f, company, university, workAt, country, studyAt," + + " universityCity, locationCity, min(len) as distance\n" + "ORDER BY distance ASC, f.lastName ASC, f.id ASC\n" + "LIMIT 20\n" + "\n" - + "\n" - + "OPTIONAL MATCH (f:" - + " PERSON)-[workAt:WORKAT]->(company:ORGANISATION)-[:ISLOCATEDIN]->(country:PLACE)\n" + "WITH \n" - + " f, distance,\n" + + " f, distance, locationCity,\n" + " CASE\n" + " WHEN company is null Then null\n" + " ELSE [company.name, workAt.workFrom, country.name]\n" - + " END as companies\n" - + "WITH f, collect(companies) as company_info, distance\n" - + "\n" - + "OPTIONAL MATCH (f:" - + " PERSON)-[studyAt:STUDYAT]->(university)-[:ISLOCATEDIN]->(universityCity:PLACE)\n" - + "WITH\n" - + " f, company_info, distance,\n" - + "\tCASE \n" - + "\t\tWHEN university is null Then null\n" - + "\t\tELSE [university.name, studyAt.classYear," + + " END as companies,\n" + + " CASE \n" + + "\t\t WHEN university is null Then null\n" + + "\t\t ELSE [university.name, studyAt.classYear," + " universityCity.name]\n" - + "\tEND as universities\n" - + "WITH f, collect(universities) as university_info ," - + " company_info, distance\n" + + "\t END as universities\n" + + "WITH f, distance, locationCity, collect(companies) as" + + " company_info, collect(universities) as university_info\n" + "\n" - + "MATCH (f:PERSON)-[:ISLOCATEDIN]->(locationCity:PLACE)\n" - + "\n" - + "return f.id AS friendId;", + + "return f.id AS friendId,\n" + + " f.lastName AS friendLastName,\n" + + " distance AS distanceFromPerson,\n" + + " f.birthday AS friendBirthday,\n" + + " f.creationDate AS friendCreationDate,\n" + + " f.gender AS friendGender,\n" + + " f.browserUsed AS friendBrowserUsed,\n" + + " f.locationIP AS friendLocationIp,\n" + + " locationCity.name AS friendCityName,\n" + + " university_info AS friendUniversities,\n" + + " company_info AS friendCompanies;", builder) .build(); RelNode after = optimizer.optimize(before, new GraphIOProcessor(builder, irMeta)); Assert.assertEquals( - "GraphLogicalProject(friendId=[f.id], isAppend=[false])\n" - + " GraphPhysicalExpand(tableConfig=[[EdgeLabel(ISLOCATEDIN, PERSON, PLACE)]]," - + " alias=[locationCity], startAlias=[f], opt=[OUT], physicalOpt=[VERTEX])\n" - + " GraphLogicalAggregate(keys=[{variables=[f, company_info, distance]," - + " aliases=[f, company_info, distance]}], values=[[{operands=[universities]," - + " aggFunction=COLLECT, alias='university_info', distinct=false}]])\n" - + " GraphLogicalProject(f=[f], company_info=[company_info]," - + " distance=[distance], universities=[CASE(IS NULL(university), null:NULL," - + " ARRAY(university.name, studyAt.classYear, universityCity.name))]," - + " isAppend=[false])\n" - + " GraphPhysicalExpand(tableConfig=[[EdgeLabel(ISLOCATEDIN," + "GraphLogicalProject(friendId=[f.id], friendLastName=[f.lastName]," + + " distanceFromPerson=[distance], friendBirthday=[f.birthday]," + + " friendCreationDate=[f.creationDate], friendGender=[f.gender]," + + " friendBrowserUsed=[f.browserUsed], friendLocationIp=[f.locationIP]," + + " friendCityName=[locationCity.name], friendUniversities=[university_info]," + + " friendCompanies=[company_info], isAppend=[false])\n" + + " GraphLogicalAggregate(keys=[{variables=[f, distance, locationCity]," + + " aliases=[f, distance, locationCity]}], values=[[{operands=[companies]," + + " aggFunction=COLLECT, alias='company_info', distinct=false}," + + " {operands=[universities], aggFunction=COLLECT, alias='university_info'," + + " distinct=false}]])\n" + + " GraphLogicalProject(f=[f], distance=[distance]," + + " locationCity=[locationCity], companies=[CASE(IS NULL(company), null:NULL," + + " ARRAY(company.name, workAt.workFrom, country.name))], universities=[CASE(IS" + + " NULL(university), null:NULL, ARRAY(university.name, studyAt.classYear," + + " universityCity.name))], isAppend=[false])\n" + + " GraphLogicalSort(sort0=[distance], sort1=[f.lastName], sort2=[f.id]," + + " dir0=[ASC], dir1=[ASC], dir2=[ASC], fetch=[20])\n" + + " GraphLogicalAggregate(keys=[{variables=[f, company, university," + + " workAt, country, studyAt, universityCity, locationCity], aliases=[f," + + " company, university, workAt, country, studyAt, universityCity," + + " locationCity]}], values=[[{operands=[len], aggFunction=MIN," + + " alias='distance', distinct=false}]])\n" + + " GraphLogicalProject(f=[f], company=[company]," + + " university=[university], workAt=[workAt], country=[country]," + + " studyAt=[studyAt], universityCity=[universityCity]," + + " locationCity=[locationCity], len=[k.~len], isAppend=[false])\n" + + " GraphPhysicalExpand(tableConfig=[[EdgeLabel(ISLOCATEDIN, PERSON," + + " PLACE)]], alias=[locationCity], startAlias=[f], opt=[OUT]," + + " physicalOpt=[VERTEX])\n" + + " GraphPhysicalExpand(tableConfig=[[EdgeLabel(ISLOCATEDIN," + " ORGANISATION, PLACE)]], alias=[universityCity], opt=[OUT]," + " physicalOpt=[VERTEX], optional=[true])\n" - + " GraphLogicalGetV(tableConfig=[{isAll=false," + + " GraphLogicalGetV(tableConfig=[{isAll=false," + " tables=[ORGANISATION]}], alias=[university], opt=[END])\n" - + " GraphLogicalExpand(tableConfig=[{isAll=false," + + " GraphLogicalExpand(tableConfig=[{isAll=false," + " tables=[STUDYAT]}], alias=[studyAt], startAlias=[f], opt=[OUT]," + " optional=[true])\n" - + " GraphLogicalAggregate(keys=[{variables=[f, distance]," - + " aliases=[f, distance]}], values=[[{operands=[companies]," - + " aggFunction=COLLECT, alias='company_info', distinct=false}]])\n" - + " GraphLogicalProject(f=[f], distance=[distance]," - + " companies=[CASE(IS NULL(company), null:NULL, ARRAY(company.name," - + " workAt.workFrom, country.name))], isAppend=[false])\n" - + " GraphPhysicalExpand(tableConfig=[[EdgeLabel(ISLOCATEDIN," + + " GraphPhysicalExpand(tableConfig=[[EdgeLabel(ISLOCATEDIN," + " ORGANISATION, PLACE)]], alias=[country], opt=[OUT], physicalOpt=[VERTEX]," + " optional=[true])\n" - + " GraphLogicalGetV(tableConfig=[{isAll=false," + + " GraphLogicalGetV(tableConfig=[{isAll=false," + " tables=[ORGANISATION]}], alias=[company], opt=[END])\n" - + " GraphLogicalExpand(tableConfig=[{isAll=false," + + " GraphLogicalExpand(tableConfig=[{isAll=false," + " tables=[WORKAT]}], alias=[workAt], startAlias=[f], opt=[OUT]," + " optional=[true])\n" - + " GraphLogicalSort(sort0=[distance]," - + " sort1=[f.lastName], sort2=[f.id], dir0=[ASC], dir1=[ASC], dir2=[ASC]," - + " fetch=[20])\n" - + " GraphLogicalProject(f=[f], distance=[k.~len]," - + " isAppend=[false])\n" - + " LogicalFilter(condition=[<>(f, p)])\n" - + " GraphLogicalGetV(tableConfig=[{isAll=false," + + " LogicalFilter(condition=[<>(p, f)])\n" + + " GraphLogicalGetV(tableConfig=[{isAll=false," + " tables=[PERSON]}], alias=[f], fusedFilter=[[=(_.firstName, ?1)]]," + " opt=[END])\n" - + " " + + " " + " GraphLogicalPathExpand(expand=[GraphLogicalExpand(tableConfig=[{isAll=false," + " tables=[KNOWS]}], alias=[_], opt=[BOTH])\n" + "], getV=[GraphLogicalGetV(tableConfig=[{isAll=false, tables=[PERSON]}]," + " alias=[_], opt=[OTHER])\n" - + "], offset=[1], fetch=[3], path_opt=[ANY_SHORTEST], result_opt=[ALL_V_E]," + + "], offset=[1], fetch=[3], path_opt=[ARBITRARY], result_opt=[ALL_V_E]," + " alias=[k], start_alias=[p])\n" - + " " + + " " + " GraphLogicalSource(tableConfig=[{isAll=false, tables=[PERSON]}], alias=[p]," + " opt=[VERTEX], uniqueKeyFilters=[=(_.id, ?0)])", after.explain().trim()); @@ -340,86 +357,86 @@ public void ldbc5_test() { RelNode before = com.alibaba.graphscope.cypher.antlr4.Utils.eval( "MATCH (person:PERSON { id: $personId })-[:KNOWS*1..3]-(friend)\n" - + "WITH DISTINCT friend\n" - + "WHERE friend.id <> $personId\n" - + "MATCH (friend)<-[membership:HASMEMBER]-(forum)\n" - + "WHERE membership.joinDate > $minDate\n" - + "CALL {\n" - + " WITH forum\n" - + " RETURN forum, 0 AS postCount\n" - + " ORDER BY forum.id ASC\n" - + " LIMIT 20\n" - + "}\n" - + "UNION\n" - + "CALL {\n" - + " WITH friend, collect(forum) AS forums\n" - + " MATCH" - + " (friend)<-[:HASCREATOR]-(post)<-[:CONTAINEROF]-(forum)\n" - + " WHERE forum IN forums\n" - + " WITH forum, count(post) AS postCount\n" - + " RETURN forum, postCount\n" - + " ORDER BY postCount DESC, forum.id ASC\n" - + " LIMIT 20\n" - + "}\n" - + "WITH forum, max(postCount) AS postCount\n" - + "RETURN forum, postCount\n" - + "ORDER BY postCount DESC, forum.id ASC\n" - + "LIMIT 20;", + + "WITH DISTINCT friend\n" + + "WHERE friend.id <> $personId\n" + + "MATCH (friend)<-[membership:HASMEMBER]-(forum)\n" + + "WHERE membership.joinDate > $minDate\n" + + "CALL {\n" + + " WITH forum\n" + + " RETURN forum, 0 AS postCount\n" + + " ORDER BY forum.id ASC\n" + + " LIMIT 20\n" + + "}\n" + + "UNION\n" + + "CALL {\n" + + " WITH friend, collect(forum) AS forums\n" + + " MATCH" + + " (friend)<-[:HASCREATOR]-(post)<-[:CONTAINEROF]-(forum)\n" + + " WHERE forum IN forums\n" + + " WITH forum, count(post) AS postCount\n" + + " RETURN forum, postCount\n" + + " ORDER BY postCount DESC, forum.id ASC\n" + + " LIMIT 20\n" + + "}\n" + + "WITH forum, max(postCount) AS postCount\n" + + "RETURN forum, postCount\n" + + "ORDER BY postCount DESC, forum.id ASC\n" + + "LIMIT 20;", builder) .build(); RelNode after = optimizer.optimize(before, new GraphIOProcessor(builder, irMeta)); Assert.assertEquals( "root:\n" - + "GraphLogicalSort(sort0=[postCount], sort1=[forum.id], dir0=[DESC]," - + " dir1=[ASC], fetch=[20])\n" - + " GraphLogicalProject(forum=[forum], postCount=[postCount]," - + " isAppend=[false])\n" - + " GraphLogicalAggregate(keys=[{variables=[forum], aliases=[forum]}]," - + " values=[[{operands=[postCount], aggFunction=MAX, alias='postCount'," - + " distinct=false}]])\n" - + " LogicalUnion(all=[true])\n" - + " GraphLogicalSort(sort0=[forum.id], dir0=[ASC], fetch=[20])\n" - + " GraphLogicalProject(forum=[forum], postCount=[0]," - + " isAppend=[false])\n" - + " GraphLogicalProject(forum=[forum], isAppend=[false])\n" - + " CommonTableScan(table=[[common#1874145243]])\n" - + " GraphLogicalSort(sort0=[postCount], sort1=[forum.id], dir0=[DESC]," - + " dir1=[ASC], fetch=[20])\n" - + " GraphLogicalProject(forum=[forum], postCount=[postCount]," - + " isAppend=[false])\n" - + " GraphLogicalAggregate(keys=[{variables=[forum]," - + " aliases=[forum]}], values=[[{operands=[post], aggFunction=COUNT," - + " alias='postCount', distinct=false}]])\n" - + " LogicalFilter(condition=[IN(forum, forums)])\n" - + " GraphPhysicalExpand(tableConfig=[{isAll=false," - + " tables=[CONTAINEROF]}], alias=[forum], opt=[IN], physicalOpt=[VERTEX])\n" - + " GraphPhysicalGetV(tableConfig=[{isAll=false," - + " tables=[POST]}], alias=[post], opt=[START], physicalOpt=[ITSELF])\n" - + " GraphPhysicalExpand(tableConfig=[[EdgeLabel(HASCREATOR," - + " POST, PERSON)]], alias=[_], startAlias=[friend], opt=[IN]," - + " physicalOpt=[VERTEX])\n" - + " GraphLogicalAggregate(keys=[{variables=[friend]," - + " aliases=[friend]}], values=[[{operands=[forum], aggFunction=COLLECT," - + " alias='forums', distinct=false}]])\n" - + " CommonTableScan(table=[[common#1874145243]])\n" - + "common#1874145243:\n" - + "GraphLogicalGetV(tableConfig=[{isAll=false, tables=[FORUM]}], alias=[forum]," - + " opt=[START])\n" - + " GraphLogicalExpand(tableConfig=[{isAll=false, tables=[HASMEMBER]}]," - + " alias=[membership], startAlias=[friend], fusedFilter=[[>(_.joinDate, ?1)]]," - + " opt=[IN])\n" - + " LogicalFilter(condition=[<>(friend.id, ?0)])\n" - + " GraphLogicalAggregate(keys=[{variables=[friend], aliases=[friend]}]," - + " values=[[]])\n" - + " GraphLogicalGetV(tableConfig=[{isAll=false, tables=[PERSON]}]," - + " alias=[friend], opt=[END])\n" - + " " - + " GraphLogicalPathExpand(fused=[GraphPhysicalExpand(tableConfig=[{isAll=false," - + " tables=[KNOWS]}], alias=[_], opt=[BOTH], physicalOpt=[VERTEX])\n" - + "], offset=[1], fetch=[2], path_opt=[ARBITRARY], result_opt=[END_V]," - + " alias=[_], start_alias=[person])\n" - + " GraphLogicalSource(tableConfig=[{isAll=false, tables=[PERSON]}]," - + " alias=[person], opt=[VERTEX], uniqueKeyFilters=[=(_.id, ?0)])", + + "GraphLogicalSort(sort0=[postCount], sort1=[forum.id], dir0=[DESC]," + + " dir1=[ASC], fetch=[20])\n" + + " GraphLogicalProject(forum=[forum], postCount=[postCount]," + + " isAppend=[false])\n" + + " GraphLogicalAggregate(keys=[{variables=[forum], aliases=[forum]}]," + + " values=[[{operands=[postCount], aggFunction=MAX, alias='postCount'," + + " distinct=false}]])\n" + + " LogicalUnion(all=[true])\n" + + " GraphLogicalSort(sort0=[forum.id], dir0=[ASC], fetch=[20])\n" + + " GraphLogicalProject(forum=[forum], postCount=[0]," + + " isAppend=[false])\n" + + " GraphLogicalProject(forum=[forum], isAppend=[false])\n" + + " CommonTableScan(table=[[common#1874145243]])\n" + + " GraphLogicalSort(sort0=[postCount], sort1=[forum.id], dir0=[DESC]," + + " dir1=[ASC], fetch=[20])\n" + + " GraphLogicalProject(forum=[forum], postCount=[postCount]," + + " isAppend=[false])\n" + + " GraphLogicalAggregate(keys=[{variables=[forum]," + + " aliases=[forum]}], values=[[{operands=[post], aggFunction=COUNT," + + " alias='postCount', distinct=false}]])\n" + + " LogicalFilter(condition=[IN(forum, forums)])\n" + + " GraphPhysicalExpand(tableConfig=[{isAll=false," + + " tables=[CONTAINEROF]}], alias=[forum], opt=[IN], physicalOpt=[VERTEX])\n" + + " GraphPhysicalGetV(tableConfig=[{isAll=false," + + " tables=[POST]}], alias=[post], opt=[START], physicalOpt=[ITSELF])\n" + + " GraphPhysicalExpand(tableConfig=[[EdgeLabel(HASCREATOR," + + " POST, PERSON)]], alias=[_], startAlias=[friend], opt=[IN]," + + " physicalOpt=[VERTEX])\n" + + " GraphLogicalAggregate(keys=[{variables=[friend]," + + " aliases=[friend]}], values=[[{operands=[forum], aggFunction=COLLECT," + + " alias='forums', distinct=false}]])\n" + + " CommonTableScan(table=[[common#1874145243]])\n" + + "common#1874145243:\n" + + "GraphLogicalGetV(tableConfig=[{isAll=false, tables=[FORUM]}], alias=[forum]," + + " opt=[START])\n" + + " GraphLogicalExpand(tableConfig=[{isAll=false, tables=[HASMEMBER]}]," + + " alias=[membership], startAlias=[friend], fusedFilter=[[>(_.joinDate, ?1)]]," + + " opt=[IN])\n" + + " LogicalFilter(condition=[<>(friend.id, ?0)])\n" + + " GraphLogicalAggregate(keys=[{variables=[friend], aliases=[friend]}]," + + " values=[[]])\n" + + " GraphLogicalGetV(tableConfig=[{isAll=false, tables=[PERSON]}]," + + " alias=[friend], opt=[END])\n" + + " " + + " GraphLogicalPathExpand(fused=[GraphPhysicalExpand(tableConfig=[{isAll=false," + + " tables=[KNOWS]}], alias=[_], opt=[BOTH], physicalOpt=[VERTEX])\n" + + "], offset=[1], fetch=[2], path_opt=[ARBITRARY], result_opt=[END_V]," + + " alias=[_], start_alias=[person])\n" + + " GraphLogicalSource(tableConfig=[{isAll=false, tables=[PERSON]}]," + + " alias=[person], opt=[VERTEX], uniqueKeyFilters=[=(_.id, ?0)])", com.alibaba.graphscope.common.ir.tools.Utils.toString(after).trim()); } @@ -684,100 +701,100 @@ public void ldbc10_test() { RelNode before = com.alibaba.graphscope.cypher.antlr4.Utils.eval( "MATCH (person:PERSON {id: $personId})-[:KNOWS*2..3]-(friend:" - + " PERSON)-[:ISLOCATEDIN]->(city:PLACE)\n" - + "WHERE \n" - + " NOT friend=person \n" - + " AND NOT (friend:PERSON)-[:KNOWS]-(person :PERSON" - + " {id: $personId})\n" - + "WITH \n" - + " person, \n" - + " city, \n" - + " friend, \n" - + " friend.birthday as birthday\n" - + "WITH DISTINCT friend, city, person\n" - + "\n" - + "OPTIONAL MATCH (friend :" - + " PERSON)<-[:HASCREATOR]-(post:POST)\n" - + "WITH friend, city, person, count(post) as postCount\n" - + "\n" - + "OPTIONAL MATCH" - + " (friend)<-[:HASCREATOR]-(post1:POST)-[:HASTAG]->(tag:TAG)<-[:HASINTEREST]-(person:" - + " PERSON {id: $personId})\n" - + "WITH friend, city, postCount, count(distinct post1) as" - + " commonPostCount\n" - + "\n" - + "RETURN friend.id AS personId,\n" - + " friend.firstName AS personFirstName,\n" - + " friend.lastName AS personLastName,\n" - + " commonPostCount - (postCount - commonPostCount) AS" - + " commonInterestScore,\n" - + " friend.gender AS personGender,\n" - + " city.name AS personCityName\n" - + "ORDER BY commonInterestScore DESC, personId ASC\n" - + "LIMIT 10;", + + " PERSON)-[:ISLOCATEDIN]->(city:PLACE)\n" + + "WHERE \n" + + " NOT friend=person \n" + + " AND NOT (friend:PERSON)-[:KNOWS]-(person :PERSON" + + " {id: $personId})\n" + + "WITH \n" + + " person, \n" + + " city, \n" + + " friend, \n" + + " friend.birthday as birthday\n" + + "WITH DISTINCT friend, city, person\n" + + "\n" + + "OPTIONAL MATCH (friend :" + + " PERSON)<-[:HASCREATOR]-(post:POST)\n" + + "WITH friend, city, person, count(post) as postCount\n" + + "\n" + + "OPTIONAL MATCH" + + " (friend)<-[:HASCREATOR]-(post1:POST)-[:HASTAG]->(tag:TAG)<-[:HASINTEREST]-(person:" + + " PERSON {id: $personId})\n" + + "WITH friend, city, postCount, count(distinct post1) as" + + " commonPostCount\n" + + "\n" + + "RETURN friend.id AS personId,\n" + + " friend.firstName AS personFirstName,\n" + + " friend.lastName AS personLastName,\n" + + " commonPostCount - (postCount - commonPostCount) AS" + + " commonInterestScore,\n" + + " friend.gender AS personGender,\n" + + " city.name AS personCityName\n" + + "ORDER BY commonInterestScore DESC, personId ASC\n" + + "LIMIT 10;", builder) .build(); RelNode after = optimizer.optimize(before, new GraphIOProcessor(builder, irMeta)); Assert.assertEquals( "GraphLogicalSort(sort0=[commonInterestScore], sort1=[personId], dir0=[DESC]," - + " dir1=[ASC], fetch=[10])\n" - + " GraphLogicalProject(personId=[friend.id]," - + " personFirstName=[friend.firstName], personLastName=[friend.lastName]," - + " commonInterestScore=[-(commonPostCount, -(postCount, commonPostCount))]," - + " personGender=[friend.gender], personCityName=[city.name]," - + " isAppend=[false])\n" - + " GraphLogicalAggregate(keys=[{variables=[friend, city, postCount]," - + " aliases=[friend, city, postCount]}], values=[[{operands=[post1]," - + " aggFunction=COUNT, alias='commonPostCount', distinct=true}]])\n" - + " LogicalJoin(condition=[AND(=(friend, friend), =(person, person))]," - + " joinType=[left])\n" - + " GraphLogicalAggregate(keys=[{variables=[friend, city, person]," - + " aliases=[friend, city, person]}], values=[[{operands=[post]," - + " aggFunction=COUNT, alias='postCount', distinct=false}]])\n" - + " GraphPhysicalGetV(tableConfig=[{isAll=false, tables=[POST]}]," - + " alias=[post], opt=[START], physicalOpt=[ITSELF])\n" - + " GraphPhysicalExpand(tableConfig=[[EdgeLabel(HASCREATOR, POST," - + " PERSON)]], alias=[_], startAlias=[friend], opt=[IN], physicalOpt=[VERTEX]," - + " optional=[true])\n" - + " GraphLogicalAggregate(keys=[{variables=[friend, city, person]," - + " aliases=[friend, city, person]}], values=[[]])\n" - + " GraphLogicalProject(person=[person], city=[city]," - + " friend=[friend], birthday=[friend.birthday], isAppend=[false])\n" - + " LogicalJoin(condition=[AND(=(person, person), =(friend," - + " friend))], joinType=[anti])\n" - + " LogicalFilter(condition=[<>(friend, person)])\n" - + " " - + " GraphPhysicalExpand(tableConfig=[[EdgeLabel(ISLOCATEDIN, PERSON, PLACE)]]," - + " alias=[city], startAlias=[friend], opt=[OUT], physicalOpt=[VERTEX])\n" - + " GraphLogicalGetV(tableConfig=[{isAll=false," - + " tables=[PERSON]}], alias=[friend], opt=[END])\n" - + " " - + " GraphLogicalPathExpand(fused=[GraphPhysicalExpand(tableConfig=[{isAll=false," - + " tables=[KNOWS]}], alias=[_], opt=[BOTH], physicalOpt=[VERTEX])\n" - + "], offset=[2], fetch=[1], path_opt=[ARBITRARY], result_opt=[END_V]," - + " alias=[_], start_alias=[person])\n" - + " GraphLogicalSource(tableConfig=[{isAll=false," - + " tables=[PERSON]}], alias=[person], opt=[VERTEX], uniqueKeyFilters=[=(_.id," - + " ?0)])\n" - + " GraphPhysicalExpand(tableConfig=[{isAll=false," - + " tables=[KNOWS]}], alias=[friend], startAlias=[person], opt=[BOTH]," - + " physicalOpt=[VERTEX])\n" - + " GraphLogicalSource(tableConfig=[{isAll=false," - + " tables=[PERSON]}], alias=[person], opt=[VERTEX], uniqueKeyFilters=[=(_.id," - + " ?0)])\n" - + " GraphPhysicalExpand(tableConfig=[[EdgeLabel(HASCREATOR, POST," - + " PERSON)]], alias=[friend], startAlias=[post1], opt=[OUT]," - + " physicalOpt=[VERTEX])\n" - + " GraphPhysicalGetV(tableConfig=[{isAll=false, tables=[POST]}]," - + " alias=[post1], opt=[START], physicalOpt=[ITSELF])\n" - + " GraphPhysicalExpand(tableConfig=[[EdgeLabel(HASTAG, POST," - + " TAG)]], alias=[_], startAlias=[tag], opt=[IN], physicalOpt=[VERTEX])\n" - + " GraphPhysicalExpand(tableConfig=[{isAll=false," - + " tables=[HASINTEREST]}], alias=[tag], startAlias=[person], opt=[OUT]," - + " physicalOpt=[VERTEX])\n" - + " GraphLogicalSource(tableConfig=[{isAll=false," - + " tables=[PERSON]}], alias=[person], opt=[VERTEX], uniqueKeyFilters=[=(_.id," - + " ?0)])", + + " dir1=[ASC], fetch=[10])\n" + + " GraphLogicalProject(personId=[friend.id]," + + " personFirstName=[friend.firstName], personLastName=[friend.lastName]," + + " commonInterestScore=[-(commonPostCount, -(postCount, commonPostCount))]," + + " personGender=[friend.gender], personCityName=[city.name]," + + " isAppend=[false])\n" + + " GraphLogicalAggregate(keys=[{variables=[friend, city, postCount]," + + " aliases=[friend, city, postCount]}], values=[[{operands=[post1]," + + " aggFunction=COUNT, alias='commonPostCount', distinct=true}]])\n" + + " LogicalJoin(condition=[AND(=(friend, friend), =(person, person))]," + + " joinType=[left])\n" + + " GraphLogicalAggregate(keys=[{variables=[friend, city, person]," + + " aliases=[friend, city, person]}], values=[[{operands=[post]," + + " aggFunction=COUNT, alias='postCount', distinct=false}]])\n" + + " GraphPhysicalGetV(tableConfig=[{isAll=false, tables=[POST]}]," + + " alias=[post], opt=[START], physicalOpt=[ITSELF])\n" + + " GraphPhysicalExpand(tableConfig=[[EdgeLabel(HASCREATOR, POST," + + " PERSON)]], alias=[_], startAlias=[friend], opt=[IN], physicalOpt=[VERTEX]," + + " optional=[true])\n" + + " GraphLogicalAggregate(keys=[{variables=[friend, city, person]," + + " aliases=[friend, city, person]}], values=[[]])\n" + + " GraphLogicalProject(person=[person], city=[city]," + + " friend=[friend], birthday=[friend.birthday], isAppend=[false])\n" + + " LogicalJoin(condition=[AND(=(person, person), =(friend," + + " friend))], joinType=[anti])\n" + + " LogicalFilter(condition=[<>(friend, person)])\n" + + " " + + " GraphPhysicalExpand(tableConfig=[[EdgeLabel(ISLOCATEDIN, PERSON, PLACE)]]," + + " alias=[city], startAlias=[friend], opt=[OUT], physicalOpt=[VERTEX])\n" + + " GraphLogicalGetV(tableConfig=[{isAll=false," + + " tables=[PERSON]}], alias=[friend], opt=[END])\n" + + " " + + " GraphLogicalPathExpand(fused=[GraphPhysicalExpand(tableConfig=[{isAll=false," + + " tables=[KNOWS]}], alias=[_], opt=[BOTH], physicalOpt=[VERTEX])\n" + + "], offset=[2], fetch=[1], path_opt=[ARBITRARY], result_opt=[END_V]," + + " alias=[_], start_alias=[person])\n" + + " GraphLogicalSource(tableConfig=[{isAll=false," + + " tables=[PERSON]}], alias=[person], opt=[VERTEX], uniqueKeyFilters=[=(_.id," + + " ?0)])\n" + + " GraphPhysicalExpand(tableConfig=[{isAll=false," + + " tables=[KNOWS]}], alias=[friend], startAlias=[person], opt=[BOTH]," + + " physicalOpt=[VERTEX])\n" + + " GraphLogicalSource(tableConfig=[{isAll=false," + + " tables=[PERSON]}], alias=[person], opt=[VERTEX], uniqueKeyFilters=[=(_.id," + + " ?0)])\n" + + " GraphPhysicalExpand(tableConfig=[[EdgeLabel(HASCREATOR, POST," + + " PERSON)]], alias=[friend], startAlias=[post1], opt=[OUT]," + + " physicalOpt=[VERTEX])\n" + + " GraphPhysicalGetV(tableConfig=[{isAll=false, tables=[POST]}]," + + " alias=[post1], opt=[START], physicalOpt=[ITSELF])\n" + + " GraphPhysicalExpand(tableConfig=[[EdgeLabel(HASTAG, POST," + + " TAG)]], alias=[_], startAlias=[tag], opt=[IN], physicalOpt=[VERTEX])\n" + + " GraphPhysicalExpand(tableConfig=[{isAll=false," + + " tables=[HASINTEREST]}], alias=[tag], startAlias=[person], opt=[OUT]," + + " physicalOpt=[VERTEX])\n" + + " GraphLogicalSource(tableConfig=[{isAll=false," + + " tables=[PERSON]}], alias=[person], opt=[VERTEX], uniqueKeyFilters=[=(_.id," + + " ?0)])", after.explain().trim()); } @@ -903,91 +920,4 @@ public void ldbc12_test() { + " uniqueKeyFilters=[=(_.id, 2199023382370)])", after.explain().trim()); } - - @Test - public void ldbc14_test() { - GraphBuilder builder = Utils.mockGraphBuilder(optimizer, irMeta); - RelNode before = - com.alibaba.graphscope.cypher.antlr4.Utils.eval( - "MATCH all ShortestPath((person1:PERSON { id: $person1Id" - + " })-[path:KNOWS*0..10]-(person2:PERSON { id: $person2Id" - + " }))\n" - + "WITH path, gs.function.relationships(path) as rels_in_path," - + " gs.function.nodes(path) as nodes_in_path\n" - + "UNWIND rels_in_path as rel\n" - + "WITH path, rels_in_path, nodes_in_path," - + " gs.function.startNode(rel) as rel0," - + " gs.function.endNode(rel) as rel1\n" - + "OPTIONAL MATCH" - + " (rel0:PERSON)<-[:HASCREATOR]-(n)-[:REPLYOF]-(m)-[:HASCREATOR]->(rel1:PERSON)\n" - + "With path, nodes_in_path, rels_in_path,\n" - + " CASE WHEN labels(m) <> labels(n) THEN 1 ELSE 0 END as" - + " ra,\n" - + " CASE WHEN labels(m) = labels(n) THEN 1 ELSE 0 END as" - + " rb\n" - + "With path, nodes_in_path, rels_in_path, SUM(ra) AS" - + " weight1Count, SUM(rb) as weight2Count\n" - + "UNWIND nodes_in_path as node\n" - + "WITH path, COLLECT(node.id) as personIdsInPath," - + " weight1Count, weight2Count\n" - + "RETURN personIdsInPath, (weight1Count +" - + " gs.function.toFloat(weight2Count) / 2) AS pathWeight\n" - + "ORDER BY pathWeight DESC;", - builder) - .build(); - RelNode after = optimizer.optimize(before, new GraphIOProcessor(builder, irMeta)); - Assert.assertEquals( - "root:\n" - + "GraphLogicalSort(sort0=[pathWeight], dir0=[DESC])\n" - + " GraphLogicalProject(personIdsInPath=[personIdsInPath]," - + " pathWeight=[+(weight1Count, /(gs.function.toFloat(weight2Count), 2))]," - + " isAppend=[false])\n" - + " GraphLogicalAggregate(keys=[{variables=[path, weight1Count," - + " weight2Count], aliases=[path, weight1Count, weight2Count]}]," - + " values=[[{operands=[node.id], aggFunction=COLLECT, alias='personIdsInPath'," - + " distinct=false}]])\n" - + " GraphLogicalUnfold(key=[nodes_in_path], alias=[node])\n" - + " GraphLogicalAggregate(keys=[{variables=[path, nodes_in_path," - + " rels_in_path], aliases=[path, nodes_in_path, rels_in_path]}]," - + " values=[[{operands=[ra], aggFunction=SUM, alias='weight1Count'," - + " distinct=false}, {operands=[rb], aggFunction=SUM, alias='weight2Count'," - + " distinct=false}]])\n" - + " GraphLogicalProject(path=[path], nodes_in_path=[nodes_in_path]," - + " rels_in_path=[rels_in_path], ra=[CASE(<>(m.~label, n.~label), 1, 0)]," - + " rb=[CASE(=(m.~label, n.~label), 1, 0)], isAppend=[false])\n" - + " MultiJoin(joinFilter=[=(m, m)], isFullOuterJoin=[false]," - + " joinTypes=[[INNER, INNER]], outerJoinConditions=[[NULL, NULL]]," - + " projFields=[[ALL, ALL]])\n" - + " GraphPhysicalExpand(tableConfig=[[EdgeLabel(REPLYOF, COMMENT," - + " POST), EdgeLabel(REPLYOF, COMMENT, COMMENT)]], alias=[m], opt=[BOTH]," - + " physicalOpt=[VERTEX], optional=[true])\n" - + " GraphPhysicalExpand(tableConfig=[{isAll=false," - + " tables=[HASCREATOR]}], alias=[n], startAlias=[rel0], opt=[IN]," - + " physicalOpt=[VERTEX], optional=[true])\n" - + " CommonTableScan(table=[[common#-1230129050]])\n" - + " GraphPhysicalExpand(tableConfig=[[EdgeLabel(HASCREATOR, POST," - + " PERSON), EdgeLabel(HASCREATOR, COMMENT, PERSON)]], alias=[m]," - + " startAlias=[rel1], opt=[IN], physicalOpt=[VERTEX], optional=[true])\n" - + " CommonTableScan(table=[[common#-1230129050]])\n" - + "common#-1230129050:\n" - + "GraphLogicalProject(path=[path], rels_in_path=[rels_in_path]," - + " nodes_in_path=[nodes_in_path], rel0=[gs.function.startNode(rel)]," - + " rel1=[gs.function.endNode(rel)], isAppend=[false])\n" - + " GraphLogicalUnfold(key=[rels_in_path], alias=[rel])\n" - + " GraphLogicalProject(path=[path]," - + " rels_in_path=[gs.function.relationships(path)]," - + " nodes_in_path=[gs.function.nodes(path)], isAppend=[false])\n" - + " GraphLogicalGetV(tableConfig=[{isAll=false, tables=[PERSON]}]," - + " alias=[person2], fusedFilter=[[=(_.id, ?1)]], opt=[END])\n" - + " " - + " GraphLogicalPathExpand(expand=[GraphLogicalExpand(tableConfig=[{isAll=false," - + " tables=[KNOWS]}], alias=[_], opt=[BOTH])\n" - + "], getV=[GraphLogicalGetV(tableConfig=[{isAll=false, tables=[PERSON]}]," - + " alias=[_], opt=[OTHER])\n" - + "], fetch=[10], path_opt=[ALL_SHORTEST], result_opt=[ALL_V_E], alias=[path]," - + " start_alias=[person1])\n" - + " GraphLogicalSource(tableConfig=[{isAll=false, tables=[PERSON]}]," - + " alias=[person1], opt=[VERTEX], uniqueKeyFilters=[=(_.id, ?0)])", - com.alibaba.graphscope.common.ir.tools.Utils.toString(after).trim()); - } } diff --git a/interactive_engine/compiler/src/test/resources/logback-test.xml b/interactive_engine/compiler/src/test/resources/logback-test.xml new file mode 100644 index 000000000000..6aa06705038f --- /dev/null +++ b/interactive_engine/compiler/src/test/resources/logback-test.xml @@ -0,0 +1,29 @@ + + + + +       +          [%d{ISO8601}][%p][%t][%c:%L] %m%n +       +     + + + + + \ No newline at end of file