Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 17 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,21 +16,25 @@ See the License for the specific language governing permissions and
limitations under the License.
{% endcomment %}
-->
[![Travis Build Status](https://travis-ci.org/apache/calcite.svg?branch=master)](https://travis-ci.org/apache/calcite)
[![AppVeyor Build Status](https://ci.appveyor.com/api/projects/status/github/apache/calcite?svg=true&branch=master)](https://ci.appveyor.com/project/ApacheSoftwareFoundation/calcite)
[![Travis Build Status](https://travis-ci.com/yunpengn/calcite.svg?branch=master)](https://travis-ci.com/yunpengn/calcite)
[![AppVeyor Build status](https://ci.appveyor.com/api/projects/status/qo30vjfl2rwsapnx?svg=true)](https://ci.appveyor.com/project/yunpengn/calcite)

# Apache Calcite

Apache Calcite is a dynamic data management framework.
This is a forked version of the [Apache Calcite](http://calcite.apache.org) framework, with enhancements on outer join reorderability. _We do NOT guarantee compatibility with its upstream version._

It contains many of the pieces that comprise a typical
database management system but omits the storage primitives.
It provides an industry standard SQL parser and validator,
a customisable optimizer with pluggable rules and cost functions,
logical and physical algebraic operators, various transformation
algorithms from SQL to algebra (and the opposite), and many
adapters for executing SQL queries over Cassandra, Druid,
Elasticsearch, MongoDB, Kafka, and others, with minimal
configuration.
This [repository](https://github.com/yunpengn/calcite) is currently maintained by **[Yunpeng Niu](https://github.com/yunpengn)**.

For more details, see the [home page](http://calcite.apache.org).
## Development Environment Setup

- Install the latest version of [IntelliJ IDEA](https://www.jetbrains.com/idea/) by [JetBrains](https://www.jetbrains.com/).
- Clone the repository by `git clone git@github.com:yunpengn/calcite.git`.
- Navigate to the cloned folder by `cd calcite/`.
- Import all dependencies by `./mvnw -DskipTests clean install`.
- This step may take a long time and need stable Internet connection. Please be patient.
- Open the IDE and import the project.
- Start coding!

## Licence

[Apache Licence 2.0](LICENSE)
265 changes: 265 additions & 0 deletions core/src/main/java/org/apache/calcite/Runner.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,265 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to you 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 org.apache.calcite;

import org.apache.calcite.adapter.enumerable.EnumerableConvention;
import org.apache.calcite.adapter.enumerable.EnumerableRules;
import org.apache.calcite.adapter.java.ReflectiveSchema;
import org.apache.calcite.config.Lex;
import org.apache.calcite.plan.RelOptUtil;
import org.apache.calcite.plan.RelTraitSet;
import org.apache.calcite.rel.RelNode;
import org.apache.calcite.rel.rel2sql.RelToSqlConverter;
import org.apache.calcite.rel.rules.JoinCommuteRule;
import org.apache.calcite.rel.rules.custom.AsscomInnerOuterRule;
import org.apache.calcite.rel.rules.custom.AsscomOuterInnerRule;
import org.apache.calcite.rel.rules.custom.AsscomOuterOuterRule;
import org.apache.calcite.rel.rules.custom.AssocInnerOuterRule;
import org.apache.calcite.rel.rules.custom.AssocOuterInnerRule;
import org.apache.calcite.rel.rules.custom.NullifyJoinRule;
import org.apache.calcite.schema.SchemaPlus;
import org.apache.calcite.sql.SqlDialect;
import org.apache.calcite.sql.SqlNode;
import org.apache.calcite.sql.parser.SqlParser;
import org.apache.calcite.tools.FrameworkConfig;
import org.apache.calcite.tools.Frameworks;
import org.apache.calcite.tools.Planner;
import org.apache.calcite.tools.Program;
import org.apache.calcite.tools.Programs;

/**
* A runner class for manual testing.
*/
public class Runner {
// Defines the default dialect used in this class.
private static final SqlDialect DEFAULT_DIALECT
= SqlDialect.DatabaseProduct.MYSQL.getDialect();

// A counter for the number of transactions executed so far.
private static int count = 1;

public static void main(String[] args) throws Exception {
// 1. A single inner join.
String sqlQuery = "select e.name, d.depName "
+ "from p.employees e join p.departments d on e.depID = d.depID";
Program programs = Programs.ofRules(
EnumerableRules.ENUMERABLE_PROJECT_RULE,
EnumerableRules.ENUMERABLE_JOIN_RULE);
buildAndTransformQuery(programs, sqlQuery, false);

// 2. A single left outer join.
sqlQuery = "select e.name, d.depName "
+ "from p.employees e left join p.departments d on e.depID = d.depID";
programs = Programs.ofRules(
NullifyJoinRule.INSTANCE,
EnumerableRules.ENUMERABLE_PROJECT_RULE,
EnumerableRules.ENUMERABLE_JOIN_RULE);
buildAndTransformQuery(programs, sqlQuery, false);

// 3. Two joins (left outer join + inner join) - for Rule 21.
sqlQuery = "select e.name, d.depName, c.cmpName "
+ "from p.employees e "
+ "left join p.departments d on e.depID = d.depID "
+ "inner join p.companies c on d.cmpID = c.cmpID";
programs = Programs.ofRules(
AssocOuterInnerRule.INSTANCE,
EnumerableRules.ENUMERABLE_PROJECT_RULE,
EnumerableRules.ENUMERABLE_JOIN_RULE);
buildAndTransformQuery(programs, sqlQuery, true);

// 4. Two joins (inner join + left outer join) - for Rule 22.
sqlQuery = "select e.name, d.depName, c.cmpName "
+ "from p.departments d "
+ "inner join p.employees e on d.depID = e.depID "
+ "right join p.companies c on d.cmpID = c.cmpID";
programs = Programs.ofRules(
AssocInnerOuterRule.INSTANCE,
JoinCommuteRule.SWAP_OUTER,
EnumerableRules.ENUMERABLE_PROJECT_RULE,
EnumerableRules.ENUMERABLE_JOIN_RULE);
buildAndTransformQuery(programs, sqlQuery, true);

// 5. Two joins (left outer join + inner join) - for Rule 23.
sqlQuery = "select e.name, d.depName, c.cmpName "
+ "from p.employees e "
+ "left join p.departments d on e.depID = d.depID "
+ "inner join p.companies c on d.cmpID = c.cmpID";
programs = Programs.ofRules(
AsscomOuterInnerRule.INSTANCE,
JoinCommuteRule.INSTANCE,
EnumerableRules.ENUMERABLE_PROJECT_RULE,
EnumerableRules.ENUMERABLE_JOIN_RULE);
buildAndTransformQuery(programs, sqlQuery, true);

// 6. Two joins (inner join + left outer join) - for Rule 24.
sqlQuery = "select e.name, d.depName, c.cmpName "
+ "from p.employees e "
+ "inner join p.departments d on e.depID = d.depID "
+ "right join p.companies c on d.cmpID = c.cmpID";
programs = Programs.ofRules(
AsscomInnerOuterRule.INSTANCE,
JoinCommuteRule.SWAP_OUTER,
EnumerableRules.ENUMERABLE_PROJECT_RULE,
EnumerableRules.ENUMERABLE_JOIN_RULE);
buildAndTransformQuery(programs, sqlQuery, true);

// 7. Two joins (left outer join + left outer join) - for Rule 25.
sqlQuery = "select e.name, d.depName, c.cmpName "
+ "from p.employees e "
+ "left join p.departments d on e.depID = d.depID "
+ "right join p.companies c on d.cmpID = c.cmpID";
programs = Programs.ofRules(
AsscomOuterOuterRule.INSTANCE,
JoinCommuteRule.SWAP_OUTER,
EnumerableRules.ENUMERABLE_PROJECT_RULE,
EnumerableRules.ENUMERABLE_JOIN_RULE);
buildAndTransformQuery(programs, sqlQuery, true);
}

/**
* This method emulates the whole life cycle of a given SQL query: parse, validate build and
* transform. It will close the planner after usage.
*
* @param programs is the set of transformation rules to be used.
* @param sqlQuery is the original SQL query in its string representation.
* @param ignoreTypeCheck indicates whether type check should be turned off.
* @throws Exception when there is error during any step.
*/
private static void buildAndTransformQuery(final Program programs,
final String sqlQuery, final boolean ignoreTypeCheck) throws Exception {
// Builds the schema.
final SchemaPlus rootSchema = Frameworks.createRootSchema(true);
final SchemaPlus defaultSchema = rootSchema.add("p", new ReflectiveSchema(new People()));

// Creates the planner.
final SqlParser.Config parserConfig = SqlParser.configBuilder().setLex(Lex.MYSQL).build();
final FrameworkConfig config = Frameworks.newConfigBuilder()
.parserConfig(parserConfig)
.defaultSchema(defaultSchema)
.programs(programs)
.build();
final Planner planner = Frameworks.getPlanner(config);

System.out.println("============================ Start ============================");
System.out.println("Transaction ID: " + count++ + "\n");

// Prints the original SQL query string.
System.out.println("Input query:");
System.out.println(sqlQuery + "\n");
if (ignoreTypeCheck) {
RelOptUtil.disableTypeCheck = true;
}

// Parses, validates and builds the query.
final SqlNode parse = planner.parse(sqlQuery);
final SqlNode validate = planner.validate(parse);
final RelNode relNode = planner.rel(validate).rel;
System.out.println("Before transformation:");
System.out.println(RelOptUtil.toString(relNode));

// Transforms the query.
RelTraitSet traitSet = relNode.getTraitSet().replace(EnumerableConvention.INSTANCE);
RelNode transformedNode = planner.transform(0, traitSet, relNode);
System.out.println("After transformation:");
System.out.println(RelOptUtil.toString(transformedNode));
if (ignoreTypeCheck) {
RelOptUtil.disableTypeCheck = false;
}

// Converts the transformed relational expression back to SQL query string.
final RelToSqlConverter converter = new RelToSqlConverter(DEFAULT_DIALECT);
final SqlNode transformedSqlNode = converter.visitChild(0, transformedNode).asStatement();
final String transformedSqlQuery = transformedSqlNode.toSqlString(DEFAULT_DIALECT).getSql();
System.out.println("Output query:");
System.out.println(transformedSqlQuery + "\n");
System.out.println("============================= End =============================\n");

// Closes the planner.
planner.close();
}

/**
* Represents the database named company. */
public static class People {
public final Employee[] employees = {
new Employee(10, 1, "Daniel"),
new Employee(20, 1, "Mark"),
new Employee(30, 2, "Smith"),
new Employee(40, 3, "Armstrong"),
new Employee(50, 2, "Gabriel"),
new Employee(60, 5, "Daniel"),
new Employee(70, 7, "Joe"),
new Employee(80, 2, "Kim"),
new Employee(90, 1, "Gino")
};

public final Department[] departments = {
new Department(1, "Engineering", 100),
new Department(2, "Finance", 100)
};

public final Company[] companies = {
new Company(100, "All Link Pte Ltd"),
new Company(200, "")
};
}

/**
* Represents the schema of the employee table. */
public static class Employee {
public final int empID;
public final int depID;
public final String name;

Employee(int empID, int depID, String name) {
this.empID = empID;
this.depID = depID;
this.name = name;
}
}

/**
* Represents the schema of the department table. */
public static class Department {
public final int depID;
public final String depName;
public final int cmpID;

Department(int depID, String depName, int cmpID) {
this.depID = depID;
this.depName = depName;
this.cmpID = cmpID;
}
}

/**
* Represents the schema of the company table. */
public static class Company {
public final int cmpID;
public final String cmpName;

Company(int cmpID, String cmpName) {
this.cmpID = cmpID;
this.cmpName = cmpName;
}
}

private Runner() {
}
}

// End Runner.java
36 changes: 35 additions & 1 deletion core/src/main/java/org/apache/calcite/plan/RelOptUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import javax.annotation.Nonnull;

/**
Expand All @@ -138,6 +139,11 @@ public abstract class RelOptUtil {

public static final double EPSILON = 1.0e-5;

/**
* A feature flag to decide whether we should disable typeCheck temporarily.
*/
public static boolean disableTypeCheck = false;

@SuppressWarnings("Guava")
@Deprecated // to be removed before 2.0
public static final com.google.common.base.Predicate<Filter>
Expand Down Expand Up @@ -348,7 +354,7 @@ public static boolean areRowTypesEqual(
|| type2.getSqlTypeName() == SqlTypeName.ANY) {
continue;
}
if (!type1.equals(type2)) {
if (!type1.equals(type2) && !disableTypeCheck) {
return false;
}
}
Expand Down Expand Up @@ -1046,6 +1052,34 @@ public static RexNode splitCorrelatedFilterCondition(
filter.getCluster().getRexBuilder(), nonEquiList, true);
}

/**
* Checks whether a given predicate is referring to any attribute in a given list.
*
*
* @param condition is the given predicate.
* @param fields is the given list of attributes.
* @return true if not referring to any attribute; false otherwise.
*/
public static boolean isNotReferringTo(RexNode condition, List<RelDataTypeField> fields) {
if (!(condition instanceof RexCall)) {
return false;
}

// Converts to RexNode format.
Set<RexNode> set = fields.stream()
.map(field -> new RexInputRef(field.getIndex(), field.getType()))
.collect(Collectors.toSet());

// Checks whether the set contains each attribute.
RexCall call = (RexCall) condition;
for (RexNode attribute: call.getOperands()) {
if (set.contains(attribute)) {
return false;
}
}
return true;
}

private static void splitJoinCondition(
List<RelDataTypeField> sysFieldList,
List<RelNode> inputs,
Expand Down
Loading