Skip to content

Commit

Permalink
[GIE Compiler] Support Procedure Call in Cypher Queries (#2927)
Browse files Browse the repository at this point in the history
<!--
Thanks for your contribution! please review
https://github.com/alibaba/GraphScope/blob/main/CONTRIBUTING.md before
opening an issue.
-->

## What do these changes do?
1. support procedure call in cypher queries.
2. support `long` data type in cypher queries, i.e. `2l`.

<!-- Please give a short brief about these changes. -->

## Related issue number
#2686 

<!-- Are there any issues opened that will be resolved by merging this
change? -->

Fixes

---------

Co-authored-by: siyuan0322 <siyuanzhang.zsy@alibaba-inc.com>
Co-authored-by: Longbin Lai <longbin.lailb@alibaba-inc.com>
Co-authored-by: longbinlai <longbin.lai@gmail.com>
  • Loading branch information
4 people authored Jul 7, 2023
1 parent 7330954 commit 41ee79a
Show file tree
Hide file tree
Showing 30 changed files with 811 additions and 25 deletions.
3 changes: 3 additions & 0 deletions interactive_engine/compiler/conf/ir.compiler.properties
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ graph.schema: ../executor/ir/core/resource/modern_schema.json
graph.store: exp
graph.planner: {"isOn":true,"opt":"RBO","rules":["FilterMatchRule"]}

# set stored procedures uri
# graph.stored.procedures.uri: <your stored procedures path in uri format>

# disable the authentication if username or password not set
# auth.username: default
# auth.password: default
Expand Down
4 changes: 4 additions & 0 deletions interactive_engine/compiler/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@
<groupId>com.google.protobuf</groupId>
<artifactId>protobuf-java</artifactId>
</dependency>
<dependency>
<groupId>com.google.protobuf</groupId>
<artifactId>protobuf-java-util</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.pegasus</groupId>
<artifactId>pegasus-client</artifactId>
Expand Down
28 changes: 26 additions & 2 deletions interactive_engine/compiler/src/main/antlr4/CypherGS.g4
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,31 @@ oC_Statement
: oC_Query ;

oC_Query
: oC_RegularQuery ;
: oC_RegularQuery
| oC_StandaloneCall
;

oC_StandaloneCall
: CALL SP ( oC_ExplicitProcedureInvocation | oC_ImplicitProcedureInvocation ) ( SP? YIELD SP ( '*' ) )? ;

oC_ExplicitProcedureInvocation
: oC_ProcedureName SP? '(' SP? ( oC_Expression SP? ( ',' SP? oC_Expression SP? )* )? ')' ;

oC_ImplicitProcedureInvocation
: oC_ProcedureName ;

oC_ProcedureName
: oC_Namespace oC_SymbolicName ;

oC_Namespace
: ( oC_SymbolicName '.' )* ;

oC_ProcedureResultField
: oC_SymbolicName ;

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_With )* ( SP oC_Return ) ;
Expand Down Expand Up @@ -314,7 +338,7 @@ oC_NumberLiteral
oC_IntegerLiteral
: HexInteger
| OctalInteger
| DecimalInteger
| DecimalInteger ('l' | 'L') ?
;

