Skip to content

Commit

Permalink
[GIE Compiler] add arbitray array or map types to support ListLiteral…
Browse files Browse the repository at this point in the history
… or MapLiteral in cypher
  • Loading branch information
shirly121 committed Oct 18, 2023
1 parent bd0b988 commit 89c417b
Show file tree
Hide file tree
Showing 9 changed files with 270 additions and 14 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@

package com.alibaba.graphscope.common.ir.rex.operator;

import com.alibaba.graphscope.common.ir.type.GraphTypeFactoryImpl;

import org.apache.calcite.rel.type.RelDataType;
import org.apache.calcite.rel.type.RelDataTypeFactory;
import org.apache.calcite.sql.SqlCallBinding;
Expand Down Expand Up @@ -43,7 +45,11 @@ public RelDataType inferReturnType(SqlOperatorBinding opBinding) {
RelDataTypeFactory typeFactory = opBinding.getTypeFactory();
List<RelDataType> argTypes = opBinding.collectOperandTypes();
RelDataType componentType = getComponentType(typeFactory, argTypes);
return SqlTypeUtil.createArrayType(typeFactory, componentType, false);
if (componentType != null && componentType.getSqlTypeName() != SqlTypeName.ANY) {
return SqlTypeUtil.createArrayType(typeFactory, componentType, false);
} else {
return ((GraphTypeFactoryImpl) typeFactory).createArbitraryArrayType(argTypes, false);
}
}

// operands of array value constructor can be any, even if empty, i.e []
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@

package com.alibaba.graphscope.common.ir.rex.operator;

import com.alibaba.graphscope.common.ir.type.GraphTypeFactoryImpl;

