diff --git a/interactive_engine/common/src/main/java/com/alibaba/graphscope/groot/common/util/IrSchemaParser.java b/interactive_engine/common/src/main/java/com/alibaba/graphscope/groot/common/util/IrSchemaParser.java index 3739691531be..b9994a984a92 100644 --- a/interactive_engine/common/src/main/java/com/alibaba/graphscope/groot/common/util/IrSchemaParser.java +++ b/interactive_engine/common/src/main/java/com/alibaba/graphscope/groot/common/util/IrSchemaParser.java @@ -144,9 +144,11 @@ private int getDataTypeId(DataType dataType) { return 8; case STRING_LIST: return 9; - case UNKNOWN: + case DATE: + return 12; default: - return 11; + throw new UnsupportedOperationException( + "convert from DataType " + dataType + " to ir core is unsupported yet"); } } } diff --git a/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/common/ir/meta/schema/GraphOptTable.java b/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/common/ir/meta/schema/GraphOptTable.java index 452f7e2d06ba..4c9fed3c3b16 100644 --- a/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/common/ir/meta/schema/GraphOptTable.java +++ b/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/common/ir/meta/schema/GraphOptTable.java @@ -108,14 +108,21 @@ private RelDataType deriveType(GraphProperty property) { switch (property.getDataType()) { case BOOL: return typeFactory.createSqlType(SqlTypeName.BOOLEAN); + case CHAR: case STRING: return typeFactory.createSqlType(SqlTypeName.CHAR); + case SHORT: case INT: return typeFactory.createSqlType(SqlTypeName.INTEGER); case LONG: return typeFactory.createSqlType(SqlTypeName.BIGINT); + case FLOAT: + return typeFactory.createSqlType(SqlTypeName.FLOAT); case DOUBLE: return typeFactory.createSqlType(SqlTypeName.DOUBLE); + case DATE: + return typeFactory.createSqlType( + SqlTypeName.DATE); // todo: support Time and DateTime in GraphSchema default: throw new UnsupportedOperationException( "type " + property.getDataType().name() + " not supported"); diff --git a/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/common/ir/meta/schema/Utils.java b/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/common/ir/meta/schema/Utils.java index ca8d2f32576c..58990d564689 100644 --- a/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/common/ir/meta/schema/Utils.java +++ b/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/common/ir/meta/schema/Utils.java @@ -153,12 +153,14 @@ public static DataType toDataType(Object type) { throw new UnsupportedOperationException( "unsupported primitive type: " + value); } - } else { - throw new UnsupportedOperationException("unsupported type: " + type); + } else if ((value = typeMap.get("date")) instanceof Map) { + Object format = ((Map) value).get("date_format"); + if (format != null && format.toString().equals("DF_YYYY_MM_DD")) { + return DataType.DATE; + } } - } else { - throw new UnsupportedOperationException("unsupported type: " + type); } + throw new UnsupportedOperationException("unsupported type: " + type); } /** @@ -259,8 +261,11 @@ public static final DataType toDataType(int ordinal) { return DataType.DOUBLE_LIST; case 9: return DataType.STRING_LIST; + case 12: + return DataType.DATE; default: - return DataType.UNKNOWN; + throw new UnsupportedOperationException( + "convert from ir core type " + ordinal + " to DataType is unsupported yet"); } } } diff --git a/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/common/ir/runtime/proto/RexToProtoConverter.java b/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/common/ir/runtime/proto/RexToProtoConverter.java index 0331ffb289cd..3e57e4e77d3f 100644 --- a/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/common/ir/runtime/proto/RexToProtoConverter.java +++ b/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/common/ir/runtime/proto/RexToProtoConverter.java @@ -28,6 +28,8 @@ import org.apache.calcite.sql.SqlKind; import org.apache.calcite.sql.SqlOperator; +import java.util.List; + /** * convert an expression in calcite to logical expression in ir_core */ @@ -49,6 +51,8 @@ public OuterExpression.Expression visitCall(RexCall call) { return visitCase(call); } else if (operator.getKind() == SqlKind.ARRAY_VALUE_CONSTRUCTOR) { return visitArrayValueConstructor(call); + } else if (operator.getKind() == SqlKind.EXTRACT) { + return visitExtract(call); } else if (call.getOperands().size() == 1) { return visitUnaryOperator(call); } else { @@ -97,6 +101,23 @@ private OuterExpression.Expression visitArrayValueConstructor(RexCall call) { .build(); } + private OuterExpression.Expression visitExtract(RexCall call) { + List operands = call.getOperands(); + Preconditions.checkArgument( + operands.size() == 2 && operands.get(0) instanceof RexLiteral, + "'EXTRACT' operator has invalid operands " + operands); + OuterExpression.Expression.Builder exprBuilder = OuterExpression.Expression.newBuilder(); + exprBuilder.addOperators( + OuterExpression.ExprOpr.newBuilder() + .setExtract( + OuterExpression.Extract.newBuilder() + .setInterval( + Utils.protoInterval((RexLiteral) operands.get(0))) + .setDataTime(operands.get(1).accept(this))) + .setNodeType(Utils.protoIrDataType(call.getType(), isColumnId))); + return exprBuilder.build(); + } + private OuterExpression.Expression visitUnaryOperator(RexCall call) { SqlOperator operator = call.getOperator(); RexNode operand = call.getOperands().get(0); diff --git a/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/common/ir/runtime/proto/Utils.java b/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/common/ir/runtime/proto/Utils.java index 77d03892c69a..df8395834f38 100644 --- a/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/common/ir/runtime/proto/Utils.java +++ b/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/common/ir/runtime/proto/Utils.java @@ -22,11 +22,14 @@ import com.alibaba.graphscope.gaia.proto.DataType; import com.alibaba.graphscope.gaia.proto.GraphAlgebra; import com.alibaba.graphscope.gaia.proto.OuterExpression; +import com.google.common.base.Preconditions; import com.google.protobuf.Int32Value; +import org.apache.calcite.avatica.util.TimeUnit; import org.apache.calcite.rel.type.RelDataType; import org.apache.calcite.rex.RexLiteral; import org.apache.calcite.sql.SqlOperator; +import org.apache.calcite.sql.type.SqlTypeName; import org.apache.calcite.util.NlsString; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -224,6 +227,8 @@ public static final Common.DataType protoBasicDataType(RelDataType basicType) { + elementType.getSqlTypeName() + " is unsupported yet"); } + case DATE: + return Common.DataType.DATE; default: throw new UnsupportedOperationException( "basic type " + basicType.getSqlTypeName() + " is unsupported yet"); @@ -328,4 +333,27 @@ public static final DataType.GraphDataType.GraphElementLabel protoElementLabel( } return builder.build(); } + + public static final OuterExpression.Extract.Interval protoInterval(RexLiteral literal) { + Preconditions.checkArgument( + literal.getType().getSqlTypeName() == SqlTypeName.SYMBOL, + "interval should be an literal of 'SYMBOL' type"); + TimeUnit timeUnit = literal.getValueAs(TimeUnit.class); + switch (timeUnit) { + case YEAR: + return OuterExpression.Extract.Interval.YEAR; + case MONTH: + return OuterExpression.Extract.Interval.MONTH; + case DAY: + return OuterExpression.Extract.Interval.DAY; + case HOUR: + return OuterExpression.Extract.Interval.HOUR; + case MINUTE: + return OuterExpression.Extract.Interval.MINUTE; + case SECOND: + return OuterExpression.Extract.Interval.SECOND; + default: + throw new UnsupportedOperationException("unsupported interval type " + timeUnit); + } + } } diff --git a/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/common/ir/tools/GraphBuilder.java b/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/common/ir/tools/GraphBuilder.java index c7702e49a58c..7cb79604a7a4 100644 --- a/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/common/ir/tools/GraphBuilder.java +++ b/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/common/ir/tools/GraphBuilder.java @@ -56,6 +56,7 @@ import org.apache.calcite.sql.SqlKind; import org.apache.calcite.sql.SqlOperator; import org.apache.calcite.sql.type.BasicSqlType; +import org.apache.calcite.sql.type.IntervalSqlType; import org.apache.calcite.sql.type.SqlTypeName; import org.apache.calcite.tools.RelBuilder; import org.apache.calcite.util.Litmus; @@ -580,6 +581,20 @@ private RexNode call_(SqlOperator operator, List operandList) { RelDataType returnType = operator.inferReturnType(callBinding); // derive unknown types of operands operandList = inferOperandTypes(operator, returnType, operandList); + if (operator.getKind() == SqlKind.EXTRACT) { + RexNode intervalOperand = operandList.get(0); + if (intervalOperand instanceof RexLiteral + && ((RexLiteral) intervalOperand).isNull() + && intervalOperand.getType() instanceof IntervalSqlType) { + IntervalSqlType intervalType = (IntervalSqlType) intervalOperand.getType(); + List newOperands = Lists.newArrayList(); + newOperands.add( + getRexBuilder() + .makeFlag(intervalType.getIntervalQualifier().getStartUnit())); + newOperands.add(operandList.get(1)); + operandList = newOperands; + } + } final RexBuilder builder = cluster.getRexBuilder(); return builder.makeCall(returnType, operator, operandList); } @@ -621,7 +636,8 @@ private boolean isCurrentSupported(SqlOperator operator) { || (sqlKind == SqlKind.NOT) || sqlKind == SqlKind.ARRAY_VALUE_CONSTRUCTOR || sqlKind == SqlKind.IS_NULL - || sqlKind == SqlKind.IS_NOT_NULL; + || sqlKind == SqlKind.IS_NOT_NULL + || sqlKind == SqlKind.EXTRACT; } @Override diff --git a/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/common/ir/tools/GraphStdOperatorTable.java b/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/common/ir/tools/GraphStdOperatorTable.java index bcc67b3375da..1959fa4571fb 100644 --- a/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/common/ir/tools/GraphStdOperatorTable.java +++ b/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/common/ir/tools/GraphStdOperatorTable.java @@ -217,4 +217,13 @@ public static final SqlFunction USER_DEFINED_PROCEDURE(StoredProcedureMeta meta) ReturnTypes.explicit(SqlTypeName.ANY).andThen(SqlTypeTransforms.TO_ARRAY), GraphInferTypes.FIRST_KNOWN, OperandTypes.ANY); + + public static final SqlFunction EXTRACT = + new SqlFunction( + "EXTRACT", + SqlKind.EXTRACT, + ReturnTypes.BIGINT_NULLABLE, + null, + GraphOperandTypes.INTERVALINTERVAL_INTERVALDATETIME, + SqlFunctionCategory.SYSTEM); } diff --git a/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/cypher/antlr4/visitor/ExpressionVisitor.java b/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/cypher/antlr4/visitor/ExpressionVisitor.java index e5694c808cd7..21a7b1e8d899 100644 --- a/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/cypher/antlr4/visitor/ExpressionVisitor.java +++ b/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/cypher/antlr4/visitor/ExpressionVisitor.java @@ -17,11 +17,13 @@ package com.alibaba.graphscope.cypher.antlr4.visitor; import com.alibaba.graphscope.common.ir.rel.type.group.GraphAggCall; +import com.alibaba.graphscope.common.ir.rex.RexGraphVariable; import com.alibaba.graphscope.common.ir.rex.RexTmpVariable; import com.alibaba.graphscope.common.ir.tools.GraphBuilder; import com.alibaba.graphscope.common.ir.tools.GraphRexBuilder; import com.alibaba.graphscope.common.ir.tools.GraphStdOperatorTable; import com.alibaba.graphscope.common.ir.type.GraphProperty; +import com.alibaba.graphscope.common.ir.type.GraphSchemaType; import com.alibaba.graphscope.cypher.antlr4.visitor.type.ExprVisitorResult; import com.alibaba.graphscope.grammar.CypherGSBaseVisitor; import com.alibaba.graphscope.grammar.CypherGSParser; @@ -31,12 +33,16 @@ import com.google.common.collect.Lists; import org.antlr.v4.runtime.tree.TerminalNode; +import org.apache.calcite.avatica.util.TimeUnit; import org.apache.calcite.rel.RelNode; import org.apache.calcite.rex.RexDynamicParam; +import org.apache.calcite.rex.RexLiteral; import org.apache.calcite.rex.RexNode; import org.apache.calcite.rex.RexSubQuery; +import org.apache.calcite.sql.SqlIntervalQualifier; import org.apache.calcite.sql.SqlKind; import org.apache.calcite.sql.SqlOperator; +import org.apache.calcite.sql.parser.SqlParserPos; import org.apache.calcite.tools.RelBuilder; import org.apache.commons.lang3.ObjectUtils; import org.checkerframework.checker.nullness.qual.Nullable; @@ -198,8 +204,16 @@ public ExprVisitorResult visitOC_PropertyOrLabelsExpression( throw new IllegalArgumentException("cannot get property from an literal"); } else { String aliasName = ctx.oC_Atom().oC_Variable().getText(); + RexGraphVariable variable = builder.variable(aliasName); String propertyName = ctx.oC_PropertyLookup().oC_PropertyKeyName().getText(); - return new ExprVisitorResult(builder.variable(aliasName, propertyName)); + RexNode expr = + (variable.getType() instanceof GraphSchemaType) + ? builder.variable(aliasName, propertyName) + : builder.call( + GraphStdOperatorTable.EXTRACT, + createIntervalLiteral(propertyName), + variable); + return new ExprVisitorResult(expr); } } } @@ -508,4 +522,11 @@ public int generate(@Nullable String paramName) { public ImmutableMap getDynamicParams() { return this.paramsBuilder.build(); } + + private RexLiteral createIntervalLiteral(String fieldName) { + TimeUnit timeUnit = TimeUnit.valueOf(fieldName.toUpperCase()); + SqlIntervalQualifier intervalQualifier = + new SqlIntervalQualifier(timeUnit, null, SqlParserPos.ZERO); + return builder.getRexBuilder().makeIntervalLiteral(null, intervalQualifier); + } } diff --git a/interactive_engine/compiler/src/main/java/org/apache/calcite/sql/type/GraphOperandTypes.java b/interactive_engine/compiler/src/main/java/org/apache/calcite/sql/type/GraphOperandTypes.java index 0499b4c7a8b5..3f5094a5f8e4 100644 --- a/interactive_engine/compiler/src/main/java/org/apache/calcite/sql/type/GraphOperandTypes.java +++ b/interactive_engine/compiler/src/main/java/org/apache/calcite/sql/type/GraphOperandTypes.java @@ -67,6 +67,9 @@ public abstract class GraphOperandTypes { public static final SqlSingleOperandTypeChecker BOOLEAN_BOOLEAN = family(SqlTypeFamily.BOOLEAN, SqlTypeFamily.BOOLEAN); + public static final SqlSingleOperandTypeChecker INTERVALINTERVAL_INTERVALDATETIME = + OperandTypes.or(INTERVAL_SAME_SAME, INTERVAL_DATETIME); + /** * create {@code RexFamilyOperandTypeChecker} to validate type based on {@code RexNode} * @param families diff --git a/interactive_engine/compiler/src/test/java/com/alibaba/graphscope/common/config/YamlConfigTest.java b/interactive_engine/compiler/src/test/java/com/alibaba/graphscope/common/config/YamlConfigTest.java index 0ac216a2821b..d230d3c5f238 100644 --- a/interactive_engine/compiler/src/test/java/com/alibaba/graphscope/common/config/YamlConfigTest.java +++ b/interactive_engine/compiler/src/test/java/com/alibaba/graphscope/common/config/YamlConfigTest.java @@ -81,10 +81,19 @@ public void schema_config_test() throws Exception { IrGraphSchema graphSchema = new IrGraphSchema(new LocalMetaDataReader(configs)); Assert.assertEquals( "DefaultGraphVertex{labelId=0, label=person," - + " propertyList=[DefaultGraphProperty{id=0, name=id, dataType=LONG}," - + " DefaultGraphProperty{id=1, name=name, dataType=STRING}," - + " DefaultGraphProperty{id=2, name=age, dataType=INT}], primaryKeyList=[id]}", + + " propertyList=[DefaultGraphProperty{id=0, name=id, dataType=LONG}," + + " DefaultGraphProperty{id=1, name=name, dataType=STRING}," + + " DefaultGraphProperty{id=2, name=age, dataType=INT}]," + + " primaryKeyList=[id]}", graphSchema.getElement("person").toString()); + Assert.assertEquals( + "DefaultGraphVertex{labelId=1, label=software," + + " propertyList=[DefaultGraphProperty{id=0, name=id, dataType=LONG}," + + " DefaultGraphProperty{id=1, name=name, dataType=STRING}," + + " DefaultGraphProperty{id=2, name=lang, dataType=STRING}," + + " DefaultGraphProperty{id=3, name=creationDate, dataType=DATE}]," + + " primaryKeyList=[id]}", + graphSchema.getElement("software").toString()); } @Test diff --git a/interactive_engine/compiler/src/test/java/com/alibaba/graphscope/cypher/antlr4/WhereTest.java b/interactive_engine/compiler/src/test/java/com/alibaba/graphscope/cypher/antlr4/WhereTest.java index aaaa6033a135..b7053dc7df52 100644 --- a/interactive_engine/compiler/src/test/java/com/alibaba/graphscope/cypher/antlr4/WhereTest.java +++ b/interactive_engine/compiler/src/test/java/com/alibaba/graphscope/cypher/antlr4/WhereTest.java @@ -22,6 +22,7 @@ import org.apache.calcite.plan.RelOptPlanner; import org.apache.calcite.rel.RelNode; import org.apache.calcite.rel.rules.CoreRules; +import org.apache.calcite.runtime.CalciteException; import org.junit.Assert; import org.junit.Test; @@ -262,4 +263,45 @@ public void where_8_test() { + "], matchOpt=[INNER])", after.explain().trim()); } + + @Test + public void where_9_test() { + RelNode where = + Utils.eval( + "Match (a:software) With a.creationDate as creationDate Where" + + " creationDate.month > 1990 and creationDate.day = 12 Return" + + " creationDate") + .build(); + Assert.assertEquals( + "GraphLogicalProject(creationDate=[creationDate], isAppend=[false])\n" + + " LogicalFilter(condition=[AND(>(EXTRACT(FLAG(MONTH), creationDate), 1990)," + + " =(EXTRACT(FLAG(DAY), creationDate), 12))])\n" + + " GraphLogicalProject(creationDate=[a.creationDate], isAppend=[false])\n" + + " GraphLogicalSource(tableConfig=[{isAll=false, tables=[software]}]," + + " alias=[a], opt=[VERTEX])", + where.explain().trim()); + } + + // expect to throw exceptions + @Test + public void where_10_test() { + try { + RelNode where = + Utils.eval( + "Match (a:software) With a.name as creationDate Where" + + " creationDate.month > 1990 and creationDate.day = 12" + + " Return creationDate") + .build(); + } catch (CalciteException e) { + Assert.assertTrue( + e.getMessage() + .contains( + "Cannot apply EXTRACT to arguments of type 'EXTRACT(, )'. Supported form(s):" + + " 'EXTRACT(, )'\n" + + "'EXTRACT(, )'")); + return; + } + Assert.fail("should have thrown exceptions for property 'name' is not a date type"); + } } diff --git a/interactive_engine/compiler/src/test/resources/config/modern/graph.yaml b/interactive_engine/compiler/src/test/resources/config/modern/graph.yaml index 9a0bf6cc535f..23df4e758bde 100644 --- a/interactive_engine/compiler/src/test/resources/config/modern/graph.yaml +++ b/interactive_engine/compiler/src/test/resources/config/modern/graph.yaml @@ -43,6 +43,11 @@ schema: property_name: lang property_type: primitive_type: DT_STRING + - property_id: 3 + property_name: creationDate + property_type: + date: + date_format: DF_YYYY_MM_DD primary_keys: - id edge_types: diff --git a/interactive_engine/compiler/src/test/resources/schema/modern.json b/interactive_engine/compiler/src/test/resources/schema/modern.json index 6b2a57ea36bd..4c8ed85fa7be 100644 --- a/interactive_engine/compiler/src/test/resources/schema/modern.json +++ b/interactive_engine/compiler/src/test/resources/schema/modern.json @@ -29,6 +29,14 @@ }, "data_type": 4, "is_primary_key": false + }, + { + "key": { + "id": 3, + "name": "creationDate" + }, + "data_type": 12, + "is_primary_key": false } ] }, diff --git a/interactive_engine/executor/ir/graph_proxy/src/utils/expr/eval_pred.rs b/interactive_engine/executor/ir/graph_proxy/src/utils/expr/eval_pred.rs index abcaff88b771..59d10fc542fa 100644 --- a/interactive_engine/executor/ir/graph_proxy/src/utils/expr/eval_pred.rs +++ b/interactive_engine/executor/ir/graph_proxy/src/utils/expr/eval_pred.rs @@ -490,9 +490,12 @@ fn process_predicates( } Item::Arith(_) => return Ok(None), Item::Param(param) => { - return Err(ExprError::Unsupported(format!("Dynamic Param {:?}", param))) + return Err(ExprError::unsupported(format!("Dynamic Param {:?}", param))) } Item::Case(case) => return Err(ExprError::unsupported(format!("Case When {:?}", case))), + Item::Extract(extract) => { + return Err(ExprError::unsupported(format!("Extract {:?}", extract))) + } } } } diff --git a/interactive_engine/executor/ir/proto/common.proto b/interactive_engine/executor/ir/proto/common.proto index 61195d37a28e..1c87785c8a53 100644 --- a/interactive_engine/executor/ir/proto/common.proto +++ b/interactive_engine/executor/ir/proto/common.proto @@ -85,4 +85,5 @@ enum DataType { STRING_ARRAY = 9; PAIR_ARRAY = 10; NONE = 11; -} \ No newline at end of file + DATE = 12; // todo: define `DATE` as message structure which contains date_format +} diff --git a/interactive_engine/executor/ir/proto/expr.proto b/interactive_engine/executor/ir/proto/expr.proto index c65250912967..fcaae8e745f9 100644 --- a/interactive_engine/executor/ir/proto/expr.proto +++ b/interactive_engine/executor/ir/proto/expr.proto @@ -140,6 +140,21 @@ message Case { Expression else_result_expression = 2; } +// extract interval from a given expression which should be of temporal type +message Extract { + enum Interval { + YEAR = 0; + MONTH = 1; + DAY = 2; + HOUR = 3; + MINUTE = 4; + SECOND = 5; + } + + Interval interval = 1; + Expression data_time = 2; +} + // An operator of expression is one of Logical, Arithmetic, Const and Variable. message ExprOpr { enum Brace { @@ -157,9 +172,10 @@ message ExprOpr { // dynamic param in expression DynamicParam param = 9; Case case = 10; + Extract extract = 11; } // The data of type of ExprOpr - common.IrDataType node_type = 11; + common.IrDataType node_type = 12; } // An inner representation of an expression