HexInteger
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,6 @@
public class GraphConfig {
public static final Config<String> GRAPH_SCHEMA = Config.stringConfig("graph.schema", ".");
public static final Config<String> GRAPH_STORE = Config.stringConfig("graph.store", "exp");
public static final Config<String> GRAPH_STORED_PROCEDURES_URI =
Config.stringConfig("graph.stored.procedures.uri", "");
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
/*
* 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.procedure;

import com.alibaba.graphscope.common.ir.procedure.reader.StoredProceduresReader;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;

import org.apache.calcite.jdbc.JavaTypeFactoryImpl;
import org.apache.calcite.rel.type.*;
import org.apache.calcite.sql.type.SqlTypeName;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.yaml.snakeyaml.Yaml;

import java.io.IOException;
import java.net.URI;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

public class GraphStoredProcedures implements StoredProcedures {
private final RelDataTypeFactory typeFactory;
private final Map<String, StoredProcedureMeta> storedProcedureMetaMap;

public GraphStoredProcedures(StoredProceduresReader reader) throws IOException {
this.typeFactory = new JavaTypeFactoryImpl();
this.storedProcedureMetaMap = Maps.newLinkedHashMap();
for (URI uri : reader.getAllProcedureUris()) {
StoredProcedureMeta createdMeta =
createStoredProcedureMeta(reader.getProcedureMeta(uri));
this.storedProcedureMetaMap.put(createdMeta.getName(), createdMeta);
}
}

@Override
public @Nullable StoredProcedureMeta getStoredProcedure(String procedureName) {
return this.storedProcedureMetaMap.get(procedureName);
}

private StoredProcedureMeta createStoredProcedureMeta(String procedureMeta) throws IOException {
Yaml yaml = new Yaml();
Map<String, Object> config = yaml.load(procedureMeta);
String procedureName = (String) config.get("name");
return new StoredProcedureMeta(
procedureName,
createReturnType((List) config.get("returns")),
createParameters((List) config.get("params")));
}

private RelDataType createReturnType(List config) {
List<RelDataTypeField> fields = Lists.newArrayList();
Iterator iterator = config.iterator();
int index = 0;
while (iterator.hasNext()) {
Map<String, Object> field = (Map<String, Object>) iterator.next();
fields.add(
new RelDataTypeFieldImpl(
(String) field.get("name"),
index,
createDataType((String) field.get("type"))));
++index;
}
return new RelRecordType(fields);
}

private List<StoredProcedureMeta.Parameter> createParameters(List config) {
List<StoredProcedureMeta.Parameter> parameters = Lists.newArrayList();
Iterator iterator = config.iterator();
while (iterator.hasNext()) {
Map<String, Object> parameter = (Map<String, Object>) iterator.next();
parameters.add(
new StoredProcedureMeta.Parameter(
(String) parameter.get("name"),
createDataType((String) parameter.get("type"))));
}
return parameters;
}

private RelDataType createDataType(String typeString) {
typeString = typeString.toUpperCase().replaceAll("\\s*", "");
switch (typeString) {
case "STRING":
return typeFactory.createSqlType(SqlTypeName.CHAR);
case "INTEGER":
return typeFactory.createSqlType(SqlTypeName.INTEGER);
case "BOOLEAN":
return typeFactory.createSqlType(SqlTypeName.BOOLEAN);
case "FLOAT":
return typeFactory.createSqlType(SqlTypeName.FLOAT);
case "DOUBLE":
return typeFactory.createSqlType(SqlTypeName.DOUBLE);
case "LONG":
return typeFactory.createSqlType(SqlTypeName.BIGINT);
case "MULTISET(STRING)":
return typeFactory.createMultisetType(
typeFactory.createSqlType(SqlTypeName.CHAR), -1);
case "MULTISET(INTEGER)":
return typeFactory.createMultisetType(
typeFactory.createSqlType(SqlTypeName.INTEGER), -1);
case "MULTISET(BOOLEAN)":
return typeFactory.createMultisetType(
typeFactory.createSqlType(SqlTypeName.BOOLEAN), -1);
case "MULTISET(FLOAT)":
return typeFactory.createMultisetType(
typeFactory.createSqlType(SqlTypeName.FLOAT), -1);
case "MULTISET(DOUBLE)":
return typeFactory.createMultisetType(
typeFactory.createSqlType(SqlTypeName.DOUBLE), -1);
case "MULTISET(LONG)":
return typeFactory.createMultisetType(
typeFactory.createSqlType(SqlTypeName.BIGINT), -1);
case "ARRAY(STRING)":
return typeFactory.createArrayType(typeFactory.createSqlType(SqlTypeName.CHAR), -1);
case "ARRAY(INTEGER)":
return typeFactory.createArrayType(
typeFactory.createSqlType(SqlTypeName.INTEGER), -1);
case "ARRAY(BOOLEAN)":
return typeFactory.createArrayType(
typeFactory.createSqlType(SqlTypeName.BOOLEAN), -1);
case "ARRAY(FLOAT)":
return typeFactory.createArrayType(
typeFactory.createSqlType(SqlTypeName.FLOAT), -1);
case "ARRAY(DOUBLE)":
return typeFactory.createArrayType(
typeFactory.createSqlType(SqlTypeName.DOUBLE), -1);
case "ARRAY(LONG)":
return typeFactory.createArrayType(
typeFactory.createSqlType(SqlTypeName.BIGINT), -1);
default:
throw new UnsupportedOperationException("unsupported type: " + typeString);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
/*
* 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.procedure;

import org.apache.calcite.rel.type.RelDataType;

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

public class StoredProcedureMeta {
private final String name;
private final RelDataType returnType;
private final List<Parameter> parameters;

public StoredProcedureMeta(String name, RelDataType returnType, List<Parameter> parameters) {
this.name = name;
this.returnType = returnType;
this.parameters = Objects.requireNonNull(parameters);
}

public static class Parameter {
private final String name;
private final RelDataType dataType;

public Parameter(String name, RelDataType dataType) {
this.name = name;
this.dataType = dataType;
}

public String getName() {
return name;
}

public RelDataType getDataType() {
return dataType;
}

@Override
public String toString() {
return "Parameter{" + "name='" + name + '\'' + ", dataType=" + dataType + '}';
}
}

public String getName() {
return name;
}

public RelDataType getReturnType() {
return returnType;
}

public List<Parameter> getParameters() {
return Collections.unmodifiableList(parameters);
}

@Override
public String toString() {
return "StoredProcedureMeta{"
+ "name='"
+ name
+ '\''
+ ", returnType="
+ returnType
+ ", parameters="
+ parameters
+ '}';
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/*
* 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.procedure;

import org.checkerframework.checker.nullness.qual.Nullable;

public interface StoredProcedures {
@Nullable StoredProcedureMeta getStoredProcedure(String procedureName);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/*
* 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.procedure.reader;

import com.google.common.base.Preconditions;
import com.google.common.collect.Lists;

import org.apache.commons.io.FileUtils;

import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.nio.charset.StandardCharsets;
import java.util.Collections;
import java.util.List;

// a file implementation of StoredProceduresReader
public class StoredProceduresFileReader implements StoredProceduresReader {
private final URI rootUri;

public StoredProceduresFileReader(URI rootUri) {
this.rootUri = rootUri;
}

@Override
public List<URI> getAllProcedureUris() {
File rootDir = new File(rootUri);
Preconditions.checkArgument(rootDir.exists() && rootDir.isDirectory());
List<URI> procedureUris = Lists.newArrayList();
for (File file : rootDir.listFiles()) {
if (file.getName().endsWith(".yaml")) {
procedureUris.add(file.toURI());
}
}
return Collections.unmodifiableList(procedureUris);
}

@Override
public String getProcedureMeta(URI uri) throws IOException {
return FileUtils.readFileToString(new File(uri), StandardCharsets.UTF_8);
}
}
Loading

0 comments on commit 41ee79a

Please sign in to comment.