import org.apache.calcite.rel.type.RelDataType;
import org.apache.calcite.rel.type.RelDataTypeFactory;
import org.apache.calcite.sql.SqlCallBinding;
Expand All @@ -24,7 +26,6 @@
import org.apache.calcite.sql.fun.SqlMultisetValueConstructor;
import org.apache.calcite.sql.type.SqlTypeName;
import org.apache.calcite.sql.type.SqlTypeUtil;
import org.apache.calcite.util.Pair;
import org.apache.calcite.util.Static;
import org.apache.calcite.util.Util;
import org.checkerframework.checker.nullness.qual.Nullable;
Expand All @@ -44,11 +45,16 @@ public SqlMapValueConstructor() {
public RelDataType inferReturnType(SqlOperatorBinding opBinding) {
RelDataTypeFactory typeFactory = opBinding.getTypeFactory();
List<RelDataType> argTypes = opBinding.collectOperandTypes();
Pair<RelDataType, RelDataType> type =
Pair.of(
getComponentType(typeFactory, Util.quotientList(argTypes, 2, 0)),
getComponentType(typeFactory, Util.quotientList(argTypes, 2, 1)));
return SqlTypeUtil.createMapType(opBinding.getTypeFactory(), type.left, type.right, false);
List<RelDataType> keyTypes = Util.quotientList(argTypes, 2, 0);
List<RelDataType> valueTypes = Util.quotientList(argTypes, 2, 1);
RelDataType keyType = typeFactory.leastRestrictive(keyTypes);
RelDataType valueType = getComponentType(typeFactory, valueTypes);
if (valueType != null && valueType.getSqlTypeName() != SqlTypeName.ANY) {
return SqlTypeUtil.createMapType(opBinding.getTypeFactory(), keyType, valueType, false);
} else {
return ((GraphTypeFactoryImpl) typeFactory)
.createArbitraryMapType(keyType, valueTypes, false);
}
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,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.MAP_VALUE_CONSTRUCTOR) {
return visitMapValueConstructor(call);
} else if (operator.getKind() == SqlKind.EXTRACT) {
return visitExtract(call);
} else if (call.getOperands().size() == 1) {
Expand Down Expand Up @@ -101,6 +103,35 @@ private OuterExpression.Expression visitArrayValueConstructor(RexCall call) {
.build();
}

private OuterExpression.Expression visitMapValueConstructor(RexCall call) {
OuterExpression.VariableKeyValues.Builder varMapBuilder =
OuterExpression.VariableKeyValues.newBuilder();
List<RexNode> operands = call.getOperands();
for (int i = 0; i < operands.size() - 1; i += 2) {
RexNode key = operands.get(i);
RexNode value = operands.get(i + 1);
Preconditions.checkArgument(
key instanceof RexLiteral,
"key type of 'MAP_VALUE_CONSTRUCTOR' should be 'literal', but is "
+ key.getClass());
Preconditions.checkArgument(
value instanceof RexGraphVariable,
"value type of 'MAP_VALUE_CONSTRUCTOR' should be 'variable', but is "
+ value.getClass());
varMapBuilder.addKeyVals(
OuterExpression.VariableKeyValue.newBuilder()
.setKey(key.accept(this).getOperators(0).getConst())
.setValue(value.accept(this).getOperators(0).getVar())
.build());
}
return OuterExpression.Expression.newBuilder()
.addOperators(
OuterExpression.ExprOpr.newBuilder()
.setMap(varMapBuilder)
.setNodeType(Utils.protoIrDataType(call.getType(), isColumnId)))
.build();
}

private OuterExpression.Expression visitExtract(RexCall call) {
List<RexNode> operands = call.getOperands();
Preconditions.checkArgument(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,7 @@ public static final DataType.IrDataType protoIrDataType(
+ " to IrDataType is unsupported yet");
case MULTISET:
case ARRAY:
case MAP:
logger.warn("multiset or array type can not be converted to any ir core data type");
return DataType.IrDataType.newBuilder().build();
default:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/*
* Copyright 2020 Alibaba Group Holding Limited.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.alibaba.graphscope.common.ir.type;

import org.apache.calcite.rel.type.RelDataType;
import org.apache.calcite.sql.type.AbstractSqlType;
import org.apache.calcite.sql.type.SqlTypeName;
import org.apache.commons.lang3.ObjectUtils;

import java.util.Collections;
import java.util.List;

/**
* introduce a new array type to allow different component types in a single array,
* to support {@code ListLiteral} in cypher, i.e. [a.name, a, b.age]
*/
public class ArbitraryArrayType extends AbstractSqlType {
private final List<RelDataType> componentTypes;

public ArbitraryArrayType(List<RelDataType> componentTypes, boolean isNullable) {
super(SqlTypeName.ARRAY, isNullable, null);
this.componentTypes = ObjectUtils.requireNonEmpty(componentTypes);
this.computeDigest();
}

@Override
protected void generateTypeString(StringBuilder sb, boolean withDetail) {
sb.append("(" + this.componentTypes.toString() + ") ARRAY");
}

public List<RelDataType> getComponentTypes() {
return Collections.unmodifiableList(componentTypes);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/*
* Copyright 2020 Alibaba Group Holding Limited.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.alibaba.graphscope.common.ir.type;

import org.apache.calcite.rel.type.RelDataType;
import org.apache.calcite.sql.type.AbstractSqlType;
import org.apache.calcite.sql.type.SqlTypeName;
import org.apache.commons.lang3.ObjectUtils;

import java.util.Collections;
import java.util.List;
import java.util.Objects;

/**
* introduce a new map type to allow different value types in a single map,
* to support {@code MapLiteral} in cypher, i.e. [name: a.name, a: a, age: b.age]
*/
public class ArbitraryMapType extends AbstractSqlType {
private final RelDataType keyType;
private final List<RelDataType> valueTypes;

protected ArbitraryMapType(
RelDataType keyType, List<RelDataType> valueTypes, boolean isNullable) {
super(SqlTypeName.MAP, isNullable, null);
this.keyType = Objects.requireNonNull(keyType);
this.valueTypes = ObjectUtils.requireNonEmpty(valueTypes);
this.computeDigest();
}

@Override
protected void generateTypeString(StringBuilder sb, boolean withDetail) {
sb.append("(" + keyType.toString() + ", " + valueTypes.toString() + ") MAP");
}

@Override
public RelDataType getKeyType() {
return this.keyType;
}

public List<RelDataType> getValueTypes() {
return Collections.unmodifiableList(this.valueTypes);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import org.apache.calcite.rel.type.RelDataType;

import java.nio.charset.Charset;
import java.util.List;

public class GraphTypeFactoryImpl extends JavaTypeFactoryImpl {
private final Configs configs;
Expand Down Expand Up @@ -63,4 +64,14 @@ public RelDataType createTypeWithNullability(RelDataType type, boolean nullable)
public Charset getDefaultCharset() {
return Charset.forName(FrontendConfig.CALCITE_DEFAULT_CHARSET.get(configs));
}

public RelDataType createArbitraryArrayType(
List<RelDataType> componentTypes, boolean isNullable) {
return new ArbitraryArrayType(componentTypes, isNullable);
}

public RelDataType createArbitraryMapType(
RelDataType keyType, List<RelDataType> valueTypes, boolean isNullable) {
return new ArbitraryMapType(keyType, valueTypes, isNullable);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,38 @@ public ExprVisitorResult visitOC_ListLiteral(CypherGSParser.OC_ListLiteralContex
builder.call(GraphStdOperatorTable.ARRAY_VALUE_CONSTRUCTOR, expressions));
}

@Override
public ExprVisitorResult visitOC_MapLiteral(CypherGSParser.OC_MapLiteralContext ctx) {
List<String> keys =
ctx.oC_PropertyKeyName().stream()
.map(k -> k.getText())
.collect(Collectors.toList());
List<ExprVisitorResult> values =
ctx.oC_Expression().stream()
.map(k -> visitOC_Expression(k))
.collect(Collectors.toList());
Preconditions.checkArgument(
keys.size() == values.size(),
"keys size="
+ keys.size()
+ " is not consistent with values size="
+ values.size()
+ " in MapLiteral");
List<RelBuilder.AggCall> aggCallList = Lists.newArrayList();
List<RexNode> expressions = Lists.newArrayList();
for (int i = 0; i < keys.size(); ++i) {
ExprVisitorResult valueExpr = values.get(i);
if (!valueExpr.getAggCalls().isEmpty()) {
aggCallList.addAll(valueExpr.getAggCalls());
}
expressions.add(builder.literal(keys.get(i)));
expressions.add(valueExpr.getExpr());
}
return new ExprVisitorResult(
aggCallList,
builder.call(GraphStdOperatorTable.MAP_VALUE_CONSTRUCTOR, expressions));
}

@Override
public ExprVisitorResult visitOC_Parameter(CypherGSParser.OC_ParameterContext ctx) {
String paramName = ctx.oC_SymbolicName().getText();
Expand Down
Loading

0 comments on commit 89c417b

Please sign in to comment.