You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@calcite.apache.org by jh...@apache.org on 2018/02/16 21:51:12 UTC

[1/7] calcite git commit: Clean up SqlTypeAssignmentRules

Repository: calcite
Updated Branches:
  refs/heads/master 0ced3b7f5 -> 707f4de9c


Clean up SqlTypeAssignmentRules


Project: http://git-wip-us.apache.org/repos/asf/calcite/repo
Commit: http://git-wip-us.apache.org/repos/asf/calcite/commit/becb6dfb
Tree: http://git-wip-us.apache.org/repos/asf/calcite/tree/becb6dfb
Diff: http://git-wip-us.apache.org/repos/asf/calcite/diff/becb6dfb

Branch: refs/heads/master
Commit: becb6dfb85fad583d75aeaba13ac71d03bb5d4a8
Parents: fd68f25
Author: Julian Hyde <jh...@apache.org>
Authored: Mon Jan 8 19:58:41 2018 -0800
Committer: Julian Hyde <jh...@apache.org>
Committed: Fri Feb 16 10:18:01 2018 -0800

----------------------------------------------------------------------
 .../sql/type/SqlTypeAssignmentRules.java        | 464 ++++++++++---------
 .../apache/calcite/sql/type/SqlTypeUtil.java    |   4 +-
 .../org/apache/calcite/test/RexProgramTest.java |  17 +-
 3 files changed, 259 insertions(+), 226 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/calcite/blob/becb6dfb/core/src/main/java/org/apache/calcite/sql/type/SqlTypeAssignmentRules.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql/type/SqlTypeAssignmentRules.java b/core/src/main/java/org/apache/calcite/sql/type/SqlTypeAssignmentRules.java
index cd46c39..038ead3 100644
--- a/core/src/main/java/org/apache/calcite/sql/type/SqlTypeAssignmentRules.java
+++ b/core/src/main/java/org/apache/calcite/sql/type/SqlTypeAssignmentRules.java
@@ -16,80 +16,96 @@
  */
 package org.apache.calcite.sql.type;
 
+import com.google.common.base.Preconditions;
+import com.google.common.cache.CacheBuilder;
+import com.google.common.cache.CacheLoader;
+import com.google.common.cache.LoadingCache;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Sets;
+
 import java.util.EnumSet;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Map;
 import java.util.Set;
+import java.util.concurrent.ExecutionException;
+import javax.annotation.Nonnull;
 
 /**
- * Class to hold rules to determine if a type is assignable from another type.
- *
- * <p>REVIEW 7/05/04 Wael: We should split this up in Cast rules, symmetric and
- * asymmetric assignable rules
+ * Rules that determine whether a type is assignable from another type.
  */
 public class SqlTypeAssignmentRules {
   //~ Static fields/initializers ---------------------------------------------
 
-  private static SqlTypeAssignmentRules instance = null;
+  private static final SqlTypeAssignmentRules INSTANCE;
+  private static final SqlTypeAssignmentRules COERCE_INSTANCE;
 
-  private final Map<SqlTypeName, Set<SqlTypeName>> rules;
-  private final Map<SqlTypeName, Set<SqlTypeName>> coerceRules;
+  private final Map<SqlTypeName, ImmutableSet<SqlTypeName>> map;
 
   //~ Constructors -----------------------------------------------------------
 
-  private SqlTypeAssignmentRules() {
-    rules = new HashMap<>();
+  private SqlTypeAssignmentRules(
+      Map<SqlTypeName, ImmutableSet<SqlTypeName>> map) {
+    this.map = ImmutableMap.copyOf(map);
+  }
+
+  static {
+    final Builder rules = new Builder();
 
-    Set<SqlTypeName> rule;
+    final Set<SqlTypeName> rule = new HashSet<>();
 
     // IntervalYearMonth is assignable from...
     for (SqlTypeName interval : SqlTypeName.YEAR_INTERVAL_TYPES) {
-      rules.put(interval, SqlTypeName.YEAR_INTERVAL_TYPES);
+      rules.add(interval, SqlTypeName.YEAR_INTERVAL_TYPES);
     }
     for (SqlTypeName interval : SqlTypeName.DAY_INTERVAL_TYPES) {
-      rules.put(interval, SqlTypeName.DAY_INTERVAL_TYPES);
+      rules.add(interval, SqlTypeName.DAY_INTERVAL_TYPES);
+    }
+    for (SqlTypeName interval : SqlTypeName.DAY_INTERVAL_TYPES) {
+      final Set<SqlTypeName> dayIntervalTypes = SqlTypeName.DAY_INTERVAL_TYPES;
+      rules.add(interval, dayIntervalTypes);
     }
 
-    // Multiset is assignable from...
-    rules.put(SqlTypeName.MULTISET, EnumSet.of(SqlTypeName.MULTISET));
+    // MULTISET is assignable from...
+    rules.add(SqlTypeName.MULTISET, EnumSet.of(SqlTypeName.MULTISET));
 
-    // Tinyint is assignable from...
-    rules.put(SqlTypeName.TINYINT, EnumSet.of(SqlTypeName.TINYINT));
+    // TINYINT is assignable from...
+    rules.add(SqlTypeName.TINYINT, EnumSet.of(SqlTypeName.TINYINT));
 
-    // Smallint is assignable from...
-    rule = new HashSet<>();
+    // SMALLINT is assignable from...
+    rule.clear();
     rule.add(SqlTypeName.TINYINT);
     rule.add(SqlTypeName.SMALLINT);
-    rules.put(SqlTypeName.SMALLINT, rule);
+    rules.add(SqlTypeName.SMALLINT, rule);
 
-    // Int is assignable from...
-    rule = new HashSet<>();
+    // INTEGER is assignable from...
+    rule.clear();
     rule.add(SqlTypeName.TINYINT);
     rule.add(SqlTypeName.SMALLINT);
     rule.add(SqlTypeName.INTEGER);
-    rules.put(SqlTypeName.INTEGER, rule);
+    rules.add(SqlTypeName.INTEGER, rule);
 
-    // BigInt is assignable from...
-    rule = new HashSet<>();
+    // BIGINT is assignable from...
+    rule.clear();
     rule.add(SqlTypeName.TINYINT);
     rule.add(SqlTypeName.SMALLINT);
     rule.add(SqlTypeName.INTEGER);
     rule.add(SqlTypeName.BIGINT);
-    rules.put(SqlTypeName.BIGINT, rule);
+    rules.add(SqlTypeName.BIGINT, rule);
 
-    // Float is assignable from...
-    rule = new HashSet<>();
+    // FLOAT (up to 64 bit floating point) is assignable from...
+    rule.clear();
     rule.add(SqlTypeName.TINYINT);
     rule.add(SqlTypeName.SMALLINT);
     rule.add(SqlTypeName.INTEGER);
     rule.add(SqlTypeName.BIGINT);
     rule.add(SqlTypeName.DECIMAL);
     rule.add(SqlTypeName.FLOAT);
-    rules.put(SqlTypeName.FLOAT, rule);
+    rules.add(SqlTypeName.FLOAT, rule);
 
-    // Real is assignable from...
-    rule = new HashSet<>();
+    // REAL (32 bit floating point) is assignable from...
+    rule.clear();
     rule.add(SqlTypeName.TINYINT);
     rule.add(SqlTypeName.SMALLINT);
     rule.add(SqlTypeName.INTEGER);
@@ -97,10 +113,10 @@ public class SqlTypeAssignmentRules {
     rule.add(SqlTypeName.DECIMAL);
     rule.add(SqlTypeName.FLOAT);
     rule.add(SqlTypeName.REAL);
-    rules.put(SqlTypeName.REAL, rule);
+    rules.add(SqlTypeName.REAL, rule);
 
-    // Double is assignable from...
-    rule = new HashSet<>();
+    // DOUBLE is assignable from...
+    rule.clear();
     rule.add(SqlTypeName.TINYINT);
     rule.add(SqlTypeName.SMALLINT);
     rule.add(SqlTypeName.INTEGER);
@@ -109,10 +125,10 @@ public class SqlTypeAssignmentRules {
     rule.add(SqlTypeName.FLOAT);
     rule.add(SqlTypeName.REAL);
     rule.add(SqlTypeName.DOUBLE);
-    rules.put(SqlTypeName.DOUBLE, rule);
+    rules.add(SqlTypeName.DOUBLE, rule);
 
-    // Decimal is assignable from...
-    rule = new HashSet<>();
+    // DECIMAL is assignable from...
+    rule.clear();
     rule.add(SqlTypeName.TINYINT);
     rule.add(SqlTypeName.SMALLINT);
     rule.add(SqlTypeName.INTEGER);
@@ -120,63 +136,63 @@ public class SqlTypeAssignmentRules {
     rule.add(SqlTypeName.REAL);
     rule.add(SqlTypeName.DOUBLE);
     rule.add(SqlTypeName.DECIMAL);
-    rules.put(SqlTypeName.DECIMAL, rule);
+    rules.add(SqlTypeName.DECIMAL, rule);
 
-    // VarBinary is assignable from...
-    rule = new HashSet<>();
+    // VARBINARY is assignable from...
+    rule.clear();
     rule.add(SqlTypeName.VARBINARY);
     rule.add(SqlTypeName.BINARY);
-    rules.put(SqlTypeName.VARBINARY, rule);
+    rules.add(SqlTypeName.VARBINARY, rule);
 
-    // Char is assignable from...
-    rules.put(SqlTypeName.CHAR, EnumSet.of(SqlTypeName.CHAR));
+    // CHAR is assignable from...
+    rules.add(SqlTypeName.CHAR, EnumSet.of(SqlTypeName.CHAR));
 
-    // VarChar is assignable from...
-    rule = new HashSet<>();
+    // VARCHAR is assignable from...
+    rule.clear();
     rule.add(SqlTypeName.CHAR);
     rule.add(SqlTypeName.VARCHAR);
-    rules.put(SqlTypeName.VARCHAR, rule);
+    rules.add(SqlTypeName.VARCHAR, rule);
 
-    // Boolean is assignable from...
-    rules.put(SqlTypeName.BOOLEAN, EnumSet.of(SqlTypeName.BOOLEAN));
+    // BOOLEAN is assignable from...
+    rules.add(SqlTypeName.BOOLEAN, EnumSet.of(SqlTypeName.BOOLEAN));
 
-    // Binary is assignable from...
-    rule = new HashSet<>();
+    // BINARY is assignable from...
+    rule.clear();
     rule.add(SqlTypeName.BINARY);
     rule.add(SqlTypeName.VARBINARY);
-    rules.put(SqlTypeName.BINARY, rule);
+    rules.add(SqlTypeName.BINARY, rule);
 
-    // Date is assignable from ...
-    rule = new HashSet<>();
+    // DATE is assignable from...
+    rule.clear();
     rule.add(SqlTypeName.DATE);
     rule.add(SqlTypeName.TIMESTAMP);
-    rules.put(SqlTypeName.DATE, rule);
+    rules.add(SqlTypeName.DATE, rule);
 
-    // Time is assignable from ...
-    rule = new HashSet<>();
+    // TIME is assignable from...
+    rule.clear();
     rule.add(SqlTypeName.TIME);
     rule.add(SqlTypeName.TIMESTAMP);
-    rules.put(SqlTypeName.TIME, rule);
+    rules.add(SqlTypeName.TIME, rule);
 
-    // Time with local time-zone is assignable from ...
-    rules.put(SqlTypeName.TIME_WITH_LOCAL_TIME_ZONE,
+    // TIME WITH LOCAL TIME ZONE is assignable from...
+    rules.add(SqlTypeName.TIME_WITH_LOCAL_TIME_ZONE,
         EnumSet.of(SqlTypeName.TIME_WITH_LOCAL_TIME_ZONE));
 
-    // Timestamp is assignable from ...
-    rules.put(SqlTypeName.TIMESTAMP, EnumSet.of(SqlTypeName.TIMESTAMP));
+    // TIMESTAMP is assignable from ...
+    rules.add(SqlTypeName.TIMESTAMP, EnumSet.of(SqlTypeName.TIMESTAMP));
 
-    // Timestamp with local time-zone is assignable from ...
-    rules.put(SqlTypeName.TIMESTAMP_WITH_LOCAL_TIME_ZONE,
+    // TIMESTAMP WITH LOCAL TIME ZONE is assignable from...
+    rules.add(SqlTypeName.TIMESTAMP_WITH_LOCAL_TIME_ZONE,
         EnumSet.of(SqlTypeName.TIMESTAMP_WITH_LOCAL_TIME_ZONE));
 
-    // Geometry is assignable from ...
-    rules.put(SqlTypeName.GEOMETRY, EnumSet.of(SqlTypeName.GEOMETRY));
+    // GEOMETRY is assignable from ...
+    rules.add(SqlTypeName.GEOMETRY, EnumSet.of(SqlTypeName.GEOMETRY));
 
-    // Array is assignable from ...
-    rules.put(SqlTypeName.ARRAY, EnumSet.of(SqlTypeName.ARRAY));
+    // ARRAY is assignable from ...
+    rules.add(SqlTypeName.ARRAY, EnumSet.of(SqlTypeName.ARRAY));
 
-    // Any is assignable from ...
-    rule = new HashSet<>();
+    // ANY is assignable from ...
+    rule.clear();
     rule.add(SqlTypeName.TINYINT);
     rule.add(SqlTypeName.SMALLINT);
     rule.add(SqlTypeName.INTEGER);
@@ -187,14 +203,14 @@ public class SqlTypeAssignmentRules {
     rule.add(SqlTypeName.TIME);
     rule.add(SqlTypeName.DATE);
     rule.add(SqlTypeName.TIMESTAMP);
-    rules.put(SqlTypeName.ANY, rule);
+    rules.add(SqlTypeName.ANY, rule);
 
     // we use coerceRules when we're casting
-    coerceRules = copy(rules);
+    final Builder coerceRules = new Builder(rules);
 
-    // Make numbers symmetrical and
-    // make varchar/char castable to/from numbers
-    rule = new HashSet<>();
+    // Make numbers symmetrical,
+    // and make VARCHAR and CHAR castable to/from numbers
+    rule.clear();
     rule.add(SqlTypeName.TINYINT);
     rule.add(SqlTypeName.SMALLINT);
     rule.add(SqlTypeName.INTEGER);
@@ -207,148 +223,156 @@ public class SqlTypeAssignmentRules {
     rule.add(SqlTypeName.CHAR);
     rule.add(SqlTypeName.VARCHAR);
 
-    coerceRules.put(
-        SqlTypeName.TINYINT,
-        copy(rule));
-    coerceRules.put(
-        SqlTypeName.SMALLINT,
-        copy(rule));
-    coerceRules.put(
-        SqlTypeName.INTEGER,
-        copy(rule));
-    coerceRules.put(
-        SqlTypeName.BIGINT,
-        copy(rule));
-    coerceRules.put(
-        SqlTypeName.FLOAT,
-        copy(rule));
-    coerceRules.put(
-        SqlTypeName.REAL,
-        copy(rule));
-    coerceRules.put(
-        SqlTypeName.DECIMAL,
-        copy(rule));
-    coerceRules.put(
-        SqlTypeName.DOUBLE,
-        copy(rule));
-    coerceRules.put(
-        SqlTypeName.CHAR,
-        copy(rule));
-    coerceRules.put(
-        SqlTypeName.VARCHAR,
-        copy(rule));
-
-    // Exact Numerics are castable from intervals
+    coerceRules.add(SqlTypeName.TINYINT, rule);
+    coerceRules.add(SqlTypeName.SMALLINT, rule);
+    coerceRules.add(SqlTypeName.INTEGER, rule);
+    coerceRules.add(SqlTypeName.BIGINT, rule);
+    coerceRules.add(SqlTypeName.FLOAT, rule);
+    coerceRules.add(SqlTypeName.REAL, rule);
+    coerceRules.add(SqlTypeName.DECIMAL, rule);
+    coerceRules.add(SqlTypeName.DOUBLE, rule);
+    coerceRules.add(SqlTypeName.CHAR, rule);
+    coerceRules.add(SqlTypeName.VARCHAR, rule);
+
+    // Exact numeric types are castable from intervals
     for (SqlTypeName exactType : SqlTypeName.EXACT_TYPES) {
-      rule = coerceRules.get(exactType);
-      rule.addAll(SqlTypeName.INTERVAL_TYPES);
+      coerceRules.add(exactType,
+          coerceRules.copyValues(exactType)
+              .addAll(SqlTypeName.INTERVAL_TYPES)
+              .build());
     }
 
-    // intervals are castable from Exact Numeric
+    // Intervals are castable from exact numeric
     for (SqlTypeName typeName : SqlTypeName.INTERVAL_TYPES) {
-      rule = coerceRules.get(typeName);
-      rule.add(SqlTypeName.TINYINT);
-      rule.add(SqlTypeName.SMALLINT);
-      rule.add(SqlTypeName.INTEGER);
-      rule.add(SqlTypeName.BIGINT);
-      rule.add(SqlTypeName.DECIMAL);
-      rule.add(SqlTypeName.VARCHAR);
+      coerceRules.add(typeName,
+          coerceRules.copyValues(typeName)
+              .add(SqlTypeName.TINYINT)
+              .add(SqlTypeName.SMALLINT)
+              .add(SqlTypeName.INTEGER)
+              .add(SqlTypeName.BIGINT)
+              .add(SqlTypeName.DECIMAL)
+              .add(SqlTypeName.VARCHAR)
+              .build());
     }
 
-    // varchar is castable from Boolean, Date, time, timestamp, numbers and
+    // VARCHAR is castable from BOOLEAN, DATE, TIMESTAMP, numeric types and
     // intervals
-    rule = coerceRules.get(SqlTypeName.VARCHAR);
-    rule.add(SqlTypeName.BOOLEAN);
-    rule.add(SqlTypeName.DATE);
-    rule.add(SqlTypeName.TIME);
-    rule.add(SqlTypeName.TIMESTAMP);
-    rule.addAll(SqlTypeName.INTERVAL_TYPES);
-
-    // char is castable from Boolean, Date, time and timestamp and numbers
-    rule = coerceRules.get(SqlTypeName.CHAR);
-    rule.add(SqlTypeName.BOOLEAN);
-    rule.add(SqlTypeName.DATE);
-    rule.add(SqlTypeName.TIME);
-    rule.add(SqlTypeName.TIMESTAMP);
-    rule.addAll(SqlTypeName.INTERVAL_TYPES);
-
-    // Boolean is castable from char and varchar
-    rule = coerceRules.get(SqlTypeName.BOOLEAN);
-    rule.add(SqlTypeName.CHAR);
-    rule.add(SqlTypeName.VARCHAR);
-
-    // Date, time, and timestamp are castable from
-    // char and varchar
-    // Date is castable from ...
-    rule = new HashSet<>();
-    rule.add(SqlTypeName.DATE);
-    rule.add(SqlTypeName.TIMESTAMP);
-    rule.add(SqlTypeName.TIMESTAMP_WITH_LOCAL_TIME_ZONE);
-    rule.add(SqlTypeName.CHAR);
-    rule.add(SqlTypeName.VARCHAR);
-    coerceRules.put(SqlTypeName.DATE, rule);
-
-    // Time is castable from ...
-    rule = new HashSet<>();
-    rule.add(SqlTypeName.TIME);
-    rule.add(SqlTypeName.TIME_WITH_LOCAL_TIME_ZONE);
-    rule.add(SqlTypeName.TIMESTAMP);
-    rule.add(SqlTypeName.TIMESTAMP_WITH_LOCAL_TIME_ZONE);
-    rule.add(SqlTypeName.CHAR);
-    rule.add(SqlTypeName.VARCHAR);
-    coerceRules.put(SqlTypeName.TIME, rule);
-
-    // Time with local time-zone is castable from ...
-    rule = new HashSet<>();
-    rule.add(SqlTypeName.TIME);
-    rule.add(SqlTypeName.TIME_WITH_LOCAL_TIME_ZONE);
-    rule.add(SqlTypeName.TIMESTAMP);
-    rule.add(SqlTypeName.TIMESTAMP_WITH_LOCAL_TIME_ZONE);
-    rule.add(SqlTypeName.CHAR);
-    rule.add(SqlTypeName.VARCHAR);
-    coerceRules.put(SqlTypeName.TIME_WITH_LOCAL_TIME_ZONE, rule);
-
-    // Timestamp is castable from ...
-    rule = new HashSet<>();
-    rule.add(SqlTypeName.TIMESTAMP);
-    rule.add(SqlTypeName.TIMESTAMP_WITH_LOCAL_TIME_ZONE);
-    rule.add(SqlTypeName.DATE);
-    rule.add(SqlTypeName.TIME);
-    rule.add(SqlTypeName.TIME_WITH_LOCAL_TIME_ZONE);
-    rule.add(SqlTypeName.CHAR);
-    rule.add(SqlTypeName.VARCHAR);
-    coerceRules.put(SqlTypeName.TIMESTAMP, rule);
-
-    // Timestamp with local time-zone is castable from ...
-    rule = new HashSet<>();
-    rule.add(SqlTypeName.TIMESTAMP);
-    rule.add(SqlTypeName.TIMESTAMP_WITH_LOCAL_TIME_ZONE);
-    rule.add(SqlTypeName.DATE);
-    rule.add(SqlTypeName.TIME);
-    rule.add(SqlTypeName.TIME_WITH_LOCAL_TIME_ZONE);
-    rule.add(SqlTypeName.CHAR);
-    rule.add(SqlTypeName.VARCHAR);
-    coerceRules.put(SqlTypeName.TIMESTAMP_WITH_LOCAL_TIME_ZONE, rule);
+    coerceRules.add(SqlTypeName.VARCHAR,
+        coerceRules.copyValues(SqlTypeName.VARCHAR)
+            .add(SqlTypeName.BOOLEAN)
+            .add(SqlTypeName.DATE)
+            .add(SqlTypeName.TIME)
+            .add(SqlTypeName.TIMESTAMP)
+            .addAll(SqlTypeName.INTERVAL_TYPES)
+            .build());
+
+    // CHAR is castable from BOOLEAN, DATE, TIME, TIMESTAMP and numeric types
+    coerceRules.add(SqlTypeName.CHAR,
+        coerceRules.copyValues(SqlTypeName.CHAR)
+            .add(SqlTypeName.BOOLEAN)
+            .add(SqlTypeName.DATE)
+            .add(SqlTypeName.TIME)
+            .add(SqlTypeName.TIMESTAMP)
+            .addAll(SqlTypeName.INTERVAL_TYPES)
+            .build());
+
+    // BOOLEAN is castable from CHAR and VARCHAR
+    coerceRules.add(SqlTypeName.BOOLEAN,
+        coerceRules.copyValues(SqlTypeName.BOOLEAN)
+            .add(SqlTypeName.CHAR)
+            .add(SqlTypeName.VARCHAR)
+            .build());
+
+    // DATE, TIME, and TIMESTAMP are castable from
+    // CHAR and VARCHAR.
+
+    // DATE is castable from...
+    coerceRules.add(SqlTypeName.DATE,
+        coerceRules.copyValues(SqlTypeName.DATE)
+            .add(SqlTypeName.DATE)
+            .add(SqlTypeName.TIMESTAMP)
+            .add(SqlTypeName.TIMESTAMP_WITH_LOCAL_TIME_ZONE)
+            .add(SqlTypeName.CHAR)
+            .add(SqlTypeName.VARCHAR)
+            .build());
+
+    // TIME is castable from...
+    coerceRules.add(SqlTypeName.TIME,
+        coerceRules.copyValues(SqlTypeName.TIME)
+            .add(SqlTypeName.TIME)
+            .add(SqlTypeName.TIME_WITH_LOCAL_TIME_ZONE)
+            .add(SqlTypeName.TIMESTAMP)
+            .add(SqlTypeName.TIMESTAMP_WITH_LOCAL_TIME_ZONE)
+            .add(SqlTypeName.CHAR)
+            .add(SqlTypeName.VARCHAR)
+            .build());
+
+    // TIME WITH LOCAL TIME ZONE is castable from...
+    coerceRules.add(SqlTypeName.TIME_WITH_LOCAL_TIME_ZONE,
+        coerceRules.copyValues(SqlTypeName.TIME_WITH_LOCAL_TIME_ZONE)
+            .add(SqlTypeName.TIME)
+            .add(SqlTypeName.TIME_WITH_LOCAL_TIME_ZONE)
+            .add(SqlTypeName.TIMESTAMP)
+            .add(SqlTypeName.TIMESTAMP_WITH_LOCAL_TIME_ZONE)
+            .add(SqlTypeName.CHAR)
+            .add(SqlTypeName.VARCHAR)
+            .build());
+
+    // TIMESTAMP is castable from...
+    coerceRules.add(SqlTypeName.TIMESTAMP,
+        coerceRules.copyValues(SqlTypeName.TIMESTAMP)
+            .add(SqlTypeName.TIMESTAMP)
+            .add(SqlTypeName.TIMESTAMP_WITH_LOCAL_TIME_ZONE)
+            .add(SqlTypeName.DATE)
+            .add(SqlTypeName.TIME)
+            .add(SqlTypeName.TIME_WITH_LOCAL_TIME_ZONE)
+            .add(SqlTypeName.CHAR)
+            .add(SqlTypeName.VARCHAR)
+            .build());
+
+    // TIMESTAMP WITH LOCAL TIME ZONE is castable from...
+    coerceRules.add(SqlTypeName.TIMESTAMP_WITH_LOCAL_TIME_ZONE,
+        coerceRules.copyValues(SqlTypeName.TIMESTAMP_WITH_LOCAL_TIME_ZONE)
+            .add(SqlTypeName.TIMESTAMP)
+            .add(SqlTypeName.TIMESTAMP_WITH_LOCAL_TIME_ZONE)
+            .add(SqlTypeName.DATE)
+            .add(SqlTypeName.TIME)
+            .add(SqlTypeName.TIME_WITH_LOCAL_TIME_ZONE)
+            .add(SqlTypeName.CHAR)
+            .add(SqlTypeName.VARCHAR)
+            .build());
+
+    INSTANCE = new SqlTypeAssignmentRules(rules.map);
+    COERCE_INSTANCE = new SqlTypeAssignmentRules(coerceRules.map);
   }
 
   //~ Methods ----------------------------------------------------------------
 
+  /** Returns an instance that does not coerce. */
   public static synchronized SqlTypeAssignmentRules instance() {
-    if (instance == null) {
-      instance = new SqlTypeAssignmentRules();
-    }
-    return instance;
+    return instance(false);
+  }
+
+  /** Returns an instance. */
+  public static synchronized SqlTypeAssignmentRules instance(boolean coerce) {
+    return coerce ? COERCE_INSTANCE : INSTANCE;
   }
 
+  @Deprecated
   public boolean canCastFrom(
       SqlTypeName to,
       SqlTypeName from,
       boolean coerce) {
-    assert to != null;
-    assert from != null;
+    return instance(coerce).canCastFrom(to, from);
+  }
 
-    Map<SqlTypeName, Set<SqlTypeName>> ruleset =
-        coerce ? coerceRules : rules;
+  /** Returns whether it is valid to cast a value of from type {@code from} to
+   * type {@code to}. */
+  public boolean canCastFrom(
+      SqlTypeName to,
+      SqlTypeName from) {
+    Preconditions.checkNotNull(to);
+    Preconditions.checkNotNull(from);
 
     if (to == SqlTypeName.NULL) {
       return false;
@@ -356,7 +380,7 @@ public class SqlTypeAssignmentRules {
       return true;
     }
 
-    final Set<SqlTypeName> rule = ruleset.get(to);
+    final Set<SqlTypeName> rule = map.get(to);
     if (rule == null) {
       // if you hit this assert, see the constructor of this class on how
       // to add new rule
@@ -366,21 +390,43 @@ public class SqlTypeAssignmentRules {
     return rule.contains(from);
   }
 
-  @SuppressWarnings("unchecked")
-  private static <K, V> Map<K, V> copy(Map<K, V> map) {
-    Map<K, V> copy = new HashMap<>();
-    for (Map.Entry<K, V> e : map.entrySet()) {
-      if (e.getValue() instanceof Set) {
-        copy.put(e.getKey(), (V) copy((Set) e.getValue()));
-      } else {
-        copy.put(e.getKey(), e.getValue());
+
+  /** Keeps state while maps are building build. */
+  private static class Builder {
+    final Map<SqlTypeName, ImmutableSet<SqlTypeName>> map;
+    final LoadingCache<Set<SqlTypeName>, ImmutableSet<SqlTypeName>> sets;
+
+    /** Creates an empty Builder. */
+    Builder() {
+      this.map = new HashMap<>();
+      this.sets =
+          CacheBuilder.newBuilder().build(
+              new CacheLoader<Set<SqlTypeName>, ImmutableSet<SqlTypeName>>() {
+                public ImmutableSet<SqlTypeName> load(
+                    @Nonnull Set<SqlTypeName> key) {
+                  return Sets.immutableEnumSet(key);
+                }
+              });
+    }
+
+    /** Creates a Builder as a copy of another Builder. */
+    Builder(Builder builder) {
+      this.map = new HashMap<>(builder.map);
+      this.sets = builder.sets; // share the same canonical sets
+    }
+
+    void add(SqlTypeName fromType, Set<SqlTypeName> toTypes) {
+      try {
+        map.put(fromType, sets.get(toTypes));
+      } catch (ExecutionException e) {
+        throw new RuntimeException("populating SqlTypeAssignmentRules", e);
       }
     }
-    return copy;
-  }
 
-  private static <T> HashSet<T> copy(Set<T> set) {
-    return new HashSet<T>(set);
+    ImmutableSet.Builder<SqlTypeName> copyValues(SqlTypeName typeName) {
+      return ImmutableSet.<SqlTypeName>builder()
+          .addAll(map.get(typeName));
+    }
   }
 }
 

http://git-wip-us.apache.org/repos/asf/calcite/blob/becb6dfb/core/src/main/java/org/apache/calcite/sql/type/SqlTypeUtil.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql/type/SqlTypeUtil.java b/core/src/main/java/org/apache/calcite/sql/type/SqlTypeUtil.java
index 9113dd8..c8fbe03 100644
--- a/core/src/main/java/org/apache/calcite/sql/type/SqlTypeUtil.java
+++ b/core/src/main/java/org/apache/calcite/sql/type/SqlTypeUtil.java
@@ -859,8 +859,8 @@ public abstract class SqlTypeUtil {
     // where internally a cast across character repertoires is OK.  Should
     // probably clean that up.
 
-    SqlTypeAssignmentRules rules = SqlTypeAssignmentRules.instance();
-    return rules.canCastFrom(toTypeName, fromTypeName, coerce);
+    SqlTypeAssignmentRules rules = SqlTypeAssignmentRules.instance(coerce);
+    return rules.canCastFrom(toTypeName, fromTypeName);
   }
 
   /**

http://git-wip-us.apache.org/repos/asf/calcite/blob/becb6dfb/core/src/test/java/org/apache/calcite/test/RexProgramTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/calcite/test/RexProgramTest.java b/core/src/test/java/org/apache/calcite/test/RexProgramTest.java
index 9501959..94eb792 100644
--- a/core/src/test/java/org/apache/calcite/test/RexProgramTest.java
+++ b/core/src/test/java/org/apache/calcite/test/RexProgramTest.java
@@ -69,7 +69,6 @@ import org.junit.Test;
 import java.math.BigDecimal;
 import java.util.ArrayList;
 import java.util.Arrays;
-import java.util.Calendar;
 import java.util.List;
 import java.util.Map;
 import java.util.TimeZone;
@@ -1597,8 +1596,8 @@ public class RexProgramTest {
 
     for (RelDataType fromType : types) {
       for (RelDataType toType : types) {
-        if (SqlTypeAssignmentRules.instance().canCastFrom(
-            toType.getSqlTypeName(), fromType.getSqlTypeName(), false)) {
+        if (SqlTypeAssignmentRules.instance(false)
+            .canCastFrom(toType.getSqlTypeName(), fromType.getSqlTypeName())) {
           for (RexLiteral literal : map.get(fromType.getSqlTypeName())) {
             final RexNode cast = rexBuilder.makeCast(toType, literal);
             if (cast instanceof RexLiteral) {
@@ -1824,17 +1823,6 @@ public class RexProgramTest {
             RexUtil.retainDeterministic(RelOptUtil.conjunctions(n)).size());
   }
 
-  private Calendar cal(int y, int m, int d, int h, int mm, int s) {
-    final Calendar c = Util.calendar();
-    c.set(Calendar.YEAR, y);
-    c.set(Calendar.MONTH, m);
-    c.set(Calendar.DAY_OF_MONTH, d);
-    c.set(Calendar.HOUR_OF_DAY, h);
-    c.set(Calendar.MINUTE, mm);
-    c.set(Calendar.SECOND, s);
-    return c;
-  }
-
   @Test public void testConstantMap() {
     final RelDataType intType = typeFactory.createSqlType(SqlTypeName.INTEGER);
     final RelDataType rowType = typeFactory.builder()
@@ -1865,7 +1853,6 @@ public class RexProgramTest {
 
     // Contradictory constraints yield no constants
     final RexNode ref0 = rexBuilder.makeInputRef(rowType, 0);
-    final RexNode ref1 = rexBuilder.makeInputRef(rowType, 1);
     final ImmutableMap<RexNode, RexNode> map2 =
         RexUtil.predicateConstants(RexNode.class, rexBuilder,
             ImmutableList.of(eq(ref0, literal1),


[7/7] calcite git commit: [CALCITE-2059] Apache Geode Adapter (Christian Tzolov)

Posted by jh...@apache.org.
[CALCITE-2059] Apache Geode Adapter (Christian Tzolov)

- Downgrade Geode from 1.4.0 to 1.3.0 - Geode server allows connections
  only from clients with same or lower version
- Add links to presentations and tutorials

Close apache/calcite#581


Project: http://git-wip-us.apache.org/repos/asf/calcite/repo
Commit: http://git-wip-us.apache.org/repos/asf/calcite/commit/707f4de9
Tree: http://git-wip-us.apache.org/repos/asf/calcite/tree/707f4de9
Diff: http://git-wip-us.apache.org/repos/asf/calcite/diff/707f4de9

Branch: refs/heads/master
Commit: 707f4de9cafc474d0daf807b1e4c339f6eb42d7e
Parents: 3c67a60
Author: Christian Tzolov <ch...@gmail.com>
Authored: Thu Nov 3 06:30:04 2016 +0100
Committer: Julian Hyde <jh...@apache.org>
Committed: Fri Feb 16 10:18:02 2018 -0800

----------------------------------------------------------------------
 geode/README.md                                 |   2 +
 geode/pom.xml                                   | 189 ++++++++
 .../adapter/geode/rel/GeodeAggregate.java       | 135 ++++++
 .../adapter/geode/rel/GeodeEnumerator.java      |  93 ++++
 .../calcite/adapter/geode/rel/GeodeFilter.java  | 238 ++++++++++
 .../calcite/adapter/geode/rel/GeodeProject.java |  75 +++
 .../calcite/adapter/geode/rel/GeodeRel.java     | 122 +++++
 .../calcite/adapter/geode/rel/GeodeRules.java   | 370 +++++++++++++++
 .../calcite/adapter/geode/rel/GeodeSchema.java  |  88 ++++
 .../adapter/geode/rel/GeodeSchemaFactory.java   |  69 +++
 .../calcite/adapter/geode/rel/GeodeSort.java    | 106 +++++
 .../calcite/adapter/geode/rel/GeodeTable.java   | 256 ++++++++++
 .../adapter/geode/rel/GeodeTableScan.java       |  80 ++++
 .../geode/rel/GeodeToEnumerableConverter.java   | 164 +++++++
 .../rel/GeodeToEnumerableConverterRule.java     |  43 ++
 .../calcite/adapter/geode/rel/package-info.java |  26 ++
 .../geode/simple/GeodeSimpleEnumerator.java     |  76 +++
 .../geode/simple/GeodeSimpleScannableTable.java |  75 +++
 .../adapter/geode/simple/GeodeSimpleSchema.java |  84 ++++
 .../geode/simple/GeodeSimpleSchemaFactory.java  |  53 +++
 .../adapter/geode/simple/package-info.java      |  26 ++
 .../calcite/adapter/geode/util/GeodeUtils.java  | 278 +++++++++++
 .../geode/util/JavaTypeFactoryExtImpl.java      | 123 +++++
 .../adapter/geode/util/package-info.java        |  26 ++
 .../adapter/geode/rel/BaseGeodeAdapterIT.java   | 168 +++++++
 .../geode/rel/GeodeAdapterBookshopIT.java       | 468 +++++++++++++++++++
 .../adapter/geode/rel/GeodeAdapterIT.java       |  99 ++++
 .../calcite/adapter/geode/rel/GeodeZipsIT.java  | 199 ++++++++
 .../geode/rel/RelationalJdbcExample.java        | 103 ++++
 .../geode/simple/BookMasterRegionTest.java      |  57 +++
 .../adapter/geode/simple/SimpleJdbcExample.java |  95 ++++
 .../src/test/resources/model-bookshop-all.json  |  35 ++
 geode/src/test/resources/model-bookshop.json    |  35 ++
 .../resources/model-geode-pg-federation.json    |  54 +++
 .../src/test/resources/model-with-classes.json  |  39 ++
 geode/src/test/resources/model-zips.json        |  47 ++
 geode/src/test/resources/model.json             |  35 ++
 geode/src/test/resources/model2.json            |  35 ++
 pom.xml                                         |   7 +
 site/_docs/adapter.md                           |   1 +
 site/_docs/geode_adapter.md                     | 180 +++++++
 sqlline                                         |   2 +-
 sqlline.bat                                     |   2 +-
 43 files changed, 4456 insertions(+), 2 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/calcite/blob/707f4de9/geode/README.md
----------------------------------------------------------------------
diff --git a/geode/README.md b/geode/README.md
new file mode 100644
index 0000000..852e471
--- /dev/null
+++ b/geode/README.md
@@ -0,0 +1,2 @@
+##Apache Geode SQL/JBC Adapter
+

http://git-wip-us.apache.org/repos/asf/calcite/blob/707f4de9/geode/pom.xml
----------------------------------------------------------------------
diff --git a/geode/pom.xml b/geode/pom.xml
new file mode 100644
index 0000000..36c75ef
--- /dev/null
+++ b/geode/pom.xml
@@ -0,0 +1,189 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <groupId>org.apache.calcite</groupId>
+    <artifactId>calcite</artifactId>
+    <version>1.16.0-SNAPSHOT</version>
+  </parent>
+
+  <artifactId>calcite-geode</artifactId>
+  <packaging>jar</packaging>
+  <version>1.16.0-SNAPSHOT</version>
+  <name>Calcite Geode</name>
+  <description>Geode adapter for Calcite</description>
+
+  <properties>
+    <top.dir>${project.basedir}/..</top.dir>
+  </properties>
+
+  <dependencies>
+    <!-- Sorted by groupId, artifactId; calcite dependencies first. Put versions
+         in dependencyManagement in the root POM, not here. -->
+    <dependency>
+      <groupId>org.apache.calcite</groupId>
+      <artifactId>calcite-core</artifactId>
+      <type>jar</type>
+    </dependency>
+
+    <dependency>
+      <groupId>org.apache.calcite</groupId>
+      <artifactId>calcite-core</artifactId>
+      <type>test-jar</type>
+      <scope>test</scope>
+    </dependency>
+
+    <dependency>
+      <groupId>org.apache.calcite</groupId>
+      <artifactId>calcite-linq4j</artifactId>
+    </dependency>
+
+    <dependency>
+      <groupId>org.apache.commons</groupId>
+      <artifactId>commons-lang3</artifactId>
+      <version>3.2</version>
+    </dependency>
+
+    <dependency>
+      <groupId>org.apache.geode</groupId>
+      <artifactId>geode-core</artifactId>
+    </dependency>
+
+    <dependency>
+      <groupId>com.google.guava</groupId>
+      <artifactId>guava</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>junit</groupId>
+      <artifactId>junit</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.slf4j</groupId>
+      <artifactId>slf4j-api</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.slf4j</groupId>
+      <artifactId>slf4j-log4j12</artifactId>
+      <scope>test</scope>
+    </dependency>
+  </dependencies>
+
+  <profiles>
+    <profile>
+      <id>uberjdbc</id>
+      <dependencies>
+        <dependency>
+          <groupId>org.apache.geode</groupId>
+          <artifactId>geode-core</artifactId>
+        </dependency>
+      </dependencies>
+      <build>
+        <plugins>
+          <plugin>
+            <groupId>org.apache.maven.plugins</groupId>
+            <artifactId>maven-shade-plugin</artifactId>
+            <version>2.4.3</version>
+            <executions>
+              <execution>
+                <phase>package</phase>
+                <goals>
+                  <goal>shade</goal>
+                </goals>
+                <configuration>
+                  <filters>
+                    <filter>
+                      <artifact>*:*</artifact>
+                      <excludes>
+                        <exclude>META-INF/*.SF</exclude>
+                        <exclude>META-INF/*.DSA</exclude>
+                        <exclude>META-INF/*.RSA</exclude>
+                      </excludes>
+                    </filter>
+                  </filters>
+                  <createDependencyReducedPom>false</createDependencyReducedPom>
+                </configuration>
+              </execution>
+            </executions>
+          </plugin>
+        </plugins>
+      </build>
+    </profile>
+  </profiles>
+
+  <build>
+    <plugins>
+      <!-- Sorted by groupId, artifactId. Put versions in pluginManagement in
+           the root POM, not here. -->
+      <plugin>
+        <artifactId>maven-dependency-plugin</artifactId>
+        <version>${maven-dependency-plugin.version}</version>
+        <executions>
+          <execution>
+            <id>analyze</id>
+            <goals>
+              <goal>analyze-only</goal>
+            </goals>
+            <configuration>
+              <failOnWarning>true</failOnWarning>
+              <!-- ignore "unused but declared" warnings -->
+              <ignoredUnusedDeclaredDependencies>
+                <ignoredUnusedDeclaredDependency>org.slf4j:slf4j-api</ignoredUnusedDeclaredDependency>
+                <ignoredUnusedDeclaredDependency>org.slf4j:slf4j-log4j12</ignoredUnusedDeclaredDependency>
+              </ignoredUnusedDeclaredDependencies>
+            </configuration>
+          </execution>
+        </executions>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-jar-plugin</artifactId>
+        <executions>
+          <execution>
+            <goals>
+              <goal>test-jar</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-release-plugin</artifactId>
+      </plugin>
+      <!-- Parent module has the same plugin and does the work of generating
+          -sources.jar for each project. But without the plugin declared here, IDEs
+          don't know the sources are available. -->
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-source-plugin</artifactId>
+        <executions>
+          <execution>
+            <id>attach-sources</id>
+            <phase>verify</phase>
+            <goals>
+              <goal>jar-no-fork</goal>
+              <goal>test-jar-no-fork</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
+    </plugins>
+  </build>
+
+</project>

http://git-wip-us.apache.org/repos/asf/calcite/blob/707f4de9/geode/src/main/java/org/apache/calcite/adapter/geode/rel/GeodeAggregate.java
----------------------------------------------------------------------
diff --git a/geode/src/main/java/org/apache/calcite/adapter/geode/rel/GeodeAggregate.java b/geode/src/main/java/org/apache/calcite/adapter/geode/rel/GeodeAggregate.java
new file mode 100644
index 0000000..faab698
--- /dev/null
+++ b/geode/src/main/java/org/apache/calcite/adapter/geode/rel/GeodeAggregate.java
@@ -0,0 +1,135 @@
+/*
+ * 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.adapter.geode.rel;
+
+import org.apache.calcite.plan.RelOptCluster;
+import org.apache.calcite.plan.RelOptCost;
+import org.apache.calcite.plan.RelOptPlanner;
+import org.apache.calcite.plan.RelTraitSet;
+import org.apache.calcite.rel.RelNode;
+import org.apache.calcite.rel.core.Aggregate;
+import org.apache.calcite.rel.core.AggregateCall;
+import org.apache.calcite.rel.metadata.RelMetadataQuery;
+import org.apache.calcite.rel.type.RelDataType;
+import org.apache.calcite.rel.type.RelDataTypeField;
+import org.apache.calcite.util.ImmutableBitSet;
+import org.apache.calcite.util.Util;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableMap.Builder;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Implementation of
+ * {@link org.apache.calcite.rel.core.Aggregate} relational expression
+ * in Geode.
+ */
+public class GeodeAggregate extends Aggregate implements GeodeRel {
+
+  /** Creates a GeodeAggregate. */
+  public GeodeAggregate(RelOptCluster cluster,
+      RelTraitSet traitSet,
+      RelNode input,
+      boolean indicator,
+      ImmutableBitSet groupSet,
+      List<ImmutableBitSet> groupSets,
+      List<AggregateCall> aggCalls) {
+    super(cluster, traitSet, input, indicator, groupSet, groupSets, aggCalls);
+
+    assert getConvention() == GeodeRel.CONVENTION;
+    assert getConvention() == this.input.getConvention();
+    assert getConvention() == input.getConvention();
+    assert this.groupSets.size() == 1 : "Grouping sets not supported";
+
+    for (AggregateCall aggCall : aggCalls) {
+      if (aggCall.isDistinct()) {
+        System.out.println("DISTINCT based aggregation!");
+      }
+    }
+  }
+
+  @Override public Aggregate copy(RelTraitSet traitSet, RelNode input, boolean indicator,
+      ImmutableBitSet groupSet, List<ImmutableBitSet> groupSets,
+      List<AggregateCall> aggCalls) {
+    return new GeodeAggregate(getCluster(), traitSet, input, indicator, groupSet, groupSets,
+        aggCalls);
+  }
+
+  @Override public RelOptCost computeSelfCost(RelOptPlanner planner, RelMetadataQuery mq) {
+    return super.computeSelfCost(planner, mq).multiplyBy(0.1);
+  }
+
+  @Override public void implement(GeodeImplementContext geodeImplementContext) {
+
+    ((GeodeRel) getInput()).implement(geodeImplementContext);
+
+    List<String> inputFields = fieldNames(getInput().getRowType());
+
+    List<String> groupByFields = new ArrayList<>();
+
+    // GROUP BY field == cardinality of 1, GROUP BY field1, field2 => cardinality of 2 ...
+    if (groupSet.cardinality() == 1) {
+      final String groupByFieldName = inputFields.get(groupSet.nth(0));
+      groupByFields.add(groupByFieldName);
+    } else {
+      for (int group : groupSet) {
+        groupByFields.add(inputFields.get(group));
+      }
+    }
+
+    geodeImplementContext.addGroupBy(groupByFields);
+
+    // Find the aggregate functions (e.g. MAX, SUM ...)
+    Builder<String, String> aggregateFunctionMap = ImmutableMap.builder();
+    for (AggregateCall aggCall : aggCalls) {
+
+      ArrayList<Object> aggCallFieldNames = new ArrayList<>();
+      for (int i : aggCall.getArgList()) {
+        aggCallFieldNames.add(inputFields.get(i));
+      }
+      String functionName = aggCall.getAggregation().getName();
+
+      // Workaround to handle count(*) case. Geode doesn't allow "AS" aliases on
+      // 'count(*)' but allows it for count('any column name'). So we are
+      // converting the count(*) into count (first input ColumnName).
+      if ("COUNT".equalsIgnoreCase(functionName) && aggCallFieldNames.isEmpty()) {
+        aggCallFieldNames.add(inputFields.get(0));
+      }
+
+      String oqlAggregateCall = Util.toString(aggCallFieldNames, " " + functionName + "(", ", ",
+          ")");
+
+      aggregateFunctionMap.put(aggCall.getName(), oqlAggregateCall);
+    }
+
+    geodeImplementContext.addAggregateFunctions(aggregateFunctionMap.build());
+
+  }
+
+  private List<String> fieldNames(RelDataType relDataType) {
+    ArrayList<String> names = new ArrayList<>();
+
+    for (RelDataTypeField rdtf : relDataType.getFieldList()) {
+      names.add(rdtf.getName());
+    }
+    return names;
+  }
+}
+
+// End GeodeAggregate.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/707f4de9/geode/src/main/java/org/apache/calcite/adapter/geode/rel/GeodeEnumerator.java
----------------------------------------------------------------------
diff --git a/geode/src/main/java/org/apache/calcite/adapter/geode/rel/GeodeEnumerator.java b/geode/src/main/java/org/apache/calcite/adapter/geode/rel/GeodeEnumerator.java
new file mode 100644
index 0000000..3325819
--- /dev/null
+++ b/geode/src/main/java/org/apache/calcite/adapter/geode/rel/GeodeEnumerator.java
@@ -0,0 +1,93 @@
+/*
+ * 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.adapter.geode.rel;
+
+import org.apache.calcite.linq4j.Enumerator;
+import org.apache.calcite.rel.type.RelDataTypeFactory;
+import org.apache.calcite.rel.type.RelDataTypeField;
+import org.apache.calcite.rel.type.RelDataTypeSystem;
+import org.apache.calcite.rel.type.RelProtoDataType;
+import org.apache.calcite.sql.type.SqlTypeFactoryImpl;
+
+import org.apache.geode.cache.query.SelectResults;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+
+import static org.apache.calcite.adapter.geode.util.GeodeUtils.convertToRowValues;
+
+/**
+ * Enumerator that reads from a Geode Regions.
+ */
+class GeodeEnumerator implements Enumerator<Object> {
+
+  protected static final Logger LOGGER = LoggerFactory.getLogger(GeodeEnumerator.class.getName());
+
+  private Iterator iterator;
+  private Object current;
+  private List<RelDataTypeField> fieldTypes;
+
+  /**
+   * Creates a GeodeEnumerator.
+   *
+   * @param results      Geode result set ({@link SelectResults})
+   * @param protoRowType The type of resulting rows
+   */
+  GeodeEnumerator(SelectResults results, RelProtoDataType protoRowType) {
+    if (results == null) {
+      LOGGER.warn("Null OQL results!");
+    }
+    this.iterator = (results == null) ? Collections.emptyIterator() : results.iterator();
+    this.current = null;
+
+    final RelDataTypeFactory typeFactory =
+        new SqlTypeFactoryImpl(RelDataTypeSystem.DEFAULT);
+    this.fieldTypes = protoRowType.apply(typeFactory).getFieldList();
+  }
+
+  /**
+   * Produces the next row from the results.
+   *
+   * @return A rel row from the results
+   */
+  @Override public Object current() {
+    return convertToRowValues(fieldTypes, current);
+  }
+
+  @Override public boolean moveNext() {
+    if (iterator.hasNext()) {
+      current = iterator.next();
+      return true;
+    } else {
+      return false;
+    }
+  }
+
+  @Override public void reset() {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override public void close() {
+    // Nothing to do here
+  }
+}
+
+// End GeodeEnumerator.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/707f4de9/geode/src/main/java/org/apache/calcite/adapter/geode/rel/GeodeFilter.java
----------------------------------------------------------------------
diff --git a/geode/src/main/java/org/apache/calcite/adapter/geode/rel/GeodeFilter.java b/geode/src/main/java/org/apache/calcite/adapter/geode/rel/GeodeFilter.java
new file mode 100644
index 0000000..e4e5ac9
--- /dev/null
+++ b/geode/src/main/java/org/apache/calcite/adapter/geode/rel/GeodeFilter.java
@@ -0,0 +1,238 @@
+/*
+ * 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.adapter.geode.rel;
+
+import org.apache.calcite.plan.RelOptCluster;
+import org.apache.calcite.plan.RelOptCost;
+import org.apache.calcite.plan.RelOptPlanner;
+import org.apache.calcite.plan.RelOptUtil;
+import org.apache.calcite.plan.RelTraitSet;
+import org.apache.calcite.rel.RelNode;
+import org.apache.calcite.rel.core.Filter;
+import org.apache.calcite.rel.metadata.RelMetadataQuery;
+import org.apache.calcite.rel.type.RelDataType;
+import org.apache.calcite.rex.RexCall;
+import org.apache.calcite.rex.RexInputRef;
+import org.apache.calcite.rex.RexLiteral;
+import org.apache.calcite.rex.RexNode;
+import org.apache.calcite.sql.type.SqlTypeName;
+import org.apache.calcite.util.Util;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import static org.apache.calcite.sql.type.SqlTypeName.CHAR;
+import static org.apache.calcite.sql.type.SqlTypeName.NUMERIC_TYPES;
+
+/**
+ * Implementation of
+ * {@link Filter} relational expression in Geode.
+ */
+public class GeodeFilter extends Filter implements GeodeRel {
+
+  private final String match;
+
+  public GeodeFilter(RelOptCluster cluster, RelTraitSet traitSet,
+      RelNode input, RexNode condition) {
+
+    super(cluster, traitSet, input, condition);
+
+    Translator translator = new Translator(getRowType());
+    this.match = translator.translateMatch(condition);
+
+    assert getConvention() == GeodeRel.CONVENTION;
+    assert getConvention() == input.getConvention();
+  }
+
+  @Override public RelOptCost computeSelfCost(RelOptPlanner planner, RelMetadataQuery mq) {
+    return super.computeSelfCost(planner, mq).multiplyBy(0.1);
+  }
+
+  @Override public GeodeFilter copy(RelTraitSet traitSet, RelNode input, RexNode condition) {
+    return new GeodeFilter(getCluster(), traitSet, input, condition);
+  }
+
+  @Override public void implement(GeodeImplementContext geodeImplementContext) {
+    // first call the input down the tree.
+    ((GeodeRel) getInput()).implement(geodeImplementContext);
+    geodeImplementContext.addPredicates(Collections.singletonList(match));
+  }
+
+  /**
+   * Translates {@link RexNode} expressions into Geode expression strings.
+   */
+  static class Translator {
+    private final RelDataType rowType;
+
+    private final List<String> fieldNames;
+
+    Translator(RelDataType rowType) {
+      this.rowType = rowType;
+      this.fieldNames = GeodeRules.geodeFieldNames(rowType);
+    }
+
+    /**
+     * Converts the value of a literal to a string.
+     *
+     * @param literal Literal to translate
+     * @return String representation of the literal
+     */
+    private static String literalValue(RexLiteral literal) {
+      Object value = literal.getValue2();
+      StringBuilder buf = new StringBuilder();
+      buf.append(value);
+      return buf.toString();
+    }
+
+    /**
+     * Produce the OQL predicate string for the given condition.
+     *
+     * @param condition Condition to translate
+     * @return OQL predicate string
+     */
+    private String translateMatch(RexNode condition) {
+      // Returns condition decomposed by OR
+      List<RexNode> disjunctions = RelOptUtil.disjunctions(condition);
+      if (disjunctions.size() == 1) {
+        return translateAnd(disjunctions.get(0));
+      } else {
+        return translateOr(disjunctions);
+      }
+    }
+
+    /**
+     * Translate a conjunctive predicate to a OQL string.
+     *
+     * @param condition A conjunctive predicate
+     * @return OQL string for the predicate
+     */
+    private String translateAnd(RexNode condition) {
+      List<String> predicates = new ArrayList<String>();
+      for (RexNode node : RelOptUtil.conjunctions(condition)) {
+        predicates.add(translateMatch2(node));
+      }
+
+      return Util.toString(predicates, "", " AND ", "");
+    }
+
+    private String translateOr(List<RexNode> disjunctions) {
+      List<String> predicates = new ArrayList<String>();
+      for (RexNode node : disjunctions) {
+        if (RelOptUtil.conjunctions(node).size() > 1) {
+          predicates.add("(" + translateMatch(node) + ")");
+        } else {
+          predicates.add(translateMatch2(node));
+        }
+      }
+
+      return Util.toString(predicates, "", " OR ", "");
+    }
+
+    /**
+     * Translate a binary relation.
+     */
+    private String translateMatch2(RexNode node) {
+      // We currently only use equality, but inequalities on clustering keys
+      // should be possible in the future
+      switch (node.getKind()) {
+      case EQUALS:
+        return translateBinary("=", "=", (RexCall) node);
+      case LESS_THAN:
+        return translateBinary("<", ">", (RexCall) node);
+      case LESS_THAN_OR_EQUAL:
+        return translateBinary("<=", ">=", (RexCall) node);
+      case GREATER_THAN:
+        return translateBinary(">", "<", (RexCall) node);
+      case GREATER_THAN_OR_EQUAL:
+        return translateBinary(">=", "<=", (RexCall) node);
+      default:
+        throw new AssertionError("cannot translate " + node);
+      }
+    }
+
+    /**
+     * Translates a call to a binary operator, reversing arguments if
+     * necessary.
+     */
+    private String translateBinary(String op, String rop, RexCall call) {
+      final RexNode left = call.operands.get(0);
+      final RexNode right = call.operands.get(1);
+      String expression = translateBinary2(op, left, right);
+      if (expression != null) {
+        return expression;
+      }
+      expression = translateBinary2(rop, right, left);
+      if (expression != null) {
+        return expression;
+      }
+      throw new AssertionError("cannot translate op " + op + " call " + call);
+    }
+
+    /**
+     * Translates a call to a binary operator. Returns null on failure.
+     */
+    private String translateBinary2(String op, RexNode left, RexNode right) {
+      switch (right.getKind()) {
+      case LITERAL:
+        break;
+      default:
+        return null;
+      }
+
+      final RexLiteral rightLiteral = (RexLiteral) right;
+      switch (left.getKind()) {
+      case INPUT_REF:
+        final RexInputRef left1 = (RexInputRef) left;
+        String name = fieldNames.get(left1.getIndex());
+        return translateOp2(op, name, rightLiteral);
+      case CAST:
+        // FIXME This will not work in all cases (for example, we ignore string encoding)
+        return translateBinary2(op, ((RexCall) left).operands.get(0), right);
+      case OTHER_FUNCTION:
+        String item = left.accept(new GeodeRules.RexToGeodeTranslator(this.fieldNames));
+        return (item == null) ? null : item + " " + op + " " + quoteCharLiteral(rightLiteral);
+      default:
+        return null;
+      }
+    }
+
+    private String quoteCharLiteral(RexLiteral literal) {
+      String value = literalValue(literal);
+      if (literal.getTypeName() == CHAR) {
+        value = "'" + value + "'";
+      }
+      return value;
+    }
+
+    /**
+     * Combines a field name, operator, and literal to produce a predicate string.
+     */
+    private String translateOp2(String op, String name, RexLiteral right) {
+      String valueString = literalValue(right);
+      SqlTypeName typeName = rowType.getField(name, true, false).getType().getSqlTypeName();
+      if (NUMERIC_TYPES.contains(typeName)) {
+        // leave the value as it is
+      } else if (typeName != SqlTypeName.CHAR) {
+        valueString = "'" + valueString + "'";
+      }
+      return name + " " + op + " " + valueString;
+    }
+  }
+}
+
+// End GeodeFilter.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/707f4de9/geode/src/main/java/org/apache/calcite/adapter/geode/rel/GeodeProject.java
----------------------------------------------------------------------
diff --git a/geode/src/main/java/org/apache/calcite/adapter/geode/rel/GeodeProject.java b/geode/src/main/java/org/apache/calcite/adapter/geode/rel/GeodeProject.java
new file mode 100644
index 0000000..c8f328c
--- /dev/null
+++ b/geode/src/main/java/org/apache/calcite/adapter/geode/rel/GeodeProject.java
@@ -0,0 +1,75 @@
+/*
+ * 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.adapter.geode.rel;
+
+import org.apache.calcite.adapter.geode.rel.GeodeRules.RexToGeodeTranslator;
+import org.apache.calcite.plan.RelOptCluster;
+import org.apache.calcite.plan.RelOptCost;
+import org.apache.calcite.plan.RelOptPlanner;
+import org.apache.calcite.plan.RelTraitSet;
+import org.apache.calcite.rel.RelNode;
+import org.apache.calcite.rel.core.Project;
+import org.apache.calcite.rel.metadata.RelMetadataQuery;
+import org.apache.calcite.rel.type.RelDataType;
+import org.apache.calcite.rex.RexNode;
+import org.apache.calcite.util.Pair;
+
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Implementation of
+ * {@link Project}
+ * relational expression in Geode.
+ */
+public class GeodeProject extends Project implements GeodeRel {
+
+  public GeodeProject(RelOptCluster cluster, RelTraitSet traitSet,
+      RelNode input, List<? extends RexNode> projects, RelDataType rowType) {
+    super(cluster, traitSet, input, projects, rowType);
+    assert getConvention() == GeodeRel.CONVENTION;
+    assert getConvention() == input.getConvention();
+  }
+
+  @Override public Project copy(RelTraitSet traitSet, RelNode input,
+      List<RexNode> projects, RelDataType rowType) {
+    return new GeodeProject(getCluster(), traitSet, input, projects, rowType);
+  }
+
+  @Override public RelOptCost computeSelfCost(RelOptPlanner planner, RelMetadataQuery mq) {
+    return super.computeSelfCost(planner, mq).multiplyBy(0.1);
+  }
+
+  @Override public void implement(GeodeImplementContext geodeImplementContext) {
+
+    ((GeodeRel) getInput()).implement(geodeImplementContext);
+
+    final RexToGeodeTranslator translator =
+        new RexToGeodeTranslator(
+            GeodeRules.geodeFieldNames(getInput().getRowType()));
+    final Map<String, String> fields = new LinkedHashMap<>();
+    for (Pair<RexNode, String> pair : getNamedProjects()) {
+      final String name = pair.right;
+      final String originalName = pair.left.accept(translator);
+      fields.put(originalName, name);
+    }
+    geodeImplementContext.addSelectFields(fields);
+  }
+}
+
+// End GeodeProject.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/707f4de9/geode/src/main/java/org/apache/calcite/adapter/geode/rel/GeodeRel.java
----------------------------------------------------------------------
diff --git a/geode/src/main/java/org/apache/calcite/adapter/geode/rel/GeodeRel.java b/geode/src/main/java/org/apache/calcite/adapter/geode/rel/GeodeRel.java
new file mode 100644
index 0000000..ebf1d0d
--- /dev/null
+++ b/geode/src/main/java/org/apache/calcite/adapter/geode/rel/GeodeRel.java
@@ -0,0 +1,122 @@
+/*
+ * 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.adapter.geode.rel;
+
+import org.apache.calcite.plan.Convention;
+import org.apache.calcite.plan.RelOptTable;
+import org.apache.calcite.rel.RelNode;
+
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Relational expression that uses Geode calling convention.
+ */
+public interface GeodeRel extends RelNode {
+
+  /**
+   * Calling convention for relational operations that occur in Geode.
+   */
+  Convention CONVENTION = new Convention.Impl("GEODE", GeodeRel.class);
+
+  /**
+   * Callback for the implementation process that collects the context from the
+   * {@link GeodeRel} required to convert the relational tree into physical such.
+   *
+   * @param geodeImplementContext Context class that collects the feedback from the
+   *                              call back method calls
+   */
+  void implement(GeodeImplementContext geodeImplementContext);
+
+  /**
+   * Shared context used by the {@link GeodeRel} relations.
+   *
+   * <p>Callback context class for the implementation process that converts a
+   * tree of {@code GeodeRel} nodes into an OQL query.
+   */
+  class GeodeImplementContext {
+    final Map<String, String> selectFields = new LinkedHashMap<>();
+
+    final List<String> whereClause = new ArrayList<>();
+
+    final List<String> orderByFields = new ArrayList<>();
+
+    final List<String> groupByFields = new ArrayList<>();
+
+    final Map<String, String> oqlAggregateFunctions = new LinkedHashMap<>();
+
+    String limitValue = null;
+
+    RelOptTable table;
+
+    GeodeTable geodeTable;
+
+    /**
+     * Adds new projected fields.
+     *
+     * @param fields New fields to be projected from a query
+     */
+    public void addSelectFields(Map<String, String> fields) {
+      if (fields != null) {
+        selectFields.putAll(fields);
+      }
+    }
+
+    /**
+     * Adds new  restricted predicates.
+     *
+     * @param predicates New predicates to be applied to the query
+     */
+    public void addPredicates(List<String> predicates) {
+      if (predicates != null) {
+        whereClause.addAll(predicates);
+      }
+    }
+
+    public void addOrderByFields(List<String> orderByFieldLists) {
+      orderByFields.addAll(orderByFieldLists);
+    }
+
+    public void setLimit(String limit) {
+      limitValue = limit;
+    }
+
+    public void addGroupBy(List<String> groupByFields) {
+      this.groupByFields.addAll(groupByFields);
+    }
+
+    public void addAggregateFunctions(Map<String, String> oqlAggregateFunctions) {
+      this.oqlAggregateFunctions.putAll(oqlAggregateFunctions);
+    }
+
+    @Override public String toString() {
+      return "GeodeImplementContext{"
+          + "selectFields=" + selectFields
+          + ", whereClause=" + whereClause
+          + ", orderByFields=" + orderByFields
+          + ", limitValue='" + limitValue + '\''
+          + ", groupByFields=" + groupByFields
+          + ", table=" + table
+          + ", geodeTable=" + geodeTable
+          + '}';
+    }
+  }
+}
+
+// End GeodeRel.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/707f4de9/geode/src/main/java/org/apache/calcite/adapter/geode/rel/GeodeRules.java
----------------------------------------------------------------------
diff --git a/geode/src/main/java/org/apache/calcite/adapter/geode/rel/GeodeRules.java b/geode/src/main/java/org/apache/calcite/adapter/geode/rel/GeodeRules.java
new file mode 100644
index 0000000..bfac3f0
--- /dev/null
+++ b/geode/src/main/java/org/apache/calcite/adapter/geode/rel/GeodeRules.java
@@ -0,0 +1,370 @@
+/*
+ * 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.adapter.geode.rel;
+
+import org.apache.calcite.plan.Convention;
+import org.apache.calcite.plan.RelOptRule;
+import org.apache.calcite.plan.RelOptRuleCall;
+import org.apache.calcite.plan.RelOptUtil;
+import org.apache.calcite.plan.RelTraitSet;
+import org.apache.calcite.rel.RelCollations;
+import org.apache.calcite.rel.RelNode;
+import org.apache.calcite.rel.convert.ConverterRule;
+import org.apache.calcite.rel.core.Sort;
+import org.apache.calcite.rel.logical.LogicalAggregate;
+import org.apache.calcite.rel.logical.LogicalFilter;
+import org.apache.calcite.rel.logical.LogicalProject;
+import org.apache.calcite.rel.type.RelDataType;
+import org.apache.calcite.rex.RexCall;
+import org.apache.calcite.rex.RexInputRef;
+import org.apache.calcite.rex.RexLiteral;
+import org.apache.calcite.rex.RexNode;
+import org.apache.calcite.rex.RexVisitorImpl;
+import org.apache.calcite.sql.SqlKind;
+import org.apache.calcite.sql.fun.SqlStdOperatorTable;
+import org.apache.calcite.sql.type.SqlTypeName;
+import org.apache.calcite.sql.validate.SqlValidatorUtil;
+
+import com.google.common.base.Predicate;
+
+import java.util.AbstractList;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Rules and relational operators for {@link GeodeRel#CONVENTION} calling convention.
+ */
+public class GeodeRules {
+
+  public static final RelOptRule[] RULES = {
+      GeodeFilterRule.INSTANCE,
+      GeodeProjectRule.INSTANCE,
+      GeodeSortLimitRule.INSTANCE,
+      GeodeAggregateRule.INSTANCE
+  };
+
+  private GeodeRules() {
+  }
+
+  /**
+   * Returns 'string' if it is a call to item['string'], null otherwise.
+   */
+  static String isItem(RexCall call) {
+    if (call.getOperator() != SqlStdOperatorTable.ITEM) {
+      return null;
+    }
+    final RexNode op0 = call.getOperands().get(0);
+    final RexNode op1 = call.getOperands().get(1);
+
+    if (op0 instanceof RexInputRef
+        && ((RexInputRef) op0).getIndex() == 0
+        && op1 instanceof RexLiteral
+        && ((RexLiteral) op1).getValue2() instanceof String) {
+      return (String) ((RexLiteral) op1).getValue2();
+    }
+    return null;
+  }
+
+  static List<String> geodeFieldNames(final RelDataType rowType) {
+
+    List<String> fieldNames = new AbstractList<String>() {
+      @Override public String get(int index) {
+        return rowType.getFieldList().get(index).getName();
+      }
+
+      @Override public int size() {
+        return rowType.getFieldCount();
+      }
+    };
+
+    return SqlValidatorUtil.uniquify(fieldNames, true);
+  }
+
+  /**
+   * Translator from {@link RexNode} to strings in Geode's expression language.
+   */
+  static class RexToGeodeTranslator extends RexVisitorImpl<String> {
+
+    private final List<String> inFields;
+
+    protected RexToGeodeTranslator(List<String> inFields) {
+      super(true);
+      this.inFields = inFields;
+    }
+
+    @Override public String visitInputRef(RexInputRef inputRef) {
+      return inFields.get(inputRef.getIndex());
+    }
+
+    @Override public String visitCall(RexCall call) {
+      final List<String> strings = visitList(call.operands);
+      if (call.getOperator() == SqlStdOperatorTable.ITEM) {
+        final RexNode op1 = call.getOperands().get(1);
+        if (op1 instanceof RexLiteral) {
+          if (op1.getType().getSqlTypeName() == SqlTypeName.INTEGER) {
+            return stripQuotes(strings.get(0)) + "[" + ((RexLiteral) op1).getValue2() + "]";
+          } else if (op1.getType().getSqlTypeName() == SqlTypeName.CHAR) {
+            return stripQuotes(strings.get(0)) + "." + ((RexLiteral) op1).getValue2();
+          }
+        }
+      }
+
+      return super.visitCall(call);
+    }
+
+    private String stripQuotes(String s) {
+      return s.startsWith("'") && s.endsWith("'") ? s.substring(1, s.length() - 1) : s;
+    }
+
+    List<String> visitList(List<RexNode> list) {
+      final List<String> strings = new ArrayList<>();
+      for (RexNode node : list) {
+        strings.add(node.accept(this));
+      }
+      return strings;
+    }
+  }
+
+  /**
+   * Rule to convert a {@link LogicalProject} to a {@link GeodeProject}.
+   */
+  private static class GeodeProjectRule extends GeodeConverterRule {
+
+    private static final GeodeProjectRule INSTANCE = new GeodeProjectRule();
+
+    private GeodeProjectRule() {
+      super(LogicalProject.class, "GeodeProjectRule");
+    }
+
+    @Override public boolean matches(RelOptRuleCall call) {
+      LogicalProject project = call.rel(0);
+      for (RexNode e : project.getProjects()) {
+        if (e.getType().getSqlTypeName() == SqlTypeName.GEOMETRY) {
+          // For spatial Functions Drop to Calcite Enumerable
+          return false;
+        }
+      }
+
+      return true;
+    }
+
+    @Override public RelNode convert(RelNode rel) {
+      final LogicalProject project = (LogicalProject) rel;
+      final RelTraitSet traitSet = project.getTraitSet().replace(out);
+      return new GeodeProject(
+          project.getCluster(),
+          traitSet,
+          convert(project.getInput(), out),
+          project.getProjects(),
+          project.getRowType());
+    }
+  }
+
+  /**
+   * Rule to convert {@link org.apache.calcite.rel.core.Aggregate} to a
+   * {@link GeodeAggregate}.
+   */
+  private static class GeodeAggregateRule extends GeodeConverterRule {
+
+    private static final GeodeAggregateRule INSTANCE = new GeodeAggregateRule();
+
+    GeodeAggregateRule() {
+      super(LogicalAggregate.class, "GeodeAggregateRule");
+    }
+
+    @Override public RelNode convert(RelNode rel) {
+      final LogicalAggregate aggregate = (LogicalAggregate) rel;
+      final RelTraitSet traitSet = aggregate.getTraitSet().replace(out);
+      return new GeodeAggregate(
+          aggregate.getCluster(),
+          traitSet,
+          convert(aggregate.getInput(), traitSet.simplify()),
+          aggregate.indicator,
+          aggregate.getGroupSet(),
+          aggregate.getGroupSets(),
+          aggregate.getAggCallList());
+    }
+  }
+
+  /**
+   * Rule to convert the Limit in {@link org.apache.calcite.rel.core.Sort} to a
+   * {@link GeodeSort}.
+   */
+  private static class GeodeSortLimitRule extends RelOptRule {
+
+    private static final GeodeSortLimitRule INSTANCE =
+        new GeodeSortLimitRule(
+          new Predicate<Sort>() {
+            public boolean apply(Sort input) {
+              // OQL doesn't support for offsets (e.g. LIMIT 10 OFFSET 500)
+              return input.offset == null;
+            }
+          });
+
+    GeodeSortLimitRule(Predicate<Sort> predicate) {
+      super(operand(Sort.class, null, predicate, any()), "GeodeSortLimitRule");
+    }
+
+    @Override public void onMatch(RelOptRuleCall call) {
+      final Sort sort = call.rel(0);
+
+      final RelTraitSet traitSet = sort.getTraitSet()
+          .replace(GeodeRel.CONVENTION)
+          .replace(sort.getCollation());
+
+      GeodeSort geodeSort = new GeodeSort(sort.getCluster(), traitSet,
+          convert(sort.getInput(), traitSet.replace(RelCollations.EMPTY)),
+          sort.getCollation(), sort.fetch);
+
+      call.transformTo(geodeSort);
+    }
+  }
+
+  /**
+   * Rule to convert a {@link LogicalFilter} to a
+   * {@link GeodeFilter}.
+   */
+  private static class GeodeFilterRule extends RelOptRule {
+
+    private static final GeodeFilterRule INSTANCE = new GeodeFilterRule();
+
+    private GeodeFilterRule() {
+      super(operand(LogicalFilter.class, operand(GeodeTableScan.class, none())),
+          "GeodeFilterRule");
+    }
+
+    @Override public boolean matches(RelOptRuleCall call) {
+      // Get the condition from the filter operation
+      LogicalFilter filter = call.rel(0);
+      RexNode condition = filter.getCondition();
+
+      List<String> fieldNames = GeodeRules.geodeFieldNames(filter.getInput().getRowType());
+
+      List<RexNode> disjunctions = RelOptUtil.disjunctions(condition);
+      if (disjunctions.size() != 1) {
+        return true;
+      } else {
+        // Check that all conjunctions are primary field conditions.
+        condition = disjunctions.get(0);
+        for (RexNode predicate : RelOptUtil.conjunctions(condition)) {
+          if (!isEqualityOnKey(predicate, fieldNames)) {
+            return false;
+          }
+        }
+      }
+
+      return true;
+    }
+
+    /**
+     * Check if the node is a supported predicate (primary field condition).
+     *
+     * @param node       Condition node to check
+     * @param fieldNames Names of all columns in the table
+     * @return True if the node represents an equality predicate on a primary key
+     */
+    private boolean isEqualityOnKey(RexNode node, List<String> fieldNames) {
+
+      RexCall call = (RexCall) node;
+      final RexNode left = call.operands.get(0);
+      final RexNode right = call.operands.get(1);
+
+      if (checkConditionContainsInputRefOrLiterals(left, right, fieldNames)) {
+        return true;
+      }
+      return checkConditionContainsInputRefOrLiterals(right, left, fieldNames);
+
+    }
+
+    /**
+     * Checks whether a condition contains input refs of literals.
+     *
+     * @param left       Left operand of the equality
+     * @param right      Right operand of the equality
+     * @param fieldNames Names of all columns in the table
+     * @return Whether condition is supported
+     */
+    private boolean checkConditionContainsInputRefOrLiterals(RexNode left,
+        RexNode right, List<String> fieldNames) {
+      // FIXME Ignore casts for rel and assume they aren't really necessary
+      if (left.isA(SqlKind.CAST)) {
+        left = ((RexCall) left).getOperands().get(0);
+      }
+
+      if (right.isA(SqlKind.CAST)) {
+        right = ((RexCall) right).getOperands().get(0);
+      }
+
+      if (left.isA(SqlKind.INPUT_REF) && right.isA(SqlKind.LITERAL)) {
+        final RexInputRef left1 = (RexInputRef) left;
+        String name = fieldNames.get(left1.getIndex());
+        return name != null;
+      } else if (left.isA(SqlKind.INPUT_REF) && right.isA(SqlKind.INPUT_REF)) {
+
+        final RexInputRef left1 = (RexInputRef) left;
+        String leftName = fieldNames.get(left1.getIndex());
+
+        final RexInputRef right1 = (RexInputRef) right;
+        String rightName = fieldNames.get(right1.getIndex());
+
+        return (leftName != null) && (rightName != null);
+      }
+      if (left.isA(SqlKind.OTHER_FUNCTION) && right.isA(SqlKind.LITERAL)) {
+        if (((RexCall) left).getOperator() != SqlStdOperatorTable.ITEM) {
+          return false;
+        }
+        // Should be ITEM
+        return true;
+      }
+
+      return false;
+    }
+
+    public void onMatch(RelOptRuleCall call) {
+      LogicalFilter filter = call.rel(0);
+      GeodeTableScan scan = call.rel(1);
+      if (filter.getTraitSet().contains(Convention.NONE)) {
+        final RelNode converted = convert(filter, scan);
+        call.transformTo(converted);
+      }
+    }
+
+    private RelNode convert(LogicalFilter filter, GeodeTableScan scan) {
+      final RelTraitSet traitSet = filter.getTraitSet().replace(GeodeRel.CONVENTION);
+      return new GeodeFilter(
+          filter.getCluster(),
+          traitSet,
+          convert(filter.getInput(), GeodeRel.CONVENTION),
+          filter.getCondition());
+    }
+  }
+
+  /**
+   * Base class for planner rules that convert a relational
+   * expression to Geode calling convention.
+   */
+  abstract static class GeodeConverterRule extends ConverterRule {
+    protected final Convention out;
+
+    GeodeConverterRule(Class<? extends RelNode> clazz, String description) {
+      super(clazz, Convention.NONE, GeodeRel.CONVENTION, description);
+      this.out = GeodeRel.CONVENTION;
+    }
+  }
+}
+
+// End GeodeRules.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/707f4de9/geode/src/main/java/org/apache/calcite/adapter/geode/rel/GeodeSchema.java
----------------------------------------------------------------------
diff --git a/geode/src/main/java/org/apache/calcite/adapter/geode/rel/GeodeSchema.java b/geode/src/main/java/org/apache/calcite/adapter/geode/rel/GeodeSchema.java
new file mode 100644
index 0000000..2e38741
--- /dev/null
+++ b/geode/src/main/java/org/apache/calcite/adapter/geode/rel/GeodeSchema.java
@@ -0,0 +1,88 @@
+/*
+ * 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.adapter.geode.rel;
+
+import org.apache.calcite.adapter.geode.util.GeodeUtils;
+import org.apache.calcite.schema.SchemaPlus;
+import org.apache.calcite.schema.Table;
+import org.apache.calcite.schema.impl.AbstractSchema;
+import org.apache.calcite.util.trace.CalciteTrace;
+
+import org.apache.geode.cache.Region;
+import org.apache.geode.cache.client.ClientCache;
+
+import com.google.common.collect.ImmutableMap;
+
+import org.slf4j.Logger;
+
+import java.util.Iterator;
+import java.util.Map;
+
+import static org.apache.calcite.adapter.geode.util.GeodeUtils.createClientCache;
+import static org.apache.calcite.adapter.geode.util.GeodeUtils.createRelDataType;
+
+/**
+ * Schema mapped onto a Geode Region.
+ */
+public class GeodeSchema extends AbstractSchema {
+
+  protected static final Logger LOGGER = CalciteTrace.getPlannerTracer();
+
+  final ClientCache clientCache;
+  private final SchemaPlus parentSchema;
+  private String[] regionNames;
+  private ImmutableMap<String, Table> tableMap;
+
+  public GeodeSchema(String locatorHost, int locatorPort,
+      String[] regionNames, String pdxAutoSerializerPackageExp,
+      SchemaPlus parentSchema) {
+    super();
+    this.regionNames = regionNames;
+    this.parentSchema = parentSchema;
+
+    this.clientCache = createClientCache(locatorHost, locatorPort,
+        pdxAutoSerializerPackageExp, true);
+  }
+
+  @Override protected Map<String, Table> getTableMap() {
+
+    if (tableMap == null) {
+
+      final ImmutableMap.Builder<String, Table> builder = ImmutableMap.builder();
+
+      // Extract the first entity of each Regions and use it to build a table types
+      for (String regionName : regionNames) {
+        Region region = GeodeUtils.createRegionProxy(clientCache, regionName);
+
+        Iterator regionIterator = region.keySetOnServer().iterator();
+
+        Object firstRegionEntry = region.get(regionIterator.next());
+        // TODO: how to handle empty Regions? JMX?
+        Table table = new GeodeTable(this, regionName, createRelDataType(firstRegionEntry),
+            clientCache);
+
+        builder.put(regionName, table);
+      }
+
+      tableMap = builder.build();
+    }
+
+    return tableMap;
+  }
+}
+
+// End GeodeSchema.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/707f4de9/geode/src/main/java/org/apache/calcite/adapter/geode/rel/GeodeSchemaFactory.java
----------------------------------------------------------------------
diff --git a/geode/src/main/java/org/apache/calcite/adapter/geode/rel/GeodeSchemaFactory.java b/geode/src/main/java/org/apache/calcite/adapter/geode/rel/GeodeSchemaFactory.java
new file mode 100644
index 0000000..8052925
--- /dev/null
+++ b/geode/src/main/java/org/apache/calcite/adapter/geode/rel/GeodeSchemaFactory.java
@@ -0,0 +1,69 @@
+/*
+ * 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.adapter.geode.rel;
+
+import org.apache.calcite.model.ModelHandler;
+import org.apache.calcite.runtime.GeoFunctions;
+import org.apache.calcite.schema.Schema;
+import org.apache.calcite.schema.SchemaFactory;
+import org.apache.calcite.schema.SchemaPlus;
+
+import com.google.common.collect.ImmutableList;
+
+import java.util.Map;
+
+/**
+ * Factory that creates a {@link GeodeSchema}.
+ */
+@SuppressWarnings("UnusedDeclaration")
+public class GeodeSchemaFactory implements SchemaFactory {
+
+  public static final String LOCATOR_HOST = "locatorHost";
+  public static final String LOCATOR_PORT = "locatorPort";
+  public static final String REGIONS = "regions";
+  public static final String PDX_SERIALIZABLE_PACKAGE_PATH = "pdxSerializablePackagePath";
+  public static final String ALLOW_SPATIAL_FUNCTIONS = "spatialFunction";
+  public static final String COMMA_DELIMITER = ",";
+
+  public GeodeSchemaFactory() {
+    // Do Nothing
+  }
+
+  public synchronized Schema create(SchemaPlus parentSchema, String name,
+      Map<String, Object> operand) {
+    Map map = (Map) operand;
+    String locatorHost = (String) map.get(LOCATOR_HOST);
+    int locatorPort = Integer.valueOf((String) map.get(LOCATOR_PORT));
+    String[] regionNames = ((String) map.get(REGIONS)).split(COMMA_DELIMITER);
+    String pbxSerializablePackagePath = (String) map.get(PDX_SERIALIZABLE_PACKAGE_PATH);
+
+    boolean allowSpatialFunctions = true;
+    if (map.containsKey(ALLOW_SPATIAL_FUNCTIONS)) {
+      allowSpatialFunctions = Boolean.valueOf((String) map.get(ALLOW_SPATIAL_FUNCTIONS));
+    }
+
+    if (allowSpatialFunctions) {
+      ModelHandler.addFunctions(parentSchema, null, ImmutableList.<String>of(),
+          GeoFunctions.class.getName(), "*", true);
+    }
+
+    return new GeodeSchema(locatorHost, locatorPort, regionNames,
+        pbxSerializablePackagePath, parentSchema);
+  }
+}
+
+// End GeodeSchemaFactory.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/707f4de9/geode/src/main/java/org/apache/calcite/adapter/geode/rel/GeodeSort.java
----------------------------------------------------------------------
diff --git a/geode/src/main/java/org/apache/calcite/adapter/geode/rel/GeodeSort.java b/geode/src/main/java/org/apache/calcite/adapter/geode/rel/GeodeSort.java
new file mode 100644
index 0000000..a1a3c2c
--- /dev/null
+++ b/geode/src/main/java/org/apache/calcite/adapter/geode/rel/GeodeSort.java
@@ -0,0 +1,106 @@
+/*
+ * 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.adapter.geode.rel;
+
+import org.apache.calcite.plan.RelOptCluster;
+import org.apache.calcite.plan.RelOptCost;
+import org.apache.calcite.plan.RelOptPlanner;
+import org.apache.calcite.plan.RelTraitSet;
+import org.apache.calcite.rel.RelCollation;
+import org.apache.calcite.rel.RelFieldCollation;
+import org.apache.calcite.rel.RelNode;
+import org.apache.calcite.rel.core.Sort;
+import org.apache.calcite.rel.metadata.RelMetadataQuery;
+import org.apache.calcite.rex.RexLiteral;
+import org.apache.calcite.rex.RexNode;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Implementation of
+ * {@link Sort}
+ * relational expression in Geode.
+ */
+public class GeodeSort extends Sort implements GeodeRel {
+
+  public static final String ASC = "ASC";
+  public static final String DESC = "DESC";
+
+  /** Creates a GeodeSort. */
+  public GeodeSort(RelOptCluster cluster, RelTraitSet traitSet,
+      RelNode input, RelCollation collation, RexNode fetch) {
+    super(cluster, traitSet, input, collation, null, fetch);
+
+    assert getConvention() == GeodeRel.CONVENTION;
+    assert getConvention() == input.getConvention();
+  }
+
+  @Override public RelOptCost computeSelfCost(RelOptPlanner planner,
+      RelMetadataQuery mq) {
+
+    RelOptCost cost = super.computeSelfCost(planner, mq);
+
+    if (fetch != null) {
+      return cost.multiplyBy(0.05);
+    } else {
+      return cost;
+    }
+  }
+
+  @Override public Sort copy(RelTraitSet traitSet, RelNode input,
+      RelCollation newCollation, RexNode offset, RexNode fetch) {
+    return new GeodeSort(getCluster(), traitSet, input, collation, fetch);
+  }
+
+  @Override public void implement(GeodeImplementContext geodeImplementContext) {
+
+    ((GeodeRel) getInput()).implement(geodeImplementContext);
+
+
+    List<RelFieldCollation> sortCollations = collation.getFieldCollations();
+
+    if (!sortCollations.isEmpty()) {
+
+      List<String> orderByFields = new ArrayList<String>();
+
+      for (RelFieldCollation fieldCollation : sortCollations) {
+        final String name = fieldName(fieldCollation.getFieldIndex());
+        orderByFields.add(name + " " + direction(fieldCollation.getDirection()));
+      }
+      geodeImplementContext.addOrderByFields(orderByFields);
+    }
+
+
+    if (fetch != null) {
+      geodeImplementContext.setLimit(((RexLiteral) fetch).getValue().toString());
+    }
+  }
+
+  private String fieldName(int index) {
+    return getRowType().getFieldList().get(index).getName();
+  }
+
+  private String direction(RelFieldCollation.Direction relDirection) {
+    if (relDirection == RelFieldCollation.Direction.DESCENDING) {
+      return DESC;
+    }
+    return ASC;
+  }
+}
+
+// End GeodeSort.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/707f4de9/geode/src/main/java/org/apache/calcite/adapter/geode/rel/GeodeTable.java
----------------------------------------------------------------------
diff --git a/geode/src/main/java/org/apache/calcite/adapter/geode/rel/GeodeTable.java b/geode/src/main/java/org/apache/calcite/adapter/geode/rel/GeodeTable.java
new file mode 100644
index 0000000..c39b94c
--- /dev/null
+++ b/geode/src/main/java/org/apache/calcite/adapter/geode/rel/GeodeTable.java
@@ -0,0 +1,256 @@
+/*
+ * 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.adapter.geode.rel;
+
+import org.apache.calcite.adapter.geode.util.JavaTypeFactoryExtImpl;
+import org.apache.calcite.adapter.java.AbstractQueryableTable;
+import org.apache.calcite.linq4j.AbstractEnumerable;
+import org.apache.calcite.linq4j.Enumerable;
+import org.apache.calcite.linq4j.Enumerator;
+import org.apache.calcite.linq4j.QueryProvider;
+import org.apache.calcite.linq4j.Queryable;
+import org.apache.calcite.plan.RelOptCluster;
+import org.apache.calcite.plan.RelOptTable;
+import org.apache.calcite.rel.RelNode;
+import org.apache.calcite.rel.type.RelDataType;
+import org.apache.calcite.rel.type.RelDataTypeFactory;
+import org.apache.calcite.rel.type.RelDataTypeImpl;
+import org.apache.calcite.rel.type.RelProtoDataType;
+import org.apache.calcite.schema.SchemaPlus;
+import org.apache.calcite.schema.TranslatableTable;
+import org.apache.calcite.schema.impl.AbstractTableQueryable;
+import org.apache.calcite.sql.type.SqlTypeName;
+import org.apache.calcite.util.Util;
+
+import org.apache.geode.cache.client.ClientCache;
+import org.apache.geode.cache.query.QueryService;
+import org.apache.geode.cache.query.SelectResults;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableList.Builder;
+import com.google.common.collect.ImmutableMap;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Table based on a Geode Region
+ */
+public class GeodeTable extends AbstractQueryableTable implements TranslatableTable {
+
+  protected static final Logger LOGGER = LoggerFactory.getLogger(GeodeTable.class.getName());
+
+  private GeodeSchema schema;
+
+  private String regionName;
+
+  private RelDataType relDataType;
+
+  private ClientCache clientCache;
+
+  public GeodeTable(GeodeSchema schema,
+      String regionName,
+      RelDataType relDataType,
+      ClientCache clientCache) {
+    super(Object[].class);
+    this.schema = schema;
+    this.regionName = regionName;
+    this.relDataType = relDataType;
+    this.clientCache = clientCache;
+  }
+
+  public String toString() {
+    return "GeodeTable {" + regionName + "}";
+  }
+
+  /**
+   * Executes an OQL query on the underlying table.
+   *
+   * <p>Called by the {@link GeodeQueryable} which in turn is
+   * called via the generated code.
+   *
+   * @param clientCache Geode client cache
+   * @param fields      List of fields to project
+   * @param predicates  A list of predicates which should be used in the query
+   * @return Enumerator of results
+   */
+  public Enumerable<Object> query(final ClientCache clientCache,
+      final List<Map.Entry<String, Class>> fields,
+      final List<Map.Entry<String, String>> selectFields,
+      final List<Map.Entry<String, String>> aggregateFunctions,
+      final List<String> groupByFields,
+      List<String> predicates,
+      List<String> orderByFields,
+      String limit) {
+
+    final RelDataTypeFactory typeFactory = new JavaTypeFactoryExtImpl();
+    final RelDataTypeFactory.Builder fieldInfo = typeFactory.builder();
+
+    for (Map.Entry<String, Class> field : fields) {
+      SqlTypeName typeName = typeFactory.createJavaType(field.getValue()).getSqlTypeName();
+      fieldInfo.add(field.getKey(), typeFactory.createSqlType(typeName)).nullable(true);
+    }
+
+    final RelProtoDataType resultRowType = RelDataTypeImpl.proto(fieldInfo.build());
+
+    ImmutableMap<String, String> aggFuncMap = ImmutableMap.of();
+    if (!aggregateFunctions.isEmpty()) {
+      ImmutableMap.Builder<String, String> aggFuncMapBuilder = ImmutableMap.builder();
+      for (Map.Entry<String, String> e : aggregateFunctions) {
+        aggFuncMapBuilder.put(e.getKey(), e.getValue());
+      }
+      aggFuncMap = aggFuncMapBuilder.build();
+    }
+
+    // Construct the list of fields to project
+    Builder<String> selectBuilder = ImmutableList.builder();
+    if (!groupByFields.isEmpty()) {
+      for (String groupByField : groupByFields) {
+        selectBuilder.add(groupByField + " AS " + groupByField);
+      }
+      if (!aggFuncMap.isEmpty()) {
+        for (Map.Entry<String, String> e : aggFuncMap.entrySet()) {
+          selectBuilder.add(e.getValue() + " AS " + e.getKey());
+        }
+      }
+    } else {
+      if (selectFields.isEmpty()) {
+        if (!aggFuncMap.isEmpty()) {
+          for (Map.Entry<String, String> e : aggFuncMap.entrySet()) {
+            selectBuilder.add(e.getValue() + " AS " + e.getKey());
+          }
+        } else {
+          selectBuilder.add("*");
+        }
+      } else {
+        for (Map.Entry<String, String> field : selectFields) {
+          selectBuilder.add(field.getKey() + " AS " + field.getValue());
+        }
+      }
+    }
+
+    final String oqlSelectStatement = Util.toString(selectBuilder.build(), " ", ", ", "");
+
+    // Combine all predicates conjunctively
+    String whereClause = "";
+    if (!predicates.isEmpty()) {
+      whereClause = " WHERE ";
+      whereClause += Util.toString(predicates, "", " AND ", "");
+    }
+
+    // Build and issue the query and return an Enumerator over the results
+    StringBuilder queryBuilder = new StringBuilder("SELECT ");
+    queryBuilder.append(oqlSelectStatement);
+    queryBuilder.append(" FROM /" + regionName);
+    queryBuilder.append(whereClause);
+
+    if (!groupByFields.isEmpty()) {
+      queryBuilder.append(Util.toString(groupByFields, " GROUP BY ", ", ", ""));
+    }
+
+    if (!orderByFields.isEmpty()) {
+      queryBuilder.append(Util.toString(orderByFields, " ORDER BY ", ", ", ""));
+    }
+    if (limit != null) {
+      queryBuilder.append(" LIMIT " + limit);
+    }
+
+    final String oqlQuery = queryBuilder.toString();
+
+    LOGGER.info("OQL: " + oqlQuery);
+
+    return new AbstractEnumerable<Object>() {
+      public Enumerator<Object> enumerator() {
+        SelectResults results = null;
+        QueryService queryService = clientCache.getQueryService();
+
+        try {
+          results = (SelectResults) queryService.newQuery(oqlQuery).execute();
+        } catch (Exception e) {
+          e.printStackTrace();
+        }
+
+        return new GeodeEnumerator(results, resultRowType);
+      }
+    };
+  }
+
+  public <T> Queryable<T> asQueryable(QueryProvider queryProvider,
+      SchemaPlus schema, String tableName) {
+    return new GeodeQueryable<>(queryProvider, schema, this, tableName);
+  }
+
+  @Override public RelNode toRel(
+      RelOptTable.ToRelContext context,
+      RelOptTable relOptTable) {
+
+    final RelOptCluster cluster = context.getCluster();
+    return new GeodeTableScan(cluster, cluster.traitSetOf(GeodeRel.CONVENTION),
+        relOptTable, this, null);
+  }
+
+  @Override public RelDataType getRowType(RelDataTypeFactory typeFactory) {
+    return relDataType;
+  }
+
+  /**
+   * Implementation of {@link Queryable} based on a {@link GeodeTable}.
+   *
+   * @param <T> type
+   */
+  public static class GeodeQueryable<T> extends AbstractTableQueryable<T> {
+
+    public GeodeQueryable(QueryProvider queryProvider, SchemaPlus schema,
+        GeodeTable table, String tableName) {
+      super(queryProvider, schema, table, tableName);
+    }
+
+    // tzolov: this should never be called for queryable tables???
+    public Enumerator<T> enumerator() {
+      throw new UnsupportedOperationException("Enumerator on Queryable should never be called");
+    }
+
+    private GeodeTable getTable() {
+      return (GeodeTable) table;
+    }
+
+    private ClientCache getClientCache() {
+      return schema.unwrap(GeodeSchema.class).clientCache;
+    }
+
+    /**
+     * Called via code-generation.
+     */
+    @SuppressWarnings("UnusedDeclaration")
+    public Enumerable<Object> query(
+        List<Map.Entry<String, Class>> fields,
+        List<Map.Entry<String, String>> selectFields,
+        List<Map.Entry<String, String>> aggregateFunctions,
+        List<String> groupByFields,
+        List<String> predicates,
+        List<String> order,
+        String limit) {
+      return getTable().query(getClientCache(), fields, selectFields,
+          aggregateFunctions, groupByFields, predicates, order, limit);
+    }
+  }
+}
+
+// End GeodeTable.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/707f4de9/geode/src/main/java/org/apache/calcite/adapter/geode/rel/GeodeTableScan.java
----------------------------------------------------------------------
diff --git a/geode/src/main/java/org/apache/calcite/adapter/geode/rel/GeodeTableScan.java b/geode/src/main/java/org/apache/calcite/adapter/geode/rel/GeodeTableScan.java
new file mode 100644
index 0000000..b83838f
--- /dev/null
+++ b/geode/src/main/java/org/apache/calcite/adapter/geode/rel/GeodeTableScan.java
@@ -0,0 +1,80 @@
+/*
+ * 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.adapter.geode.rel;
+
+import org.apache.calcite.plan.RelOptCluster;
+import org.apache.calcite.plan.RelOptPlanner;
+import org.apache.calcite.plan.RelOptRule;
+import org.apache.calcite.plan.RelOptTable;
+import org.apache.calcite.plan.RelTraitSet;
+import org.apache.calcite.rel.RelNode;
+import org.apache.calcite.rel.core.TableScan;
+import org.apache.calcite.rel.type.RelDataType;
+
+import java.util.List;
+
+/**
+ * Relational expression representing a scan of a Geode collection.
+ */
+public class GeodeTableScan extends TableScan implements GeodeRel {
+
+  final GeodeTable geodeTable;
+  final RelDataType projectRowType;
+
+  /**
+   * Creates a GeodeTableScan.
+   *
+   * @param cluster        Cluster
+   * @param traitSet       Traits
+   * @param table          Table
+   * @param geodeTable     Geode table
+   * @param projectRowType Fields and types to project; null to project raw row
+   */
+  protected GeodeTableScan(RelOptCluster cluster, RelTraitSet traitSet,
+      RelOptTable table, GeodeTable geodeTable, RelDataType projectRowType) {
+    super(cluster, traitSet, table);
+    this.geodeTable = geodeTable;
+    this.projectRowType = projectRowType;
+
+    assert geodeTable != null;
+    assert getConvention() == GeodeRel.CONVENTION;
+  }
+
+  @Override public RelNode copy(RelTraitSet traitSet, List<RelNode> inputs) {
+    assert inputs.isEmpty();
+    return this;
+  }
+
+  @Override public RelDataType deriveRowType() {
+    return projectRowType != null ? projectRowType : super.deriveRowType();
+  }
+
+  @Override public void register(RelOptPlanner planner) {
+    planner.addRule(GeodeToEnumerableConverterRule.INSTANCE);
+    for (RelOptRule rule : GeodeRules.RULES) {
+      planner.addRule(rule);
+    }
+  }
+
+  @Override public void implement(GeodeImplementContext geodeImplementContext) {
+    // Note: Scan is the leaf and we do NOT visit its inputs
+    geodeImplementContext.geodeTable = geodeTable;
+    geodeImplementContext.table = table;
+  }
+}
+
+// End GeodeTableScan.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/707f4de9/geode/src/main/java/org/apache/calcite/adapter/geode/rel/GeodeToEnumerableConverter.java
----------------------------------------------------------------------
diff --git a/geode/src/main/java/org/apache/calcite/adapter/geode/rel/GeodeToEnumerableConverter.java b/geode/src/main/java/org/apache/calcite/adapter/geode/rel/GeodeToEnumerableConverter.java
new file mode 100644
index 0000000..5f85907
--- /dev/null
+++ b/geode/src/main/java/org/apache/calcite/adapter/geode/rel/GeodeToEnumerableConverter.java
@@ -0,0 +1,164 @@
+/*
+ * 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.adapter.geode.rel;
+
+import org.apache.calcite.adapter.enumerable.EnumerableRel;
+import org.apache.calcite.adapter.enumerable.EnumerableRelImplementor;
+import org.apache.calcite.adapter.enumerable.JavaRowFormat;
+import org.apache.calcite.adapter.enumerable.PhysType;
+import org.apache.calcite.adapter.enumerable.PhysTypeImpl;
+import org.apache.calcite.adapter.geode.rel.GeodeRel.GeodeImplementContext;
+import org.apache.calcite.linq4j.tree.BlockBuilder;
+import org.apache.calcite.linq4j.tree.Expression;
+import org.apache.calcite.linq4j.tree.Expressions;
+import org.apache.calcite.linq4j.tree.MethodCallExpression;
+import org.apache.calcite.linq4j.tree.Types;
+import org.apache.calcite.plan.ConventionTraitDef;
+import org.apache.calcite.plan.RelOptCluster;
+import org.apache.calcite.plan.RelOptCost;
+import org.apache.calcite.plan.RelOptPlanner;
+import org.apache.calcite.plan.RelTraitSet;
+import org.apache.calcite.rel.RelNode;
+import org.apache.calcite.rel.convert.ConverterImpl;
+import org.apache.calcite.rel.metadata.RelMetadataQuery;
+import org.apache.calcite.runtime.Hook;
+import org.apache.calcite.util.BuiltInMethod;
+import org.apache.calcite.util.Pair;
+
+import com.google.common.base.Function;
+import com.google.common.collect.Lists;
+
+import java.lang.reflect.Method;
+import java.util.AbstractList;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+import static org.apache.calcite.adapter.geode.rel.GeodeRules.geodeFieldNames;
+
+/**
+ * Relational expression representing a scan of a table in a Geode data source.
+ */
+public class GeodeToEnumerableConverter extends ConverterImpl implements EnumerableRel {
+
+  protected GeodeToEnumerableConverter(RelOptCluster cluster,
+      RelTraitSet traitSet, RelNode input) {
+    super(cluster, ConventionTraitDef.INSTANCE, traitSet, input);
+  }
+
+  @Override public RelNode copy(RelTraitSet traitSet, List<RelNode> inputs) {
+    return new GeodeToEnumerableConverter(
+        getCluster(), traitSet, sole(inputs));
+  }
+
+  @Override public RelOptCost computeSelfCost(RelOptPlanner planner,
+      RelMetadataQuery mq) {
+    return super.computeSelfCost(planner, mq).multiplyBy(.1);
+  }
+
+  /**
+   * Reference to the method {@link GeodeTable.GeodeQueryable#query},
+   * used in the {@link Expression}.
+   */
+  private static final Method GEODE_QUERY_METHOD =
+      Types.lookupMethod(GeodeTable.GeodeQueryable.class, "query", List.class,
+          List.class, List.class, List.class, List.class, List.class,
+          String.class);
+
+  /**
+   * {@inheritDoc}
+   *
+   * @param implementor GeodeImplementContext
+   */
+  @Override public Result implement(EnumerableRelImplementor implementor, Prefer pref) {
+
+    // travers all relations form this to the scan leaf
+    final GeodeImplementContext geodeImplementContext = new GeodeImplementContext();
+    ((GeodeRel) getInput()).implement(geodeImplementContext);
+
+    // PhysType is Enumerable Adapter class that maps SQL types (getRowType)
+    // with physical Java types (getJavaTypes())
+    final PhysType physType = PhysTypeImpl.of(
+        implementor.getTypeFactory(),
+        rowType,
+        pref.prefer(JavaRowFormat.ARRAY));
+
+    final List<Class> physFieldClasses = new AbstractList<Class>() {
+      public Class get(int index) {
+        return physType.fieldClass(index);
+      }
+
+      public int size() {
+        return rowType.getFieldCount();
+      }
+    };
+
+    // Expression meta-program for calling the GeodeTable.GeodeQueryable#query
+    // method form the generated code
+    final BlockBuilder blockBuilder = new BlockBuilder().append(
+        Expressions.call(
+            geodeImplementContext.table.getExpression(GeodeTable.GeodeQueryable.class),
+            GEODE_QUERY_METHOD,
+            constantArrayList(Pair.zip(geodeFieldNames(rowType), physFieldClasses), Pair.class),
+            // physical fields
+            constantArrayList(toListMapPairs(geodeImplementContext.selectFields), Pair.class),
+            // selected fields
+            constantArrayList(
+                toListMapPairs(geodeImplementContext.oqlAggregateFunctions), Pair.class),
+            constantArrayList(geodeImplementContext.groupByFields, String.class),
+            constantArrayList(geodeImplementContext.whereClause, String.class),
+            constantArrayList(geodeImplementContext.orderByFields, String.class),
+            Expressions.constant(geodeImplementContext.limitValue)));
+
+    Hook.QUERY_PLAN.run(geodeImplementContext);
+
+    return implementor.result(physType, blockBuilder.toBlock());
+  }
+
+  private static List<Map.Entry<String, String>> toListMapPairs(Map<String, String> map) {
+    List<Map.Entry<String, String>> selectList = new ArrayList<>();
+    for (Map.Entry<String, String> entry : Pair.zip(map.keySet(), map.values())) {
+      selectList.add(entry);
+    }
+    return selectList;
+  }
+
+  /**
+   * E.g. {@code constantArrayList("x", "y")} returns
+   * "Arrays.asList('x', 'y')".
+   */
+  private static <T> MethodCallExpression constantArrayList(List<T> values,
+      Class clazz) {
+    return Expressions.call(BuiltInMethod.ARRAYS_AS_LIST.method,
+        Expressions.newArrayInit(clazz, constantList(values)));
+  }
+
+  /**
+   * E.g. {@code constantList("x", "y")} returns
+   * {@code {ConstantExpression("x"), ConstantExpression("y")}}.
+   */
+  private static <T> List<Expression> constantList(List<T> values) {
+    return Lists.transform(values,
+        new Function<T, Expression>() {
+          public Expression apply(T a0) {
+            return Expressions.constant(a0);
+          }
+        });
+  }
+}
+
+// End GeodeToEnumerableConverter.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/707f4de9/geode/src/main/java/org/apache/calcite/adapter/geode/rel/GeodeToEnumerableConverterRule.java
----------------------------------------------------------------------
diff --git a/geode/src/main/java/org/apache/calcite/adapter/geode/rel/GeodeToEnumerableConverterRule.java b/geode/src/main/java/org/apache/calcite/adapter/geode/rel/GeodeToEnumerableConverterRule.java
new file mode 100644
index 0000000..c36b720
--- /dev/null
+++ b/geode/src/main/java/org/apache/calcite/adapter/geode/rel/GeodeToEnumerableConverterRule.java
@@ -0,0 +1,43 @@
+/*
+ * 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.adapter.geode.rel;
+
+import org.apache.calcite.adapter.enumerable.EnumerableConvention;
+import org.apache.calcite.plan.RelTraitSet;
+import org.apache.calcite.rel.RelNode;
+import org.apache.calcite.rel.convert.ConverterRule;
+
+/**
+ * Rule to convert a relational expression from
+ * {@link GeodeRel#CONVENTION} to {@link EnumerableConvention}.
+ */
+public class GeodeToEnumerableConverterRule extends ConverterRule {
+
+  public static final ConverterRule INSTANCE = new GeodeToEnumerableConverterRule();
+
+  private GeodeToEnumerableConverterRule() {
+    super(RelNode.class, GeodeRel.CONVENTION, EnumerableConvention.INSTANCE,
+        "GeodeToEnumerableConverterRule");
+  }
+
+  @Override public RelNode convert(RelNode rel) {
+    RelTraitSet newTraitSet = rel.getTraitSet().replace(getOutConvention());
+    return new GeodeToEnumerableConverter(rel.getCluster(), newTraitSet, rel);
+  }
+}
+
+// End GeodeToEnumerableConverterRule.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/707f4de9/geode/src/main/java/org/apache/calcite/adapter/geode/rel/package-info.java
----------------------------------------------------------------------
diff --git a/geode/src/main/java/org/apache/calcite/adapter/geode/rel/package-info.java b/geode/src/main/java/org/apache/calcite/adapter/geode/rel/package-info.java
new file mode 100644
index 0000000..468b28e
--- /dev/null
+++ b/geode/src/main/java/org/apache/calcite/adapter/geode/rel/package-info.java
@@ -0,0 +1,26 @@
+/*
+ * 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.
+ */
+
+/**
+ * Query provider based on Apache Geode (Gemfire) In Memory Data Grid
+ */
+@PackageMarker
+package org.apache.calcite.adapter.geode.rel;
+
+import org.apache.calcite.avatica.util.PackageMarker;
+
+// End package-info.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/707f4de9/geode/src/main/java/org/apache/calcite/adapter/geode/simple/GeodeSimpleEnumerator.java
----------------------------------------------------------------------
diff --git a/geode/src/main/java/org/apache/calcite/adapter/geode/simple/GeodeSimpleEnumerator.java b/geode/src/main/java/org/apache/calcite/adapter/geode/simple/GeodeSimpleEnumerator.java
new file mode 100644
index 0000000..af799b4
--- /dev/null
+++ b/geode/src/main/java/org/apache/calcite/adapter/geode/simple/GeodeSimpleEnumerator.java
@@ -0,0 +1,76 @@
+/*
+ * 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.adapter.geode.simple;
+
+import org.apache.calcite.linq4j.Enumerator;
+
+import org.apache.geode.cache.client.ClientCache;
+import org.apache.geode.cache.query.QueryService;
+import org.apache.geode.cache.query.SelectResults;
+
+import java.util.Iterator;
+
+/**
+ * Geode Simple Enumerator.
+ *
+ * @param <E> Element type
+ */
+public abstract class GeodeSimpleEnumerator<E> implements Enumerator<E> {
+
+  private Iterator results;
+
+  private E current;
+  private ClientCache clientCache;
+
+  public GeodeSimpleEnumerator(ClientCache clientCache, String regionName) {
+    this.clientCache = clientCache;
+    QueryService queryService = clientCache.getQueryService();
+    String oql = "select * from /" + regionName.trim();
+    try {
+      results = ((SelectResults) queryService.newQuery(oql).execute()).iterator();
+    } catch (Exception e) {
+      e.printStackTrace();
+      results = null;
+    }
+  }
+
+  @Override public E current() {
+    return current;
+  }
+
+  @Override public boolean moveNext() {
+
+    if (results.hasNext()) {
+      current = convert(results.next());
+      return true;
+    }
+    current = null;
+    return false;
+  }
+
+  @Override public void reset() {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override public void close() {
+    /*clientCache.close(); */
+  }
+
+  public abstract E convert(Object obj);
+}
+
+// End GeodeSimpleEnumerator.java


[4/7] calcite git commit: [CALCITE-2180] Invalid code generated for negative of byte and short values

Posted by jh...@apache.org.
[CALCITE-2180] Invalid code generated for negative of byte and short values


Project: http://git-wip-us.apache.org/repos/asf/calcite/repo
Commit: http://git-wip-us.apache.org/repos/asf/calcite/commit/3c67a605
Tree: http://git-wip-us.apache.org/repos/asf/calcite/tree/3c67a605
Diff: http://git-wip-us.apache.org/repos/asf/calcite/diff/3c67a605

Branch: refs/heads/master
Commit: 3c67a605cb6d6613ea1696288254a90f621a2834
Parents: becb6df
Author: Julian Hyde <jh...@apache.org>
Authored: Thu Feb 15 12:25:14 2018 -0800
Committer: Julian Hyde <jh...@apache.org>
Committed: Fri Feb 16 10:18:02 2018 -0800

----------------------------------------------------------------------
 .../calcite/adapter/enumerable/RexImpTable.java | 12 +++++--
 core/src/test/resources/sql/misc.iq             | 36 ++++++++++++++++++++
 .../apache/calcite/linq4j/tree/Expressions.java | 10 ++++--
 .../calcite/linq4j/test/ExpressionTest.java     | 14 ++++++++
 4 files changed, 67 insertions(+), 5 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/calcite/blob/3c67a605/core/src/main/java/org/apache/calcite/adapter/enumerable/RexImpTable.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/adapter/enumerable/RexImpTable.java b/core/src/main/java/org/apache/calcite/adapter/enumerable/RexImpTable.java
index 20d340e..a1a88ad 100644
--- a/core/src/main/java/org/apache/calcite/adapter/enumerable/RexImpTable.java
+++ b/core/src/main/java/org/apache/calcite/adapter/enumerable/RexImpTable.java
@@ -31,6 +31,7 @@ import org.apache.calcite.linq4j.tree.OptimizeShuttle;
 import org.apache.calcite.linq4j.tree.ParameterExpression;
 import org.apache.calcite.linq4j.tree.Primitive;
 import org.apache.calcite.linq4j.tree.Types;
+import org.apache.calcite.linq4j.tree.UnaryExpression;
 import org.apache.calcite.plan.RelOptTable;
 import org.apache.calcite.prepare.Prepare;
 import org.apache.calcite.rel.type.RelDataType;
@@ -1901,9 +1902,14 @@ public class RexImpTable {
         RexToLixTranslator translator,
         RexCall call,
         List<Expression> translatedOperands) {
-      return Expressions.makeUnary(
-          expressionType,
-          translatedOperands.get(0));
+      final Expression operand = translatedOperands.get(0);
+      final UnaryExpression e = Expressions.makeUnary(expressionType, operand);
+      if (e.type.equals(operand.type)) {
+        return e;
+      }
+      // Certain unary operators do not preserve type. For example, the "-"
+      // operator applied to a "byte" expression returns an "int".
+      return Expressions.convert_(e, operand.type);
     }
   }
 

http://git-wip-us.apache.org/repos/asf/calcite/blob/3c67a605/core/src/test/resources/sql/misc.iq
----------------------------------------------------------------------
diff --git a/core/src/test/resources/sql/misc.iq b/core/src/test/resources/sql/misc.iq
index b9d64cd..ba2ebd6 100644
--- a/core/src/test/resources/sql/misc.iq
+++ b/core/src/test/resources/sql/misc.iq
@@ -1030,6 +1030,42 @@ Expression 'DEPTNO' is not being grouped
 
 !use scott
 
+# ORDER BY expression with SELECT DISTINCT
+select distinct deptno, job
+from "scott".emp
+order by substring(job from 2 for 1), -deptno;
++--------+-----------+
+| DEPTNO | JOB       |
++--------+-----------+
+|     30 | SALESMAN  |
+|     30 | MANAGER   |
+|     20 | MANAGER   |
+|     10 | MANAGER   |
+|     30 | CLERK     |
+|     20 | CLERK     |
+|     10 | CLERK     |
+|     20 | ANALYST   |
+|     10 | PRESIDENT |
++--------+-----------+
+(9 rows)
+
+!ok
+
+# [CALCITE-2180] Invalid code generated for negative of byte and short values
+select -deptno as d
+from "scott".dept;
++-----+
+| D   |
++-----+
+| -40 |
+| -30 |
+| -20 |
+| -10 |
++-----+
+(4 rows)
+
+!ok
+
 # [CALCITE-2099] Incorrect code generated for UNION
 select count(*) as c from "scott".emp group by deptno
 union

http://git-wip-us.apache.org/repos/asf/calcite/blob/3c67a605/linq4j/src/main/java/org/apache/calcite/linq4j/tree/Expressions.java
----------------------------------------------------------------------
diff --git a/linq4j/src/main/java/org/apache/calcite/linq4j/tree/Expressions.java b/linq4j/src/main/java/org/apache/calcite/linq4j/tree/Expressions.java
index 6902cc4..5d7f83d 100644
--- a/linq4j/src/main/java/org/apache/calcite/linq4j/tree/Expressions.java
+++ b/linq4j/src/main/java/org/apache/calcite/linq4j/tree/Expressions.java
@@ -1605,8 +1605,14 @@ public abstract class Expressions {
    */
   public static UnaryExpression makeUnary(ExpressionType expressionType,
       Expression expression) {
-    return new UnaryExpression(expressionType, expression.getType(),
-        expression);
+    Type type = expression.getType();
+    switch (expressionType) {
+    case Negate:
+      if (type == byte.class || type == short.class) {
+        type = int.class;
+      }
+    }
+    return new UnaryExpression(expressionType, type, expression);
   }
 
   /**

http://git-wip-us.apache.org/repos/asf/calcite/blob/3c67a605/linq4j/src/test/java/org/apache/calcite/linq4j/test/ExpressionTest.java
----------------------------------------------------------------------
diff --git a/linq4j/src/test/java/org/apache/calcite/linq4j/test/ExpressionTest.java b/linq4j/src/test/java/org/apache/calcite/linq4j/test/ExpressionTest.java
index 9d094b3..8f1756d 100644
--- a/linq4j/src/test/java/org/apache/calcite/linq4j/test/ExpressionTest.java
+++ b/linq4j/src/test/java/org/apache/calcite/linq4j/test/ExpressionTest.java
@@ -835,6 +835,20 @@ public class ExpressionTest {
             Expressions.constant(true),
             Expressions.constant(0),
             Expressions.constant(null)).getType());
+
+    // In Java, "-" applied to short and byte yield int.
+    assertEquals(double.class,
+        Expressions.negate(Expressions.constant((double) 1)).getType());
+    assertEquals(float.class,
+        Expressions.negate(Expressions.constant((float) 1)).getType());
+    assertEquals(long.class,
+        Expressions.negate(Expressions.constant((long) 1)).getType());
+    assertEquals(int.class,
+        Expressions.negate(Expressions.constant(1)).getType());
+    assertEquals(int.class,
+        Expressions.negate(Expressions.constant((short) 1)).getType());
+    assertEquals(int.class,
+        Expressions.negate(Expressions.constant((byte) 1)).getType());
   }
 
   @Test public void testCompile() throws NoSuchMethodException {


[5/7] calcite git commit: [CALCITE-2059] Apache Geode Adapter (Christian Tzolov)

Posted by jh...@apache.org.
http://git-wip-us.apache.org/repos/asf/calcite/blob/707f4de9/site/_docs/adapter.md
----------------------------------------------------------------------
diff --git a/site/_docs/adapter.md b/site/_docs/adapter.md
index 35001ed..4b1a521 100644
--- a/site/_docs/adapter.md
+++ b/site/_docs/adapter.md
@@ -35,6 +35,7 @@ presenting the data as tables within a schema.
   and
   <a href="{{ site.apiRoot }}/org/apache/calcite/adapter/elasticsearch5/package-summary.html">calcite-elasticsearch5</a>)
 * [File adapter](file_adapter.html) (<a href="{{ site.apiRoot }}/org/apache/calcite/adapter/file/package-summary.html">calcite-file</a>)
+* [Geode adapter](geode_adapter.html) (<a href="{{ site.apiRoot }}/org/apache/calcite/adapter/geode/package-summary.html">calcite-geode</a>)
 * JDBC adapter (part of <a href="{{ site.apiRoot }}/org/apache/calcite/adapter/jdbc/package-summary.html">calcite-core</a>)
 * MongoDB adapter (<a href="{{ site.apiRoot }}/org/apache/calcite/adapter/mongodb/package-summary.html">calcite-mongodb</a>)
 * [OS adapter](os_adapter.html) (<a href="{{ site.apiRoot }}/org/apache/calcite/adapter/os/package-summary.html">calcite-os</a>)

http://git-wip-us.apache.org/repos/asf/calcite/blob/707f4de9/site/_docs/geode_adapter.md
----------------------------------------------------------------------
diff --git a/site/_docs/geode_adapter.md b/site/_docs/geode_adapter.md
new file mode 100644
index 0000000..f6678ee
--- /dev/null
+++ b/site/_docs/geode_adapter.md
@@ -0,0 +1,180 @@
+---
+layout: docs
+title: Apache Geode adapter
+permalink: /docs/geode_adapter.html
+---
+<!--
+{% comment %}
+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.
+{% endcomment %}
+-->
+
+For instructions on downloading and building Calcite, start with the
+[tutorial]({{ site.baseurl }}/docs/tutorial.html).
+
+> Optionally: add `-Puberjdbc` to your maven build to create a single self-contained Geode JDBC adapter jar.
+
+
+Once you've managed to compile the project, you can return here to
+start querying Apache Geode with Calcite. First, we need a
+[model definition]({{ site.baseurl }}/docs/model.html).
+The model gives Calcite the necessary parameters to create an instance
+of the Geode adapter. The models can contain definitions of
+[materializations]({{ site.baseurl }}/docs/model.html#materialization).
+The name of the tables defined in the model definition corresponds to
+[Regions](https://geode.apache.org/docs/guide/12/developing/region_options/chapter_overview.html)
+in Geode.
+
+A basic example of a model file is given below:
+
+{% highlight json %}
+{
+  "version": "1.0",
+  "defaultSchema": "geode",
+  "schemas": [
+    {
+      "name": "geode_raw",
+      "type": "custom",
+      "factory": "org.apache.calcite.adapter.geode.rel.GeodeSchemaFactory",
+      "operand": {
+        "locatorHost": "localhost",
+        "locatorPort": "10334",
+        "regions": "Zips",
+        "pdxSerializablePackagePath": ".*"
+      }
+    }
+  ]
+}
+{% endhighlight %}
+
+This adapter is targeted for Geode 1.3.x. The `regions` field allows to list (comma separated)
+all Geode regions to be appear as relational tables.
+
+Assuming this file is stored as `model.json`, you can connect to
+Geode via [`sqlline`](https://github.com/julianhyde/sqlline) as
+follows:
+
+{% highlight bash %}
+$ ./sqlline
+sqlline> !connect jdbc:calcite:model=model.json admin admin
+{% endhighlight %}
+
+`sqlline` will now accept SQL queries which access your Regions using OQL.
+However, you're not restricted to issuing queries supported by
+[OQL](https://geode.apache.org/docs/guide/latest/developing/querying_basics/chapter_overview.html).
+Calcite allows you to perform complex operations such as aggregations
+or joins. The adapter will attempt to compile the query into the most
+efficient OQL possible by exploiting filtering, sorting and aggregation directly
+in Geode where possible.
+
+For example, in the example Bookshop dataset there is a Regions `BookMaster`.
+
+We can issue a SQL query to fetch the annual retail cost ordered by the cost:
+
+{% highlight sql %}
+sqlline> SELECT
+           "yearPublished",
+           SUM("retailCost") AS "totalCost"
+         FROM "TEST"."BookMaster"
+         GROUP BY "yearPublished"
+         ORDER BY "totalCost";
++---------------+--------------------+
+| yearPublished | totalCost          |
++---------------+--------------------+
+| 1971          | 11.989999771118164 |
+| 2011          | 94.9800033569336   |
++---------------+--------------------+
+{% endhighlight %}
+
+While executing this query, the Geode adapter is able to recognize
+that the projection, grouping and ordering can be performed natively by Geode.
+
+The final OQL query given to Geode is below:
+
+{% highlight sql %}
+SELECT  yearPublished AS yearPublished,  SUM(retailCost) AS totalCost
+FROM /BookMaster
+GROUP BY yearPublished
+ORDER BY totalCost ASC
+{% endhighlight %}
+
+Operations that are not supported in Geode are handled by Calcite itself.
+For example the following JOIN query on the same Bookshop dataset
+
+{% highlight sql %}
+sqlline> SELECT
+           "i"."itemNumber",
+           "m"."author",
+           "m"."retailCost"
+         FROM "TEST"."BookInventory" "i"
+           JOIN "TEST"."BookMaster" "m" ON "i"."itemNumber" = "m"."itemNumber"
+         WHERE "m"."retailCost" > 20;
++------------+----------------+------------+
+| itemNumber | author         | retailCost |
++------------+----------------+------------+
+| 123        | Daisy Mae West | 34.99      |
++------------+----------------+------------+
+{% endhighlight %}
+
+Will result into two separate OQL queries:
+
+{% highlight sql %}
+SELECT  itemNumber AS itemNumber, retailCost AS retailCost, author AS author
+FROM /BookMaster
+WHERE retailCost > 20
+{% endhighlight %}
+
+{% highlight sql %}
+SELECT  itemNumber AS itemNumber
+FROM /BookInventory
+{% endhighlight %}
+
+And the result will be joined in Calcite.
+
+To select a particular item in Geode array field use the `fieldName[index]`
+syntax:
+{% highlight sql %}
+sqlline> SELECT
+           "loc" [0] AS "lon",
+           "loc" [1] AS "lat"
+         FROM "geode".ZIPS
+{% endhighlight %}
+
+To select a nested fields use the map `fieldName[nestedFiledName]`
+syntax:
+{% highlight sql %}
+sqlline> SELECT "primaryAddress" ['postalCode'] AS "postalCode"
+         FROM "TEST"."BookCustomer"
+         WHERE "primaryAddress" ['postalCode'] > '0';
+{% endhighlight %}
+This will project `BookCustomer.primaryAddress.postalCode` value field.
+
+The following presentations and video tutorials provide further dails
+about Geode adapter:
+
+* [Enable SQL/JDBC Access to Apache Geode/GemFire Using Apache Calcite](https://www.slideshare.net/slideshow/embed_code/key/2Mil7I0ZPMLuJU)
+  (GeodeSummit/SpringOne 2017)
+* [Access Apache Geode / GemFire over SQL/JDBC](https://www.linkedin.com/pulse/access-apache-geode-gemfire-over-sqljdbc-christian-tzolov)
+* [Explore Geode & GemFire Data with IntelliJ SQL/Database tool](https://www.linkedin.com/pulse/explore-your-geode-gemfire-data-from-within-intellij-tool-tzolov)
+* [Advanced Apache Geode Data Analytics with Apache Zeppelin over SQL/JDBC](https://www.linkedin.com/pulse/advanced-apache-geode-data-analytics-zeppelin-over-sqljdbc-tzolov)
+* [Unified Access to {Geode|Greenplum|...}](https://www.linkedin.com/pulse/unified-access-geodegreenplum-christian-tzolov)
+* [Apache Calcite for Enabling SQL Access to NoSQL Data Systems such as Apache Geode](https://schd.ws/hosted_files/apachebigdataeu2016/b6/ApacheCon2016ChristianTzolov.v3.pdf)
+  (ApacheCon Big Data, 2016)
+
+There is still significant work to do in improving the flexibility and
+performance of the adapter, but if you're looking for a quick way to
+gain additional insights into data stored in Geode, Calcite should
+prove useful.

http://git-wip-us.apache.org/repos/asf/calcite/blob/707f4de9/sqlline
----------------------------------------------------------------------
diff --git a/sqlline b/sqlline
index b23dcd9..c26f001 100755
--- a/sqlline
+++ b/sqlline
@@ -37,7 +37,7 @@ if [ ! -f target/fullclasspath.txt ]; then
 fi
 
 CP=
-for module in core cassandra druid elasticsearch2 elasticsearch5 file mongodb server spark splunk example/csv example/function; do
+for module in core cassandra druid elasticsearch2 elasticsearch5 file mongodb server spark splunk geode example/csv example/function; do
   CP=${CP}${module}/target/classes:
   CP=${CP}${module}/target/test-classes:
 done

http://git-wip-us.apache.org/repos/asf/calcite/blob/707f4de9/sqlline.bat
----------------------------------------------------------------------
diff --git a/sqlline.bat b/sqlline.bat
index f7165c0..b163e6f 100644
--- a/sqlline.bat
+++ b/sqlline.bat
@@ -23,6 +23,6 @@
 :: Copy dependency jars on first call. (To force jar refresh, remove target\dependencies)
 if not exist target\dependencies (call mvn -B dependency:copy-dependencies -DoverWriteReleases=false -DoverWriteSnapshots=false -DoverWriteIfNewer=true -DoutputDirectory=target\dependencies)
 
-java -Xmx1G -cp ".\target\dependencies\*;core\target\dependencies\*;cassandra\target\dependencies\*;druid\target\dependencies\*;elasticsearch2\target\dependencies\*;elasticsearch5\target\dependencies\*;file\target\dependencies\*;mongodb\target\dependencies\*;server\target\dependencies\*;spark\target\dependencies\*;splunk\target\dependencies\*" sqlline.SqlLine --verbose=true %*
+java -Xmx1G -cp ".\target\dependencies\*;core\target\dependencies\*;cassandra\target\dependencies\*;druid\target\dependencies\*;elasticsearch2\target\dependencies\*;elasticsearch5\target\dependencies\*;geode\target\dependencies\*;file\target\dependencies\*;mongodb\target\dependencies\*;server\target\dependencies\*;spark\target\dependencies\*;splunk\target\dependencies\*" sqlline.SqlLine --verbose=true %*
 
 :: End sqlline.bat


[6/7] calcite git commit: [CALCITE-2059] Apache Geode Adapter (Christian Tzolov)

Posted by jh...@apache.org.
http://git-wip-us.apache.org/repos/asf/calcite/blob/707f4de9/geode/src/main/java/org/apache/calcite/adapter/geode/simple/GeodeSimpleScannableTable.java
----------------------------------------------------------------------
diff --git a/geode/src/main/java/org/apache/calcite/adapter/geode/simple/GeodeSimpleScannableTable.java b/geode/src/main/java/org/apache/calcite/adapter/geode/simple/GeodeSimpleScannableTable.java
new file mode 100644
index 0000000..add3dac
--- /dev/null
+++ b/geode/src/main/java/org/apache/calcite/adapter/geode/simple/GeodeSimpleScannableTable.java
@@ -0,0 +1,75 @@
+/*
+ * 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.adapter.geode.simple;
+
+import org.apache.calcite.DataContext;
+import org.apache.calcite.linq4j.AbstractEnumerable;
+import org.apache.calcite.linq4j.Enumerable;
+import org.apache.calcite.linq4j.Enumerator;
+import org.apache.calcite.rel.type.RelDataType;
+import org.apache.calcite.rel.type.RelDataTypeFactory;
+import org.apache.calcite.schema.ScannableTable;
+import org.apache.calcite.schema.impl.AbstractTable;
+
+import org.apache.geode.cache.client.ClientCache;
+
+import static org.apache.calcite.adapter.geode.util.GeodeUtils.convertToRowValues;
+
+/**
+ * Geode Simple Scannable Table Abstraction
+ */
+public class GeodeSimpleScannableTable extends AbstractTable implements ScannableTable {
+
+  private final RelDataType relDataType;
+  private String regionName;
+  private ClientCache clientCache;
+
+  public GeodeSimpleScannableTable(String regionName, RelDataType relDataType,
+      ClientCache clientCache) {
+    super();
+
+    this.regionName = regionName;
+    this.clientCache = clientCache;
+    this.relDataType = relDataType;
+  }
+
+  @Override public String toString() {
+    return "GeodeSimpleScannableTable";
+  }
+
+  @Override public RelDataType getRowType(RelDataTypeFactory typeFactory) {
+    return relDataType;
+  }
+
+  @Override public Enumerable<Object[]> scan(DataContext root) {
+    return new AbstractEnumerable<Object[]>() {
+      public Enumerator<Object[]> enumerator() {
+        return new GeodeSimpleEnumerator<Object[]>(clientCache, regionName) {
+          @Override public Object[] convert(Object obj) {
+            Object values = convertToRowValues(relDataType.getFieldList(), obj);
+            if (values instanceof Object[]) {
+              return (Object[]) values;
+            }
+            return new Object[]{values};
+          }
+        };
+      }
+    };
+  }
+}
+
+// End GeodeSimpleScannableTable.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/707f4de9/geode/src/main/java/org/apache/calcite/adapter/geode/simple/GeodeSimpleSchema.java
----------------------------------------------------------------------
diff --git a/geode/src/main/java/org/apache/calcite/adapter/geode/simple/GeodeSimpleSchema.java b/geode/src/main/java/org/apache/calcite/adapter/geode/simple/GeodeSimpleSchema.java
new file mode 100644
index 0000000..bb98e60
--- /dev/null
+++ b/geode/src/main/java/org/apache/calcite/adapter/geode/simple/GeodeSimpleSchema.java
@@ -0,0 +1,84 @@
+/*
+ * 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.adapter.geode.simple;
+
+import org.apache.calcite.adapter.geode.util.GeodeUtils;
+import org.apache.calcite.schema.Table;
+import org.apache.calcite.schema.impl.AbstractSchema;
+
+import org.apache.geode.cache.Region;
+import org.apache.geode.cache.client.ClientCache;
+
+import com.google.common.collect.ImmutableMap;
+
+import java.util.Map;
+
+import static org.apache.calcite.adapter.geode.util.GeodeUtils.createRelDataType;
+
+/**
+ * Geode Simple Schema.
+ */
+public class GeodeSimpleSchema extends AbstractSchema {
+
+  private String locatorHost;
+  private int locatorPort;
+  private String[] regionNames;
+  private String pdxAutoSerializerPackageExp;
+  private ClientCache clientCache;
+  private ImmutableMap<String, Table> tableMap;
+
+  public GeodeSimpleSchema(
+      String locatorHost, int locatorPort,
+      String[] regionNames, String pdxAutoSerializerPackageExp) {
+    super();
+    this.locatorHost = locatorHost;
+    this.locatorPort = locatorPort;
+    this.regionNames = regionNames;
+    this.pdxAutoSerializerPackageExp = pdxAutoSerializerPackageExp;
+
+    this.clientCache = GeodeUtils.createClientCache(
+        locatorHost,
+        locatorPort,
+        pdxAutoSerializerPackageExp,
+        true);
+  }
+
+  @Override protected Map<String, Table> getTableMap() {
+
+    if (tableMap == null) {
+      final ImmutableMap.Builder<String, Table> builder = ImmutableMap.builder();
+
+      for (String regionName : regionNames) {
+
+        Region region = GeodeUtils.createRegionProxy(clientCache, regionName);
+
+        // TODO: What if the region is empty
+        Object regionEntry = region.get(region.keySetOnServer().iterator().next());
+
+        Table table = new GeodeSimpleScannableTable(regionName, createRelDataType(regionEntry),
+            clientCache);
+
+        builder.put(regionName, table);
+      }
+
+      tableMap = builder.build();
+    }
+    return tableMap;
+  }
+}
+
+// End GeodeSimpleSchema.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/707f4de9/geode/src/main/java/org/apache/calcite/adapter/geode/simple/GeodeSimpleSchemaFactory.java
----------------------------------------------------------------------
diff --git a/geode/src/main/java/org/apache/calcite/adapter/geode/simple/GeodeSimpleSchemaFactory.java b/geode/src/main/java/org/apache/calcite/adapter/geode/simple/GeodeSimpleSchemaFactory.java
new file mode 100644
index 0000000..a7b3a26
--- /dev/null
+++ b/geode/src/main/java/org/apache/calcite/adapter/geode/simple/GeodeSimpleSchemaFactory.java
@@ -0,0 +1,53 @@
+/*
+ * 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.adapter.geode.simple;
+
+import org.apache.calcite.schema.Schema;
+import org.apache.calcite.schema.SchemaFactory;
+import org.apache.calcite.schema.SchemaPlus;
+
+import java.util.Map;
+
+/**
+ * Geode Simple Table Schema Factory.
+ */
+public class GeodeSimpleSchemaFactory implements SchemaFactory {
+
+  public static final String LOCATOR_HOST = "locatorHost";
+  public static final String LOCATOR_PORT = "locatorPort";
+  public static final String REGIONS = "regions";
+  public static final String PDX_SERIALIZABLE_PACKAGE_PATH = "pdxSerializablePackagePath";
+  public static final String COMMA_DELIMITER = ",";
+
+  public GeodeSimpleSchemaFactory() {
+  }
+
+  @SuppressWarnings("rawtypes")
+  @Override public Schema create(SchemaPlus parentSchema,
+      String name, Map<String, Object> operand) {
+    Map map = (Map) operand;
+
+    String locatorHost = (String) map.get(LOCATOR_HOST);
+    int locatorPort = Integer.valueOf((String) map.get(LOCATOR_PORT));
+    String[] regionNames = ((String) map.get(REGIONS)).split(COMMA_DELIMITER);
+    String pdxSerializablePackagePath = (String) map.get(PDX_SERIALIZABLE_PACKAGE_PATH);
+
+    return new GeodeSimpleSchema(locatorHost, locatorPort, regionNames, pdxSerializablePackagePath);
+  }
+}
+
+// End GeodeSimpleSchemaFactory.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/707f4de9/geode/src/main/java/org/apache/calcite/adapter/geode/simple/package-info.java
----------------------------------------------------------------------
diff --git a/geode/src/main/java/org/apache/calcite/adapter/geode/simple/package-info.java b/geode/src/main/java/org/apache/calcite/adapter/geode/simple/package-info.java
new file mode 100644
index 0000000..7f15530
--- /dev/null
+++ b/geode/src/main/java/org/apache/calcite/adapter/geode/simple/package-info.java
@@ -0,0 +1,26 @@
+/*
+ * 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.
+ */
+
+/**
+ * Query provider based on Apache Geode (Gemfire) In Memory Data Grid
+ */
+@PackageMarker
+package org.apache.calcite.adapter.geode.simple;
+
+import org.apache.calcite.avatica.util.PackageMarker;
+
+// End package-info.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/707f4de9/geode/src/main/java/org/apache/calcite/adapter/geode/util/GeodeUtils.java
----------------------------------------------------------------------
diff --git a/geode/src/main/java/org/apache/calcite/adapter/geode/util/GeodeUtils.java b/geode/src/main/java/org/apache/calcite/adapter/geode/util/GeodeUtils.java
new file mode 100644
index 0000000..fb1c095
--- /dev/null
+++ b/geode/src/main/java/org/apache/calcite/adapter/geode/util/GeodeUtils.java
@@ -0,0 +1,278 @@
+/*
+ * 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.adapter.geode.util;
+
+import org.apache.calcite.avatica.util.DateTimeUtils;
+import org.apache.calcite.linq4j.tree.Primitive;
+import org.apache.calcite.rel.type.RelDataType;
+import org.apache.calcite.rel.type.RelDataTypeField;
+import org.apache.calcite.util.Util;
+
+import org.apache.commons.lang3.StringUtils;
+
+import org.apache.geode.cache.CacheClosedException;
+import org.apache.geode.cache.Region;
+import org.apache.geode.cache.client.ClientCache;
+import org.apache.geode.cache.client.ClientCacheFactory;
+import org.apache.geode.cache.client.ClientRegionShortcut;
+import org.apache.geode.cache.query.Struct;
+import org.apache.geode.pdx.PdxInstance;
+import org.apache.geode.pdx.ReflectionBasedAutoSerializer;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Type;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * Utilities for the Geode adapter.
+ */
+public class GeodeUtils {
+
+  protected static final Logger LOGGER = LoggerFactory.getLogger(GeodeUtils.class.getName());
+
+  /**
+   * Cache for the client proxy regions created in the current ClientCache.
+   */
+  private static final Map<String, Region> REGION_MAP = new ConcurrentHashMap<>();
+
+  private static String currentLocatorHost = "";
+  private static int currentLocatorPort = -1;
+
+  private static final JavaTypeFactoryExtImpl JAVA_TYPE_FACTORY = new JavaTypeFactoryExtImpl();
+
+  private GeodeUtils() {
+  }
+
+  /**
+   * Creates a Geode client instance connected to locator and configured to
+   * support PDX instances.
+   *
+   * <p>If an old instance exists, it will be destroyed and re-created.
+   *
+   * @param locatorHost               Locator's host address
+   * @param locatorPort               Locator's port
+   * @param autoSerializerPackagePath package name of the Domain classes loaded in the regions
+   * @return Returns a Geode {@link ClientCache} instance connected to Geode cluster
+   */
+  public static synchronized ClientCache createClientCache(String locatorHost,
+      int locatorPort, String autoSerializerPackagePath,
+      boolean readSerialized) {
+    if (locatorPort != currentLocatorPort
+        || !StringUtils.equalsIgnoreCase(currentLocatorHost, locatorHost)) {
+      LOGGER.info("Close existing ClientCache ["
+          + currentLocatorHost + ":" + currentLocatorPort + "] for new Locator connection at: ["
+          + locatorHost + ":" + locatorPort + "]");
+      currentLocatorHost = locatorHost;
+      currentLocatorPort = locatorPort;
+      closeClientCache();
+    }
+
+    try {
+      // If exists returns the existing client cache. This requires that the pre-created
+      // client proxy regions can also be resolved from the regionMap
+      return ClientCacheFactory.getAnyInstance();
+    } catch (CacheClosedException cce) {
+      // Do nothing if there is no existing instance
+    }
+
+    return new ClientCacheFactory()
+        .addPoolLocator(locatorHost, locatorPort)
+        .setPdxSerializer(new ReflectionBasedAutoSerializer(autoSerializerPackagePath))
+        .setPdxReadSerialized(readSerialized)
+        .setPdxPersistent(false)
+        .create();
+  }
+
+  public static synchronized void closeClientCache() {
+    try {
+      ClientCacheFactory.getAnyInstance().close();
+    } catch (CacheClosedException cce) {
+      // Do nothing if there is no existing instance
+    }
+    REGION_MAP.clear();
+  }
+
+  /**
+   * Obtains a proxy pointing to an existing Region on the server
+   *
+   * @param clientCache {@link ClientCache} instance to interact with the Geode server
+   * @param regionName  Name of the region to create proxy for.
+   * @return Returns a Region proxy to a remote (on the Server) regions.
+   */
+  public static synchronized Region createRegionProxy(ClientCache clientCache,
+      String regionName) {
+    Region region = REGION_MAP.get(regionName);
+    if (region == null) {
+      region = clientCache
+          .createClientRegionFactory(ClientRegionShortcut.PROXY)
+          .create(regionName);
+      REGION_MAP.put(regionName, region);
+    }
+    return region;
+  }
+
+  /**
+   * Converts a Geode object into a Row tuple.
+   *
+   * @param relDataTypeFields Table relation types
+   * @param geodeResultObject Object value returned by Geode query
+   * @return List of objects values corresponding to the relDataTypeFields
+   */
+  public static Object convertToRowValues(
+      List<RelDataTypeField> relDataTypeFields, Object geodeResultObject) {
+
+    Object values;
+
+    if (geodeResultObject instanceof Struct) {
+      values = handleStructEntry(relDataTypeFields, geodeResultObject);
+    } else if (geodeResultObject instanceof PdxInstance) {
+      values = handlePdxInstanceEntry(relDataTypeFields, geodeResultObject);
+    } else {
+      values = handleJavaObjectEntry(relDataTypeFields, geodeResultObject);
+    }
+
+    return values;
+  }
+
+  private static Object handleStructEntry(
+      List<RelDataTypeField> relDataTypeFields, Object obj) {
+
+    Struct struct = (Struct) obj;
+
+    Object[] values = new Object[relDataTypeFields.size()];
+
+    int index = 0;
+    for (RelDataTypeField relDataTypeField : relDataTypeFields) {
+      Type javaType = JAVA_TYPE_FACTORY.getJavaClass(relDataTypeField.getType());
+      Object rawValue;
+      try {
+        rawValue = struct.get(relDataTypeField.getName());
+      } catch (IllegalArgumentException e) {
+        rawValue = "<error>";
+        System.err.println("Could find field : " + relDataTypeField.getName());
+        e.printStackTrace();
+      }
+      values[index++] = convert(rawValue, (Class) javaType);
+    }
+
+    if (values.length == 1) {
+      return values[0];
+    }
+
+    return values;
+  }
+
+  private static Object handlePdxInstanceEntry(
+      List<RelDataTypeField> relDataTypeFields, Object obj) {
+
+    PdxInstance pdxEntry = (PdxInstance) obj;
+
+    Object[] values = new Object[relDataTypeFields.size()];
+
+    int index = 0;
+    for (RelDataTypeField relDataTypeField : relDataTypeFields) {
+      Type javaType = JAVA_TYPE_FACTORY.getJavaClass(relDataTypeField.getType());
+      Object rawValue = pdxEntry.getField(relDataTypeField.getName());
+      values[index++] = convert(rawValue, (Class) javaType);
+    }
+
+    if (values.length == 1) {
+      return values[0];
+    }
+
+    return values;
+  }
+
+  private static Object handleJavaObjectEntry(
+      List<RelDataTypeField> relDataTypeFields, Object obj) {
+
+    Class<?> clazz = obj.getClass();
+    if (relDataTypeFields.size() == 1) {
+      try {
+        Field javaField = clazz.getDeclaredField(relDataTypeFields.get(0).getName());
+        javaField.setAccessible(true);
+        return javaField.get(obj);
+      } catch (Exception e) {
+        e.printStackTrace();
+      }
+      return null;
+    }
+
+    Object[] values = new Object[relDataTypeFields.size()];
+
+    int index = 0;
+    for (RelDataTypeField relDataTypeField : relDataTypeFields) {
+      try {
+        Field javaField = clazz.getDeclaredField(relDataTypeField.getName());
+        javaField.setAccessible(true);
+        values[index++] = javaField.get(obj);
+      } catch (Exception e) {
+        e.printStackTrace();
+      }
+    }
+    return values;
+  }
+
+  private static Object convert(Object o, Class clazz) {
+    if (o == null) {
+      return null;
+    }
+    Primitive primitive = Primitive.of(clazz);
+    if (primitive != null) {
+      clazz = primitive.boxClass;
+    } else {
+      primitive = Primitive.ofBox(clazz);
+    }
+    if (clazz == null) {
+      // This is in case of nested Objects!
+      if (o instanceof PdxInstance) {
+        return Util.toString(
+            ((PdxInstance) o).getFieldNames(), "PDX[", ",", "]");
+      }
+      return o.toString();
+    }
+    if (clazz.isInstance(o)) {
+      return o;
+    }
+    if (o instanceof Date && primitive != null) {
+      o = ((Date) o).getTime() / DateTimeUtils.MILLIS_PER_DAY;
+    }
+    if (o instanceof Number && primitive != null) {
+      return primitive.number((Number) o);
+    }
+    return o;
+  }
+
+  // Create Relational Type by inferring a Geode entry or response instance.
+  public static RelDataType createRelDataType(Object regionEntry) {
+    JavaTypeFactoryExtImpl typeFactory = new JavaTypeFactoryExtImpl();
+    if (regionEntry instanceof PdxInstance) {
+      return typeFactory.createPdxType((PdxInstance) regionEntry);
+    } else {
+      return typeFactory.createStructType(regionEntry.getClass());
+    }
+  }
+
+}
+
+// End GeodeUtils.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/707f4de9/geode/src/main/java/org/apache/calcite/adapter/geode/util/JavaTypeFactoryExtImpl.java
----------------------------------------------------------------------
diff --git a/geode/src/main/java/org/apache/calcite/adapter/geode/util/JavaTypeFactoryExtImpl.java b/geode/src/main/java/org/apache/calcite/adapter/geode/util/JavaTypeFactoryExtImpl.java
new file mode 100644
index 0000000..ca17342
--- /dev/null
+++ b/geode/src/main/java/org/apache/calcite/adapter/geode/util/JavaTypeFactoryExtImpl.java
@@ -0,0 +1,123 @@
+/*
+ * 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.adapter.geode.util;
+
+import org.apache.calcite.adapter.java.JavaTypeFactory;
+import org.apache.calcite.jdbc.JavaRecordType;
+import org.apache.calcite.jdbc.JavaTypeFactoryImpl;
+import org.apache.calcite.rel.type.RelDataType;
+import org.apache.calcite.rel.type.RelDataTypeField;
+import org.apache.calcite.rel.type.RelDataTypeFieldImpl;
+import org.apache.calcite.rel.type.RelRecordType;
+
+import org.apache.geode.pdx.PdxInstance;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+import java.lang.reflect.Type;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+
+/**
+ * Implementation of {@link JavaTypeFactory}.
+ *
+ * <p><strong>NOTE: This class is experimental and subject to
+ * change/removal without notice</strong>.</p>
+ */
+public class JavaTypeFactoryExtImpl
+    extends JavaTypeFactoryImpl {
+
+  /**
+   * See <a href="http://stackoverflow.com/questions/16966629/what-is-the-difference-between-getfields-and-getdeclaredfields-in-java-reflectio">
+   *   the difference between fields and declared fields</a>.
+   */
+  @Override public RelDataType createStructType(Class type) {
+
+    final List<RelDataTypeField> list = new ArrayList<>();
+    for (Field field : type.getDeclaredFields()) {
+      if (!Modifier.isStatic(field.getModifiers())) {
+        // FIXME: watch out for recursion
+        final Type fieldType = field.getType();
+        list.add(
+            new RelDataTypeFieldImpl(
+                field.getName(),
+                list.size(),
+                createType(fieldType)));
+      }
+    }
+    return canonize(new JavaRecordType(list, type));
+  }
+
+  public RelDataType createPdxType(PdxInstance pdxInstance) {
+    final List<RelDataTypeField> list = new ArrayList<>();
+    for (String fieldName : pdxInstance.getFieldNames()) {
+      Object field = pdxInstance.getField(fieldName);
+
+      Type fieldType;
+
+      if (field == null) {
+        fieldType = String.class;
+      } else if (field instanceof PdxInstance) {
+        // Map Nested PDX structures as String. This relates with
+        // GeodeUtils.convert case when clazz is Null.
+        fieldType = Map.class;
+        // RelDataType boza = createPdxType((PdxInstance) field);
+      } else {
+        fieldType = field.getClass();
+      }
+
+      list.add(
+          new RelDataTypeFieldImpl(
+              fieldName,
+              list.size(),
+              createType(fieldType)));
+    }
+
+    return canonize(new RelRecordType(list));
+  }
+
+  // Experimental flattering the nested structures.
+  public RelDataType createPdxType2(PdxInstance pdxInstance) {
+    final List<RelDataTypeField> list = new ArrayList<>();
+    recursiveCreatePdxType(pdxInstance, list, "");
+    return canonize(new RelRecordType(list));
+  }
+
+  private void recursiveCreatePdxType(PdxInstance pdxInstance,
+      List<RelDataTypeField> list, String fieldNamePrefix) {
+
+    for (String fieldName : pdxInstance.getFieldNames()) {
+      Object field = pdxInstance.getField(fieldName);
+      final Type fieldType = field.getClass();
+      if (fieldType instanceof PdxInstance) {
+        recursiveCreatePdxType(
+            (PdxInstance) field, list, fieldNamePrefix + fieldName + ".");
+      } else {
+        list.add(
+            new RelDataTypeFieldImpl(
+                fieldNamePrefix + fieldName,
+                list.size(),
+                createType(fieldType)));
+      }
+    }
+  }
+
+}
+
+// End JavaTypeFactoryExtImpl.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/707f4de9/geode/src/main/java/org/apache/calcite/adapter/geode/util/package-info.java
----------------------------------------------------------------------
diff --git a/geode/src/main/java/org/apache/calcite/adapter/geode/util/package-info.java b/geode/src/main/java/org/apache/calcite/adapter/geode/util/package-info.java
new file mode 100644
index 0000000..b876717
--- /dev/null
+++ b/geode/src/main/java/org/apache/calcite/adapter/geode/util/package-info.java
@@ -0,0 +1,26 @@
+/*
+ * 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.
+ */
+
+/**
+ * Query provider based on Apache Geode (Gemfire) In Memory Data Grid
+ */
+@PackageMarker
+package org.apache.calcite.adapter.geode.util;
+
+import org.apache.calcite.avatica.util.PackageMarker;
+
+// End package-info.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/707f4de9/geode/src/test/java/org/apache/calcite/adapter/geode/rel/BaseGeodeAdapterIT.java
----------------------------------------------------------------------
diff --git a/geode/src/test/java/org/apache/calcite/adapter/geode/rel/BaseGeodeAdapterIT.java b/geode/src/test/java/org/apache/calcite/adapter/geode/rel/BaseGeodeAdapterIT.java
new file mode 100644
index 0000000..cdec337
--- /dev/null
+++ b/geode/src/test/java/org/apache/calcite/adapter/geode/rel/BaseGeodeAdapterIT.java
@@ -0,0 +1,168 @@
+/*
+ * 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.adapter.geode.rel;
+
+import org.apache.calcite.linq4j.function.Function1;
+
+import org.junit.Assert;
+
+import java.io.PrintStream;
+import java.net.URL;
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.ResultSet;
+import java.sql.ResultSetMetaData;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Properties;
+
+/**
+ * Geode Adapter IT Test
+ */
+public class BaseGeodeAdapterIT {
+
+  /**
+   * Returns a function that checks the contents of a result set against an
+   * expected string.
+   */
+  private static Function1<ResultSet, Void> expect(final String... expected) {
+    return new Function1<ResultSet, Void>() {
+      public Void apply(ResultSet resultSet) {
+        try {
+          final List<String> lines = new ArrayList<>();
+          BaseGeodeAdapterIT.collect(lines, resultSet);
+          Assert.assertEquals(Arrays.asList(expected), lines);
+        } catch (SQLException e) {
+          throw new RuntimeException(e);
+        }
+        return null;
+      }
+    };
+  }
+
+  private static void collect(List<String> result, ResultSet resultSet)
+      throws SQLException {
+    final StringBuilder buf = new StringBuilder();
+    while (resultSet.next()) {
+      buf.setLength(0);
+      int n = resultSet.getMetaData().getColumnCount();
+      String sep = "";
+      for (int i = 1; i <= n; i++) {
+        buf.append(sep)
+            .append(resultSet.getMetaData().getColumnLabel(i))
+            .append("=")
+            .append(resultSet.getString(i));
+        sep = "; ";
+      }
+      result.add(toLinux(buf.toString()));
+    }
+  }
+
+  public static String toLinux(String s) {
+    return s.replaceAll("\r\n", "\n");
+  }
+
+  protected void checkSql(String model, String sql) throws SQLException {
+    checkSql(sql, model, output());
+  }
+
+  protected Function1<ResultSet, Void> output() {
+    return new Function1<ResultSet, Void>() {
+      public Void apply(ResultSet resultSet) {
+        try {
+          output(resultSet, System.out);
+        } catch (SQLException e) {
+          throw new RuntimeException(e);
+        }
+        return null;
+      }
+    };
+  }
+
+  protected void checkSql(String model, String sql, final String... expected)
+      throws SQLException {
+    checkSql(sql, model, expect(expected));
+  }
+
+  protected void checkSql(String sql, String model, Function1<ResultSet, Void> fn)
+      throws SQLException {
+    Connection connection = null;
+    Statement statement = null;
+    try {
+      Properties info = new Properties();
+      info.put("model", jsonPath(model));
+      connection = DriverManager.getConnection("jdbc:calcite:", info);
+      statement = connection.createStatement();
+      final ResultSet resultSet = statement.executeQuery(sql);
+      fn.apply(resultSet);
+    } finally {
+      close(connection, statement);
+    }
+  }
+
+  private String jsonPath(String model) {
+    return resourcePath(model + ".json");
+  }
+
+  private String resourcePath(String path) {
+    final URL url = GeodeAdapterIT.class.getResource("/" + path);
+    String s = url.toString();
+    if (s.startsWith("file:")) {
+      s = s.substring("file:".length());
+    }
+    return s;
+  }
+
+  private void output(ResultSet resultSet, PrintStream out)
+      throws SQLException {
+    final ResultSetMetaData metaData = resultSet.getMetaData();
+    final int columnCount = metaData.getColumnCount();
+    while (resultSet.next()) {
+      for (int i = 1; true; i++) {
+        out.print(resultSet.getString(i));
+        if (i < columnCount) {
+          out.print(", ");
+        } else {
+          out.println();
+          break;
+        }
+      }
+    }
+  }
+
+  private void close(Connection connection, Statement statement) {
+    if (statement != null) {
+      try {
+        statement.close();
+      } catch (SQLException e) {
+        // ignore
+      }
+    }
+    if (connection != null) {
+      try {
+        connection.close();
+      } catch (SQLException e) {
+        // ignore
+      }
+    }
+  }
+}
+
+// End BaseGeodeAdapterIT.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/707f4de9/geode/src/test/java/org/apache/calcite/adapter/geode/rel/GeodeAdapterBookshopIT.java
----------------------------------------------------------------------
diff --git a/geode/src/test/java/org/apache/calcite/adapter/geode/rel/GeodeAdapterBookshopIT.java b/geode/src/test/java/org/apache/calcite/adapter/geode/rel/GeodeAdapterBookshopIT.java
new file mode 100644
index 0000000..ba5db45
--- /dev/null
+++ b/geode/src/test/java/org/apache/calcite/adapter/geode/rel/GeodeAdapterBookshopIT.java
@@ -0,0 +1,468 @@
+/*
+ * 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.adapter.geode.rel;
+
+import org.apache.calcite.test.CalciteAssert;
+import org.apache.calcite.util.Util;
+
+import com.google.common.collect.ImmutableMap;
+
+import org.junit.Test;
+
+/**
+ * Tests for the {@code org.apache.calcite.adapter.geode} package.
+ *
+ * <p>Before calling this rel, you need to populate Geode, as follows:
+ *
+ * <blockquote><code>
+ * git clone https://github.com/vlsi/calcite-test-dataset<br>
+ * cd calcite-rel-dataset<br>
+ * mvn install
+ * </code></blockquote>
+ *
+ * <p>This will create a virtual machine with Geode and the "bookshop"
+ * and "zips" rel dataset.
+ */
+public class GeodeAdapterBookshopIT {
+  /**
+   * Connection factory based on the "geode relational " model.
+   */
+  public static final ImmutableMap<String, String> GEODE =
+      ImmutableMap.of("model",
+          GeodeAdapterBookshopIT.class.getResource("/model-bookshop.json")
+              .getPath());
+
+
+  /**
+   * Whether to run Geode tests. Enabled by default, however rel is only
+   * included if "it" profile is activated ({@code -Pit}). To disable,
+   * specify {@code -Dcalcite.rel.geode=false} on the Java command line.
+   */
+  public static final boolean ENABLED = Util.getBooleanProperty("calcite.rel.geode", true);
+
+  /**
+   * Whether to run this rel.
+   */
+  protected boolean enabled() {
+    return ENABLED;
+  }
+
+  @Test
+  public void testSelect() {
+    CalciteAssert.that()
+        .enable(enabled())
+        .with(GEODE)
+        .query("select * from \"BookMaster\"")
+        .returnsCount(3);
+  }
+
+  @Test
+  public void testWhereEqual() {
+    CalciteAssert.that()
+        .enable(enabled())
+        .with(GEODE)
+        .query("select * from \"BookMaster\" WHERE \"itemNumber\" = 123")
+        .returnsCount(1)
+        .returns("itemNumber=123; description=Run on sentences and drivel on all things mundane;"
+            + " retailCost=34.99; yearPublished=2011; author=Daisy Mae West; title=A Treatise of "
+            + "Treatises\n")
+        .explainContains("PLAN=GeodeToEnumerableConverterRel\n"
+            + "  GeodeFilterRel(condition=[=(CAST($0):INTEGER, 123)])\n"
+            + "    GeodeTableScanRel(table=[[TEST, BookMaster]])");
+  }
+
+  @Test
+  public void testWhereWithAnd() {
+    CalciteAssert.that()
+        .enable(enabled())
+        .with(GEODE)
+        .query("select * from \"BookMaster\" WHERE \"itemNumber\" > 122 "
+            + "AND \"itemNumber\" <= 123")
+        .returnsCount(1)
+        .returns("itemNumber=123; description=Run on sentences and drivel on all things mundane; "
+            + "retailCost=34.99; yearPublished=2011; author=Daisy Mae West; title=A Treatise of "
+            + "Treatises\n")
+        .explainContains("PLAN=GeodeToEnumerableConverterRel\n"
+            + "  GeodeFilterRel(condition=[AND(>($0, 122), <=($0, 123))])\n"
+            + "    GeodeTableScanRel(table=[[TEST, BookMaster]])");
+  }
+
+  @Test
+  public void testWhereWithOr() {
+    CalciteAssert.that()
+        .enable(enabled())
+        .with(GEODE)
+        .query("select \"author\" from \"BookMaster\" "
+            + "WHERE \"itemNumber\" = 123 OR \"itemNumber\" = 789")
+        .returnsCount(2)
+        .returnsUnordered("author=Jim Heavisides", "author=Daisy Mae West")
+        .explainContains("PLAN=GeodeToEnumerableConverterRel\n"
+            + "  GeodeProjectRel(author=[$4])\n"
+            + "    GeodeFilterRel(condition=[OR(=(CAST($0):INTEGER, 123), "
+            + "=(CAST($0):INTEGER, 789))])\n"
+            + "      GeodeTableScanRel(table=[[TEST, BookMaster]])\n");
+  }
+
+  @Test
+  public void testWhereWithAndOr() {
+    CalciteAssert.that()
+        .enable(enabled())
+        .with(GEODE)
+        .query("SELECT \"author\" from \"BookMaster\" "
+            + "WHERE (\"itemNumber\" > 123 AND \"itemNumber\" = 789) "
+            + "OR \"author\"='Daisy Mae West'")
+        .returnsCount(2)
+        .returnsUnordered("author=Jim Heavisides", "author=Daisy Mae West")
+        .explainContains("PLAN=GeodeToEnumerableConverterRel\n"
+            + "  GeodeProjectRel(author=[$4])\n"
+            + "    GeodeFilterRel(condition=[OR(AND(>($0, 123), =(CAST($0):INTEGER, 789)), "
+            + "=(CAST($4):VARCHAR CHARACTER SET \"ISO-8859-1\" "
+            + "COLLATE \"ISO-8859-1$en_US$primary\", 'Daisy Mae West'))])\n"
+            + "      GeodeTableScanRel(table=[[TEST, BookMaster]])\n"
+            + "\n");
+  }
+
+  // TODO: Not supported YET
+  @Test
+  public void testWhereWithOrAnd() {
+    CalciteAssert.that()
+        .enable(enabled())
+        .with(GEODE)
+        .query("SELECT \"author\" from \"BookMaster\" "
+            + "WHERE (\"itemNumber\" > 100 OR \"itemNumber\" = 789) "
+            + "AND \"author\"='Daisy Mae West'")
+        .returnsCount(1)
+        .returnsUnordered("author=Daisy Mae West")
+        .explainContains("");
+  }
+
+  @Test
+  public void testProjectionsAndWhereGreatThan() {
+    CalciteAssert.that()
+        .enable(enabled())
+        .with(GEODE)
+        .query("select \"author\" from \"BookMaster\" WHERE \"itemNumber\" > 123")
+        .returnsCount(2)
+        .returns("author=Clarence Meeks\n"
+            + "author=Jim Heavisides\n")
+        .explainContains("PLAN=GeodeToEnumerableConverterRel\n"
+            + "  GeodeProjectRel(author=[$4])\n"
+            + "    GeodeFilterRel(condition=[>($0, 123)])\n"
+            + "      GeodeTableScanRel(table=[[TEST, BookMaster]])");
+  }
+
+  @Test
+  public void testLimit() {
+    CalciteAssert.that()
+        .enable(enabled())
+        .with(GEODE)
+        .query("select * from \"BookMaster\" LIMIT 1")
+        .returnsCount(1)
+        .returns("itemNumber=123; description=Run on sentences and drivel on all things mundane; "
+            + "retailCost=34.99; yearPublished=2011; author=Daisy Mae West; title=A Treatise of "
+            + "Treatises\n")
+        .explainContains("PLAN=GeodeToEnumerableConverterRel\n"
+            + "  GeodeSortRel(fetch=[1])\n"
+            + "    GeodeTableScanRel(table=[[TEST, BookMaster]])");
+  }
+
+  @Test
+  public void testSortWithProjection() {
+    CalciteAssert.that()
+        .enable(enabled())
+        .with(GEODE)
+        .query("select \"yearPublished\" from \"BookMaster\" ORDER BY \"yearPublished\" ASC")
+        .returnsCount(3)
+        .returns("yearPublished=1971\n"
+            + "yearPublished=2011\n"
+            + "yearPublished=2011\n")
+        .explainContains("PLAN=GeodeToEnumerableConverterRel\n"
+            + "  GeodeSortRel(sort0=[$0], dir0=[ASC])\n"
+            + "    GeodeProjectRel(yearPublished=[$3])\n"
+            + "      GeodeTableScanRel(table=[[TEST, BookMaster]])\n");
+  }
+
+  @Test
+  public void testSortWithProjectionAndLimit() {
+    CalciteAssert.that()
+        .enable(enabled())
+        .with(GEODE)
+        .query("select \"yearPublished\" from \"BookMaster\" ORDER BY \"yearPublished\" "
+            + "LIMIT 2")
+        .returnsCount(2)
+        .returns("yearPublished=1971\n"
+            + "yearPublished=2011\n")
+        .explainContains("PLAN=GeodeToEnumerableConverterRel\n"
+            + "  GeodeProjectRel(yearPublished=[$3])\n"
+            + "    GeodeSortRel(sort0=[$3], dir0=[ASC], fetch=[2])\n"
+            + "      GeodeTableScanRel(table=[[TEST, BookMaster]])\n");
+  }
+
+  @Test
+  public void testSortBy2Columns() {
+    CalciteAssert.that()
+        .enable(enabled())
+        .with(GEODE)
+        .query("select \"yearPublished\", \"itemNumber\" from \"BookMaster\" ORDER BY "
+            + "\"yearPublished\" ASC, \"itemNumber\" DESC")
+        .returnsCount(3)
+        .returns("yearPublished=1971; itemNumber=456\n"
+            + "yearPublished=2011; itemNumber=789\n"
+            + "yearPublished=2011; itemNumber=123\n")
+        .explainContains("PLAN=GeodeToEnumerableConverterRel\n"
+            + "  GeodeProjectRel(yearPublished=[$3], itemNumber=[$0])\n"
+            + "    GeodeSortRel(sort0=[$3], sort1=[$0], dir0=[ASC], dir1=[DESC])\n"
+            + "      GeodeTableScanRel(table=[[TEST, BookMaster]])\n");
+  }
+
+  //
+  // TEST Group By and Aggregation Function Support
+  //
+
+  /**
+   * OQL Error: Query contains group by columns not present in projected fields
+   * Solution: Automatically expand the projections to include all missing GROUP By columns.
+   */
+  @Test
+  public void testAddMissingGroupByColumnToProjectedFields() {
+    CalciteAssert.that()
+        .enable(enabled())
+        .with(GEODE)
+        .query("select \"yearPublished\" from \"BookMaster\" GROUP BY  \"yearPublished\", "
+            + "\"author\"")
+        .returnsCount(3)
+        .returns("yearPublished=1971\n"
+            + "yearPublished=2011\n"
+            + "yearPublished=2011\n")
+        .explainContains("PLAN=GeodeToEnumerableConverterRel\n"
+            + "  GeodeProjectRel(yearPublished=[$0])\n"
+            + "    GeodeAggregateRel(group=[{3, 4}])\n"
+            + "      GeodeTableScanRel(table=[[TEST, BookMaster]])");
+  }
+
+  /**
+   * When the group by columns match the projected fields, the optimizers removes the projected
+   * relation.
+   */
+  @Test
+  public void testMissingProjectRelationOnGroupByColumnMatchingProjectedFields() {
+    CalciteAssert.that()
+        .enable(enabled())
+        .with(GEODE)
+        .query("select \"yearPublished\" from \"BookMaster\" GROUP BY \"yearPublished\"")
+        .returnsCount(2)
+        .returns("yearPublished=1971\n"
+            + "yearPublished=2011\n")
+        .explainContains("PLAN=GeodeToEnumerableConverterRel\n"
+            + "  GeodeAggregateRel(group=[{3}])\n"
+            + "    GeodeTableScanRel(table=[[TEST, BookMaster]])");
+  }
+
+  /**
+   * When the group by columns match the projected fields, the optimizers removes the projected
+   * relation.
+   */
+  @Test
+  public void testMissingProjectRelationOnGroupByColumnMatchingProjectedFields2() {
+    CalciteAssert.that()
+        .enable(enabled())
+        .with(GEODE)
+        .query("select \"yearPublished\", MAX(\"retailCost\") from \"BookMaster\" GROUP BY "
+            + "\"yearPublished\"")
+        .returnsCount(2)
+        .returns("yearPublished=1971; EXPR$1=11.99\n"
+            + "yearPublished=2011; EXPR$1=59.99\n")
+        .explainContains("PLAN=GeodeToEnumerableConverterRel\n"
+            + "  GeodeAggregateRel(group=[{3}], EXPR$1=[MAX($2)])\n"
+            + "    GeodeTableScanRel(table=[[TEST, BookMaster]])");
+  }
+
+  @Test
+  public void testCount() {
+    CalciteAssert.that()
+        .enable(enabled())
+        .with(GEODE)
+        .query("select COUNT(\"retailCost\") from \"BookMaster\"")
+        .returnsCount(1)
+        .returns("EXPR$0=3\n")
+        .returnsValue("3")
+        .explainContains("PLAN=GeodeToEnumerableConverterRel\n"
+            + "  GeodeAggregateRel(group=[{}], EXPR$0=[COUNT($2)])\n"
+            + "    GeodeTableScanRel(table=[[TEST, BookMaster]])\n");
+  }
+
+  @Test
+  public void testCountStar() {
+    CalciteAssert.that()
+        .enable(enabled())
+        .with(GEODE)
+        .query("select COUNT(*) from \"BookMaster\"")
+        .returnsCount(1)
+        .returns("EXPR$0=3\n")
+        .explainContains("PLAN=GeodeToEnumerableConverterRel\n"
+            + "  GeodeAggregateRel(group=[{}], EXPR$0=[COUNT()])\n"
+            + "    GeodeTableScanRel(table=[[TEST, BookMaster]])\n");
+  }
+
+  @Test
+  public void testCountInGroupBy() {
+    CalciteAssert.that()
+        .enable(enabled())
+        .with(GEODE)
+        .query("select \"yearPublished\", COUNT(\"retailCost\") from \"BookMaster\" GROUP BY "
+            + "\"yearPublished\"")
+        .returnsCount(2)
+        .returns("yearPublished=1971; EXPR$1=1\n"
+            + "yearPublished=2011; EXPR$1=2\n")
+        .explainContains("PLAN=GeodeToEnumerableConverterRel\n"
+            + "  GeodeAggregateRel(group=[{3}], EXPR$1=[COUNT($2)])\n"
+            + "    GeodeTableScanRel(table=[[TEST, BookMaster]])\n");
+  }
+
+  @Test
+  public void testMaxMinSumAvg() {
+    CalciteAssert.that()
+        .enable(enabled())
+        .with(GEODE)
+        .query("select MAX(\"retailCost\"), MIN(\"retailCost\"), SUM(\"retailCost\"), AVG"
+            + "(\"retailCost\") from \"BookMaster\"")
+        .returnsCount(1)
+        .returns("EXPR$0=59.99; EXPR$1=11.99; EXPR$2=106.97000122070312; "
+            + "EXPR$3=35.65666580200195\n")
+        .explainContains("PLAN=GeodeToEnumerableConverterRel\n"
+            + "  GeodeAggregateRel(group=[{}], EXPR$0=[MAX($2)], EXPR$1=[MIN($2)], EXPR$2=[SUM($2)"
+            + "], EXPR$3=[AVG($2)])\n"
+            + "    GeodeTableScanRel(table=[[TEST, BookMaster]])\n");
+  }
+
+  @Test
+  public void testMaxMinSumAvgInGroupBy() {
+    CalciteAssert.that()
+        .enable(enabled())
+        .with(GEODE)
+        .query("select \"yearPublished\", MAX(\"retailCost\"), MIN(\"retailCost\"), SUM"
+            + "(\"retailCost\"), AVG(\"retailCost\") from \"BookMaster\" "
+            + "GROUP BY  \"yearPublished\"")
+        .returnsCount(2)
+        .returns("yearPublished=2011; EXPR$1=59.99; EXPR$2=34.99; EXPR$3=94.9800033569336; "
+            + "EXPR$4=47.4900016784668\n"
+            + "yearPublished=1971; EXPR$1=11.99; EXPR$2=11.99; EXPR$3=11.989999771118164; "
+            + "EXPR$4=11.989999771118164\n")
+        .explainContains("PLAN=GeodeToEnumerableConverterRel\n"
+            + "  GeodeAggregateRel(group=[{3}], EXPR$1=[MAX($2)], EXPR$2=[MIN($2)], EXPR$3=[SUM($2)"
+            + "], EXPR$4=[AVG($2)])\n"
+            + "    GeodeTableScanRel(table=[[TEST, BookMaster]])\n");
+  }
+
+  @Test
+  public void testGroupBy() {
+    CalciteAssert.that()
+        .enable(enabled())
+        .with(GEODE)
+        .query("select \"yearPublished\", MAX(\"retailCost\") AS MAXCOST, \"author\" from "
+            + "\"BookMaster\" GROUP BY \"yearPublished\", \"author\"")
+        .returnsCount(3)
+        .returns("yearPublished=2011; MAXCOST=59.99; author=Jim Heavisides\n"
+            + "yearPublished=1971; MAXCOST=11.99; author=Clarence Meeks\n"
+            + "yearPublished=2011; MAXCOST=34.99; author=Daisy Mae West\n")
+        .explainContains("PLAN=GeodeToEnumerableConverterRel\n"
+            + "  GeodeProjectRel(yearPublished=[$0], MAXCOST=[$2], author=[$1])\n"
+            + "    GeodeAggregateRel(group=[{3, 4}], MAXCOST=[MAX($2)])\n"
+            + "      GeodeTableScanRel(table=[[TEST, BookMaster]])\n");
+  }
+
+  @Test
+  public void testSelectWithNestedPdx() {
+    CalciteAssert.that()
+        .enable(enabled())
+        .with(GEODE)
+        .query("select * from \"BookCustomer\" limit 2")
+        .returnsCount(2)
+        .explainContains("PLAN=GeodeToEnumerableConverterRel\n"
+            + "  GeodeSortRel(fetch=[2])\n"
+            + "    GeodeTableScanRel(table=[[TEST, BookCustomer]])\n");
+  }
+
+  @Test
+  public void testSelectWithNestedPdx2() {
+    CalciteAssert.that()
+        .enable(enabled())
+        .with(GEODE)
+        .query("select \"primaryAddress\" from \"BookCustomer\" limit 2")
+        .returnsCount(2)
+        .returns("primaryAddress=PDX[addressLine1,addressLine2,addressLine3,city,state,"
+            + "postalCode,country,phoneNumber,addressTag]\n"
+            + "primaryAddress=PDX[addressLine1,addressLine2,addressLine3,city,state,postalCode,"
+            + "country,phoneNumber,addressTag]\n")
+        .explainContains("PLAN=GeodeToEnumerableConverterRel\n"
+            + "  GeodeProjectRel(primaryAddress=[$3])\n"
+            + "    GeodeSortRel(fetch=[2])\n"
+            + "      GeodeTableScanRel(table=[[TEST, BookCustomer]])\n");
+  }
+
+  @Test
+  public void testSelectWithNestedPdxFieldAccess() {
+    CalciteAssert.that()
+        .enable(enabled())
+        .with(GEODE)
+        .query("select \"primaryAddress\"['city'] as \"city\" from \"BookCustomer\" limit 2")
+        .returnsCount(2)
+        .returns("city=Topeka\n"
+            + "city=San Francisco\n")
+        .explainContains("PLAN=GeodeToEnumerableConverterRel\n"
+            + "  GeodeProjectRel(city=[ITEM($3, 'city')])\n"
+            + "    GeodeSortRel(fetch=[2])\n"
+            + "      GeodeTableScanRel(table=[[TEST, BookCustomer]])\n");
+  }
+
+  @Test
+  public void testSelectWithNullFieldValue() {
+    CalciteAssert.that()
+        .enable(enabled())
+        .with(GEODE)
+        .query("select \"primaryAddress\"['addressLine2'] from \"BookCustomer\" limit"
+            + " 2")
+        .returnsCount(2)
+        .returns("EXPR$0=null\n"
+            + "EXPR$0=null\n")
+        .explainContains("PLAN=GeodeToEnumerableConverterRel\n"
+            + "  GeodeProjectRel(EXPR$0=[ITEM($3, 'addressLine2')])\n"
+            + "    GeodeSortRel(fetch=[2])\n"
+            + "      GeodeTableScanRel(table=[[TEST, BookCustomer]])\n");
+  }
+
+  @Test
+  public void testFilterWithNestedField() {
+    CalciteAssert.that()
+        .enable(enabled())
+        .with(GEODE)
+        .query("SELECT \"primaryAddress\"['postalCode'] AS \"postalCode\"\n"
+            + "FROM \"TEST\".\"BookCustomer\"\n"
+            + "WHERE \"primaryAddress\"['postalCode'] > '0'\n")
+        .returnsCount(3)
+        .returns("postalCode=50505\n"
+            + "postalCode=50505\n"
+            + "postalCode=50505\n")
+        .explainContains("PLAN=GeodeToEnumerableConverterRel\n"
+            + "  GeodeProjectRel(postalCode=[ITEM($3, 'postalCode')])\n"
+            + "    GeodeFilterRel(condition=[>(ITEM($3, 'postalCode'), '0')])\n"
+            + "      GeodeTableScanRel(table=[[TEST, BookCustomer]])\n");
+  }
+
+}
+
+// End GeodeAdapterBookshopIT.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/707f4de9/geode/src/test/java/org/apache/calcite/adapter/geode/rel/GeodeAdapterIT.java
----------------------------------------------------------------------
diff --git a/geode/src/test/java/org/apache/calcite/adapter/geode/rel/GeodeAdapterIT.java b/geode/src/test/java/org/apache/calcite/adapter/geode/rel/GeodeAdapterIT.java
new file mode 100644
index 0000000..f416d5e
--- /dev/null
+++ b/geode/src/test/java/org/apache/calcite/adapter/geode/rel/GeodeAdapterIT.java
@@ -0,0 +1,99 @@
+/*
+ * 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.adapter.geode.rel;
+
+import org.junit.Test;
+
+import java.sql.SQLException;
+
+/**
+ * Tests for the {@code org.apache.calcite.adapter.geode} package.
+ *
+ * <p>Before calling this rel, you need to populate Geode, as follows:
+ *
+ * <blockquote><code>
+ * git clone https://github.com/vlsi/calcite-test-dataset<br>
+ * cd calcite-rel-dataset<br>
+ * mvn install
+ * </code></blockquote>
+ *
+ * <p>This will create a virtual machine with Geode and the "bookshop" and "zips" rel dataset.
+ */
+public class GeodeAdapterIT extends BaseGeodeAdapterIT {
+
+  @Test
+  public void testSqlSimple() throws SQLException {
+    checkSql("model-bookshop", "SELECT \"itemNumber\" "
+        + "FROM \"BookMaster\" WHERE \"itemNumber\" > 123");
+  }
+
+  @Test
+  public void testSqlSingleNumberWhereFilter() throws SQLException {
+    checkSql("model-bookshop", "SELECT * FROM \"BookMaster\" "
+        + "WHERE \"itemNumber\" = 123");
+  }
+
+  @Test
+  public void testSqlDistinctSort() throws SQLException {
+    checkSql("model-bookshop", "SELECT DISTINCT \"itemNumber\", \"author\" "
+        + "FROM \"BookMaster\" ORDER BY \"itemNumber\", \"author\"");
+  }
+
+  @Test
+  public void testSqlDistinctSort2() throws SQLException {
+    checkSql("model-bookshop", "SELECT \"itemNumber\", \"author\" "
+        + "FROM \"BookMaster\" GROUP BY \"itemNumber\", \"author\" ORDER BY \"itemNumber\", "
+        + "\"author\"");
+  }
+
+  @Test
+  public void testSqlDistinctSort3() throws SQLException {
+    checkSql("model-bookshop", "SELECT DISTINCT * FROM \"BookMaster\"");
+  }
+
+
+  @Test
+  public void testSqlLimit2() throws SQLException {
+    checkSql("model-bookshop", "SELECT DISTINCT * FROM \"BookMaster\" LIMIT 2");
+  }
+
+
+  @Test
+  public void testSqlDisjunciton() throws SQLException {
+    checkSql("model-bookshop", "SELECT \"author\" FROM \"BookMaster\" "
+        + "WHERE \"itemNumber\" = 789 OR \"itemNumber\" = 123");
+  }
+
+  @Test
+  public void testSqlConjunciton() throws SQLException {
+    checkSql("model-bookshop", "SELECT \"author\" FROM \"BookMaster\" "
+        + "WHERE \"itemNumber\" = 789 AND \"author\" = 'Jim Heavisides'");
+  }
+
+  @Test
+  public void testSqlBookMasterWhere() throws SQLException {
+    checkSql("model-bookshop", "select \"author\", \"title\" from \"BookMaster\" "
+        + "WHERE \"author\" = \'Jim Heavisides\' LIMIT 2");
+  }
+
+  @Test
+  public void testSqlBookMasterCount() throws SQLException {
+    checkSql("model-bookshop", "select count(*) from \"BookMaster\"");
+  }
+}
+
+// End GeodeAdapterIT.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/707f4de9/geode/src/test/java/org/apache/calcite/adapter/geode/rel/GeodeZipsIT.java
----------------------------------------------------------------------
diff --git a/geode/src/test/java/org/apache/calcite/adapter/geode/rel/GeodeZipsIT.java b/geode/src/test/java/org/apache/calcite/adapter/geode/rel/GeodeZipsIT.java
new file mode 100644
index 0000000..e465393
--- /dev/null
+++ b/geode/src/test/java/org/apache/calcite/adapter/geode/rel/GeodeZipsIT.java
@@ -0,0 +1,199 @@
+/*
+ * 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.adapter.geode.rel;
+
+import org.apache.calcite.test.CalciteAssert;
+import org.apache.calcite.util.Util;
+
+import com.google.common.collect.ImmutableMap;
+
+import org.junit.Test;
+
+/**
+ * Tests for the {@code org.apache.calcite.adapter.geode} package.
+ *
+ * <p>Before calling this rel, you need to populate Geode, as follows:
+ *
+ * <blockquote><code>
+ * git clone https://github.com/vlsi/calcite-test-dataset<br>
+ * cd calcite-rel-dataset<br>
+ * mvn install
+ * </code></blockquote>
+ *
+ * <p>This will create a virtual machine with Geode and the "bookshop" and "zips" rel dataset.
+ */
+public class GeodeZipsIT {
+  /**
+   * Connection factory based on the "geode relational " model.
+   */
+  public static final ImmutableMap<String, String> GEODE_ZIPS =
+      ImmutableMap.of("CONFORMANCE", "LENIENT", "model",
+          GeodeZipsIT.class.getResource("/model-zips.json")
+              .getPath());
+
+
+  /**
+   * Whether to run Geode tests. Enabled by default, however rel is only
+   * included if "it" profile is activated ({@code -Pit}). To disable,
+   * specify {@code -Dcalcite.rel.geode=false} on the Java command line.
+   */
+  public static final boolean ENABLED = Util.getBooleanProperty("calcite.rel.geode", true);
+
+  /**
+   * Whether to run this rel.
+   */
+  protected boolean enabled() {
+    return ENABLED;
+  }
+
+
+  @Test
+  public void testGroupByView() {
+    CalciteAssert.that()
+        .enable(enabled())
+        .with(GEODE_ZIPS)
+        .query("SELECT \"state\", SUM(\"pop\") FROM \"geode\".\"ZIPS\" GROUP BY \"state\"")
+        .returnsCount(51)
+        .explainContains("PLAN=GeodeToEnumerableConverterRel\n"
+            + "  GeodeAggregateRel(group=[{1}], EXPR$1=[SUM($0)])\n"
+            + "    GeodeProjectRel(pop=[CAST($3):INTEGER], state=[CAST($4):VARCHAR(2) CHARACTER SET"
+            + " \"ISO-8859-1\" COLLATE \"ISO-8859-1$en_US$primary\"])\n"
+            + "      GeodeTableScanRel(table=[[geode_raw, Zips]])\n");
+  }
+
+  @Test
+  public void testGroupByViewWithAliases() {
+    CalciteAssert.that()
+        .enable(enabled())
+        .with(GEODE_ZIPS)
+        .query("SELECT \"state\" as \"st\", SUM(\"pop\") \"po\" "
+            + "FROM \"geode\".\"ZIPS\" GROUP BY "
+            + "\"state\"")
+        .returnsCount(51)
+        .explainContains("PLAN=GeodeToEnumerableConverterRel\n"
+            + "  GeodeAggregateRel(group=[{1}], po=[SUM($0)])\n"
+            + "    GeodeProjectRel(pop=[CAST($3):INTEGER], state=[CAST($4):VARCHAR(2) CHARACTER SET"
+            + " \"ISO-8859-1\" COLLATE \"ISO-8859-1$en_US$primary\"])\n"
+            + "      GeodeTableScanRel(table=[[geode_raw, Zips]])\n");
+  }
+
+  @Test
+  public void testGroupByRaw() {
+    CalciteAssert.that()
+        .enable(enabled())
+        .with(GEODE_ZIPS)
+        .query("SELECT \"state\" as \"st\", SUM(\"pop\") \"po\" "
+            + "FROM \"geode_raw\".\"Zips\" GROUP"
+            + " BY \"state\"")
+        .returnsCount(51)
+        .explainContains("PLAN=GeodeToEnumerableConverterRel\n"
+            + "  GeodeAggregateRel(group=[{4}], po=[SUM($3)])\n"
+            + "    GeodeTableScanRel(table=[[geode_raw, Zips]])\n");
+  }
+
+  @Test
+  public void testGroupByRawWithAliases() {
+    CalciteAssert.that()
+        .enable(enabled())
+        .with(GEODE_ZIPS)
+        .query("SELECT \"state\" AS \"st\", SUM(\"pop\") AS \"po\" "
+            + "FROM \"geode_raw\".\"Zips\" "
+            + "GROUP BY \"state\"")
+        .returnsCount(51)
+        .explainContains("PLAN=GeodeToEnumerableConverterRel\n"
+            + "  GeodeAggregateRel(group=[{4}], po=[SUM($3)])\n"
+            + "    GeodeTableScanRel(table=[[geode_raw, Zips]])\n");
+  }
+
+  @Test
+  public void testMaxRaw() {
+    CalciteAssert.that()
+        .enable(enabled())
+        .with(GEODE_ZIPS)
+        .query("SELECT MAX(\"pop\") FROM \"geode_raw\".\"Zips\"")
+        .returnsCount(1)
+        .explainContains("PLAN=GeodeToEnumerableConverterRel\n"
+            + "  GeodeAggregateRel(group=[{}], EXPR$0=[MAX($3)])\n"
+            + "    GeodeTableScanRel(table=[[geode_raw, Zips]])\n");
+  }
+
+  @Test
+  public void testJoin() {
+    CalciteAssert.that()
+        .enable(enabled())
+        .with(GEODE_ZIPS)
+        .query("SELECT \"r\".\"_id\" FROM \"geode_raw\".\"Zips\" AS \"v\" JOIN \"geode_raw\""
+            + ".\"Zips\" AS \"r\" ON \"v\".\"_id\" = \"r\".\"_id\" LIMIT 1")
+        .returnsCount(1)
+        .explainContains("PLAN=EnumerableCalc(expr#0..2=[{inputs}], _id1=[$t0])\n"
+            + "  EnumerableLimit(fetch=[1])\n"
+            + "    EnumerableJoin(condition=[=($1, $2)], joinType=[inner])\n"
+            + "      GeodeToEnumerableConverterRel\n"
+            + "        GeodeProjectRel(_id=[$0], _id0=[CAST($0):VARCHAR CHARACTER SET "
+            + "\"ISO-8859-1\" COLLATE \"ISO-8859-1$en_US$primary\"])\n"
+            + "          GeodeTableScanRel(table=[[geode_raw, Zips]])\n"
+            + "      GeodeToEnumerableConverterRel\n"
+            + "        GeodeProjectRel(_id0=[CAST($0):VARCHAR CHARACTER SET \"ISO-8859-1\" COLLATE "
+            + "\"ISO-8859-1$en_US$primary\"])\n"
+            + "          GeodeTableScanRel(table=[[geode_raw, Zips]])\n");
+  }
+
+  @Test
+  public void testSelectLocItem() {
+    CalciteAssert.that()
+        .enable(enabled())
+        .with(GEODE_ZIPS)
+        .query("SELECT \"loc\"[0] as \"lat\", \"loc\"[1] as \"lon\" "
+            + "FROM \"geode_raw\".\"Zips\" LIMIT 1")
+        .returnsCount(1)
+        .returns("lat=-74.700748; lon=41.65158\n")
+        .explainContains("PLAN=GeodeToEnumerableConverterRel\n"
+            + "  GeodeProjectRel(lat=[ITEM($2, 0)], lon=[ITEM($2, 1)])\n"
+            + "    GeodeSortRel(fetch=[1])\n"
+            + "      GeodeTableScanRel(table=[[geode_raw, Zips]])\n");
+  }
+
+  @Test
+  public void testItemPredicate() {
+    CalciteAssert.that()
+        .enable(enabled())
+        .with(GEODE_ZIPS)
+        .query("SELECT \"loc\"[0] as \"lat\", \"loc\"[1] as \"lon\" "
+            + "FROM \"geode_raw\".\"Zips\" WHERE \"loc\"[0] < 0 LIMIT 1")
+        .returnsCount(1)
+        .returns("lat=-74.700748; lon=41.65158\n")
+        .explainContains("PLAN=GeodeToEnumerableConverterRel\n"
+            + "  GeodeProjectRel(lat=[ITEM($2, 0)], lon=[ITEM($2, 1)])\n"
+            + "    GeodeSortRel(fetch=[1])\n"
+            + "      GeodeFilterRel(condition=[<(ITEM($2, 0), 0)])\n"
+            + "        GeodeTableScanRel(table=[[geode_raw, Zips]])\n");
+
+    CalciteAssert.that()
+        .enable(enabled())
+        .with(GEODE_ZIPS)
+        .query("SELECT \"loc\"[0] as \"lat\", \"loc\"[1] as \"lon\" "
+            + "FROM \"geode_raw\".\"Zips\" WHERE \"loc\"[0] > 0 LIMIT 1")
+        .returnsCount(0)
+        .explainContains("PLAN=GeodeToEnumerableConverterRel\n"
+            + "  GeodeProjectRel(lat=[ITEM($2, 0)], lon=[ITEM($2, 1)])\n"
+            + "    GeodeSortRel(fetch=[1])\n"
+            + "      GeodeFilterRel(condition=[>(ITEM($2, 0), 0)])\n"
+            + "        GeodeTableScanRel(table=[[geode_raw, Zips]])\n");
+  }
+}
+
+// End GeodeZipsIT.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/707f4de9/geode/src/test/java/org/apache/calcite/adapter/geode/rel/RelationalJdbcExample.java
----------------------------------------------------------------------
diff --git a/geode/src/test/java/org/apache/calcite/adapter/geode/rel/RelationalJdbcExample.java b/geode/src/test/java/org/apache/calcite/adapter/geode/rel/RelationalJdbcExample.java
new file mode 100644
index 0000000..5afda9b
--- /dev/null
+++ b/geode/src/test/java/org/apache/calcite/adapter/geode/rel/RelationalJdbcExample.java
@@ -0,0 +1,103 @@
+/*
+ * 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.adapter.geode.rel;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.ResultSet;
+import java.sql.ResultSetMetaData;
+import java.sql.Statement;
+import java.util.Properties;
+
+/**
+ * Example of using Geode via JDBC.
+ *
+ * <p>Before using this example, you need to populate Geode, as follows:
+ *
+ * <blockquote><code>
+ * git clone https://github.com/vlsi/calcite-test-dataset<br>
+ * cd calcite-test-dataset<br>
+ * mvn install
+ * </code></blockquote>
+ *
+ * <p>This will create a virtual machine with Geode and the "bookshop" and "zips"
+ * test data sets.
+ */
+public class RelationalJdbcExample {
+
+  protected static final Logger LOGGER = LoggerFactory.getLogger(
+      RelationalJdbcExample.class.getName());
+
+  private RelationalJdbcExample() {
+  }
+
+  public static void main(String[] args) throws Exception {
+
+    final String geodeModelJson =
+        "inline:"
+            + "{\n"
+            + "  version: '1.0',\n"
+            + "  schemas: [\n"
+            + "     {\n"
+            + "       type: 'custom',\n"
+            + "       name: 'TEST',\n"
+            + "       factory: 'org.apache.calcite.adapter.geode.rel.GeodeSchemaFactory',\n"
+            + "       operand: {\n"
+            + "         locatorHost: 'localhost', \n"
+            + "         locatorPort: '10334', \n"
+            + "         regions: 'BookMaster,BookCustomer,BookInventory,BookOrder', \n"
+            + "         pdxSerializablePackagePath: 'org.apache.calcite.adapter.geode.domain.*' \n"
+            + "       }\n"
+            + "     }\n"
+            + "   ]\n"
+            + "}";
+
+    Class.forName("org.apache.calcite.jdbc.Driver");
+
+    Properties info = new Properties();
+    info.put("model", geodeModelJson);
+
+    Connection connection = DriverManager.getConnection("jdbc:calcite:", info);
+
+    Statement statement = connection.createStatement();
+    ResultSet resultSet = statement.executeQuery(
+        "SELECT \"b\".\"author\", \"b\".\"retailCost\", \"i\".\"quantityInStock\"\n"
+            + "FROM \"TEST\".\"BookMaster\" AS \"b\" "
+            + " INNER JOIN \"TEST\".\"BookInventory\" AS \"i\""
+            + "  ON \"b\".\"itemNumber\" = \"i\".\"itemNumber\"\n "
+            + "WHERE  \"b\".\"retailCost\" > 0");
+
+    final StringBuilder buf = new StringBuilder();
+    while (resultSet.next()) {
+      ResultSetMetaData metaData = resultSet.getMetaData();
+      for (int i = 1; i <= metaData.getColumnCount(); i++) {
+        buf.append(i > 1 ? "; " : "")
+            .append(metaData.getColumnLabel(i)).append("=").append(resultSet.getObject(i));
+      }
+      LOGGER.info("Result entry: " + buf.toString());
+      buf.setLength(0);
+    }
+    resultSet.close();
+    statement.close();
+    connection.close();
+  }
+}
+
+// End RelationalJdbcExample.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/707f4de9/geode/src/test/java/org/apache/calcite/adapter/geode/simple/BookMasterRegionTest.java
----------------------------------------------------------------------
diff --git a/geode/src/test/java/org/apache/calcite/adapter/geode/simple/BookMasterRegionTest.java b/geode/src/test/java/org/apache/calcite/adapter/geode/simple/BookMasterRegionTest.java
new file mode 100644
index 0000000..8bff14f
--- /dev/null
+++ b/geode/src/test/java/org/apache/calcite/adapter/geode/simple/BookMasterRegionTest.java
@@ -0,0 +1,57 @@
+/*
+ * 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.adapter.geode.simple;
+
+import org.apache.geode.cache.Region;
+import org.apache.geode.cache.client.ClientCache;
+import org.apache.geode.cache.client.ClientCacheFactory;
+import org.apache.geode.cache.client.ClientRegionShortcut;
+import org.apache.geode.cache.query.QueryService;
+import org.apache.geode.cache.query.SelectResults;
+import org.apache.geode.pdx.ReflectionBasedAutoSerializer;
+
+/**
+ *
+ */
+public class BookMasterRegionTest {
+
+  private BookMasterRegionTest() {
+  }
+
+  public static void main(String[] args) throws Exception {
+
+    ClientCache clientCache = new ClientCacheFactory()
+        .addPoolLocator("localhost", 10334)
+        .setPdxSerializer(new ReflectionBasedAutoSerializer("org.apache.calcite.adapter.geode.*"))
+        .create();
+
+    // Using Key/Value
+    Region bookMaster = clientCache
+        .createClientRegionFactory(ClientRegionShortcut.PROXY)
+        .create("BookMaster");
+
+    System.out.println("BookMaster = " + bookMaster.get(789));
+
+    // Using OQL
+    QueryService queryService = clientCache.getQueryService();
+    String oql = "select itemNumber, description, retailCost from /BookMaster";
+    SelectResults result = (SelectResults) queryService.newQuery(oql).execute();
+    System.out.println(result.asList());
+  }
+}
+
+// End BookMasterRegionTest.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/707f4de9/geode/src/test/java/org/apache/calcite/adapter/geode/simple/SimpleJdbcExample.java
----------------------------------------------------------------------
diff --git a/geode/src/test/java/org/apache/calcite/adapter/geode/simple/SimpleJdbcExample.java b/geode/src/test/java/org/apache/calcite/adapter/geode/simple/SimpleJdbcExample.java
new file mode 100644
index 0000000..795a531
--- /dev/null
+++ b/geode/src/test/java/org/apache/calcite/adapter/geode/simple/SimpleJdbcExample.java
@@ -0,0 +1,95 @@
+/*
+ * 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.adapter.geode.simple;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.ResultSet;
+import java.sql.Statement;
+import java.util.Properties;
+
+/**
+ * Example of using Geode via JDBC.
+ */
+public class SimpleJdbcExample {
+
+  protected static final Logger LOGGER =
+      LoggerFactory.getLogger(SimpleJdbcExample.class.getName());
+
+  private SimpleJdbcExample() {
+  }
+
+  public static void main(String[] args) throws Exception {
+
+    Properties info = new Properties();
+    final String model = "inline:"
+        + "{\n"
+        + "  version: '1.0',\n"
+        + "  schemas: [\n"
+        + "     {\n"
+        + "       type: 'custom',\n"
+        + "       name: 'TEST',\n"
+        + "       factory: 'org.apache.calcite.adapter.geode.simple"
+        + ".GeodeSimpleSchemaFactory',\n"
+        + "       operand: {\n"
+        + "         locatorHost: 'localhost',\n"
+        + "         locatorPort: '10334',\n"
+        + "         regions: 'BookMaster',\n"
+        + "         pdxSerializablePackagePath: 'org.apache.calcite.adapter.geode.domain.*'\n"
+        + "       }\n"
+        + "     }\n"
+        + "  ]\n"
+        + "}";
+    info.put("model", model);
+
+    Class.forName("org.apache.calcite.jdbc.Driver");
+
+    Connection connection = DriverManager.getConnection("jdbc:calcite:", info);
+
+    Statement statement = connection.createStatement();
+
+    ResultSet resultSet = statement.executeQuery("SELECT * FROM \"TEST\".\"BookMaster\"");
+
+    final StringBuilder buf = new StringBuilder();
+
+    while (resultSet.next()) {
+
+      int columnCount = resultSet.getMetaData().getColumnCount();
+
+      for (int i = 1; i <= columnCount; i++) {
+
+        buf.append(i > 1 ? "; " : "")
+            .append(resultSet.getMetaData().getColumnLabel(i))
+            .append("=")
+            .append(resultSet.getObject(i));
+      }
+
+      LOGGER.info("Entry: " + buf.toString());
+
+      buf.setLength(0);
+    }
+
+    resultSet.close();
+    statement.close();
+    connection.close();
+  }
+}
+
+// End SimpleJdbcExample.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/707f4de9/geode/src/test/resources/model-bookshop-all.json
----------------------------------------------------------------------
diff --git a/geode/src/test/resources/model-bookshop-all.json b/geode/src/test/resources/model-bookshop-all.json
new file mode 100644
index 0000000..13ed2e5
--- /dev/null
+++ b/geode/src/test/resources/model-bookshop-all.json
@@ -0,0 +1,35 @@
+/*
+ * 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.
+ *
+ * A JSON model of a simple Calcite schema.
+ */
+{
+  "version": "1.0",
+  "defaultSchema": "TEST",
+  "schemas": [
+    {
+      "name": "TEST",
+      "type": "custom",
+      "factory": "org.apache.calcite.adapter.geode.rel.GeodeSchemaFactory",
+      "operand": {
+        "locatorHost": "localhost",
+        "locatorPort": "10334",
+        "regions": "BookMaster,BookCustomer,BookOrder,BookInventory",
+        "pdxSerializablePackagePath": "org.apache.calcite.adapter.geode.domain.bookshop.*"
+      }
+    }
+  ]
+}

http://git-wip-us.apache.org/repos/asf/calcite/blob/707f4de9/geode/src/test/resources/model-bookshop.json
----------------------------------------------------------------------
diff --git a/geode/src/test/resources/model-bookshop.json b/geode/src/test/resources/model-bookshop.json
new file mode 100644
index 0000000..aaab978
--- /dev/null
+++ b/geode/src/test/resources/model-bookshop.json
@@ -0,0 +1,35 @@
+/*
+ * 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.
+ *
+ * A JSON model of a simple Calcite schema.
+ */
+{
+  "version": "1.0",
+  "defaultSchema": "TEST",
+  "schemas": [
+    {
+      "name": "TEST",
+      "type": "custom",
+      "factory": "org.apache.calcite.adapter.geode.rel.GeodeSchemaFactory",
+      "operand": {
+        "locatorHost": "localhost",
+        "locatorPort": "10334",
+        "regions": "BookMaster,BookCustomer",
+        "pdxSerializablePackagePath": "org.apache.calcite.adapter.geode.*"
+      }
+    }
+  ]
+}

http://git-wip-us.apache.org/repos/asf/calcite/blob/707f4de9/geode/src/test/resources/model-geode-pg-federation.json
----------------------------------------------------------------------
diff --git a/geode/src/test/resources/model-geode-pg-federation.json b/geode/src/test/resources/model-geode-pg-federation.json
new file mode 100644
index 0000000..586fafa
--- /dev/null
+++ b/geode/src/test/resources/model-geode-pg-federation.json
@@ -0,0 +1,54 @@
+/*
+ * 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.
+ *
+ * A JSON model of a simple Calcite schema.
+ */
+{
+  "version": "1.0",
+  "defaultSchema": "geode",
+  "schemas": [
+    {
+      "type": "custom",
+      "name": "geode_zips",
+      "factory": "org.apache.calcite.adapter.geode.rel.GeodeSchemaFactory",
+      "operand": {
+        "locatorHost": "localhost",
+        "locatorPort": "10334",
+        "regions": "Zips",
+        "pdxSerializablePackagePath": ".*"
+      }
+    },
+    {
+      "type": "jdbc",
+      "name": "mysql_foodmart",
+      "jdbcUser": "foodmart",
+      "jdbcPassword": "foodmart",
+      "jdbcUrl": "jdbc:mysql://localhost",
+      "jdbcCatalog": "foodmart",
+      "jdbcSchema": null
+    },
+    {
+      "type": "jdbc",
+      "name": "postgresql_foodmart",
+      "jdbcUser": "foodmart",
+      "jdbcPassword": "foodmart",
+      "jdbcDriver": "org.postgresql.Driver",
+      "jdbcUrl": "jdbc:postgresql://localhost/foodmart",
+      "jdbcCatalog": "foodmart",
+      "jdbcSchema": null
+    }
+  ]
+}

http://git-wip-us.apache.org/repos/asf/calcite/blob/707f4de9/geode/src/test/resources/model-with-classes.json
----------------------------------------------------------------------
diff --git a/geode/src/test/resources/model-with-classes.json b/geode/src/test/resources/model-with-classes.json
new file mode 100644
index 0000000..b776f0e
--- /dev/null
+++ b/geode/src/test/resources/model-with-classes.json
@@ -0,0 +1,39 @@
+/*
+ * 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.
+ *
+ * A JSON model of a simple Calcite schema.
+ */
+{
+  "version": "1.0",
+  "defaultSchema": "TEST",
+  "schemas": [
+    {
+      "name": "TEST",
+      "type": "custom",
+      "factory": "org.apache.calcite.adapter.geode.simple.GeodeSchemaFactory",
+      "operand": {
+        "locatorHost": "localhost",
+        "locatorPort": "10334",
+        "regions": "BookMaster,Customer,InventoryItem,BookOrder",
+        "BookMaster": "net.tzolov.geode.bookstore.domain.BookMaster",
+        "Customer": "net.tzolov.geode.bookstore.domain.Customer",
+        "InventoryItem": "net.tzolov.geode.bookstore.domain.InventoryItem",
+        "BookOrder": "net.tzolov.geode.bookstore.domain.BookOrder",
+        "pdxSerializablePackagePath": "net.tzolov.geode.bookstore.domain.*"
+      }
+    }
+  ]
+}

http://git-wip-us.apache.org/repos/asf/calcite/blob/707f4de9/geode/src/test/resources/model-zips.json
----------------------------------------------------------------------
diff --git a/geode/src/test/resources/model-zips.json b/geode/src/test/resources/model-zips.json
new file mode 100644
index 0000000..4077768
--- /dev/null
+++ b/geode/src/test/resources/model-zips.json
@@ -0,0 +1,47 @@
+/*
+ * 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.
+ *
+ * A JSON model of a simple Calcite schema.
+ */
+{
+  "version": "1.0",
+  "defaultSchema": "geode_raw",
+  "schemas": [
+    {
+      "name": "geode_raw",
+      "type": "custom",
+      "factory": "org.apache.calcite.adapter.geode.rel.GeodeSchemaFactory",
+      "operand": {
+        "locatorHost": "localhost",
+        "locatorPort": "10334",
+        "regions": "Zips",
+        "pdxSerializablePackagePath": ".*"
+      }
+    },
+    {
+      "name": "geode",
+      "tables": [
+        {
+          "name": "ZIPS",
+          "type": "view",
+          "sql": [
+            "select \"_id\" AS \"id\", \"city\", \"loc\", cast(\"pop\" AS integer) AS \"pop\", cast(\"state\" AS varchar(2)) AS \"state\" from \"geode_raw\".\"Zips\""
+          ]
+        }
+      ]
+    }
+  ]
+}

http://git-wip-us.apache.org/repos/asf/calcite/blob/707f4de9/geode/src/test/resources/model.json
----------------------------------------------------------------------
diff --git a/geode/src/test/resources/model.json b/geode/src/test/resources/model.json
new file mode 100644
index 0000000..c1a2c86
--- /dev/null
+++ b/geode/src/test/resources/model.json
@@ -0,0 +1,35 @@
+/*
+ * 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.
+ *
+ * A JSON model of a simple Calcite schema.
+ */
+{
+  "version": "1.0",
+  "defaultSchema": "TEST",
+  "schemas": [
+    {
+      "name": "TEST",
+      "type": "custom",
+      "factory": "org.apache.calcite.adapter.geode.simple.GeodeSchemaFactory",
+      "operand": {
+        "locatorHost": "localhost",
+        "locatorPort": "10334",
+        "regions": "BookMaster",
+        "pdxSerializablePackagePath": "net.tzolov.geode.bookstore.domain.*"
+      }
+    }
+  ]
+}

http://git-wip-us.apache.org/repos/asf/calcite/blob/707f4de9/geode/src/test/resources/model2.json
----------------------------------------------------------------------
diff --git a/geode/src/test/resources/model2.json b/geode/src/test/resources/model2.json
new file mode 100644
index 0000000..4cbe328
--- /dev/null
+++ b/geode/src/test/resources/model2.json
@@ -0,0 +1,35 @@
+/*
+ * 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.
+ *
+ * A JSON model of a simple Calcite schema.
+ */
+{
+  "version": "1.0",
+  "defaultSchema": "TEST",
+  "schemas": [
+    {
+      "name": "TEST",
+      "type": "custom",
+      "factory": "org.apache.calcite.adapter.geode.simple.GeodeSchemaFactory",
+      "operand": {
+        "locatorHost": "localhost",
+        "locatorPort": "10334",
+        "regions": "BookMaster,Customer,InventoryItem,BookOrder",
+        "pdxSerializablePackagePath": "net.tzolov.geode.bookstore.domain.*"
+      }
+    }
+  ]
+}

http://git-wip-us.apache.org/repos/asf/calcite/blob/707f4de9/pom.xml
----------------------------------------------------------------------
diff --git a/pom.xml b/pom.xml
index 62d4871..b1c81b8 100644
--- a/pom.xml
+++ b/pom.xml
@@ -75,6 +75,7 @@ limitations under the License.
     <forbiddenapis.version>2.3</forbiddenapis.version>
     <freemarker.version>2.3.25-incubating</freemarker.version>
     <git-commit-id-plugin.version>2.1.9</git-commit-id-plugin.version>
+    <geode.version>1.3.0</geode.version>
 
     <!-- We support (and test against) Guava versions between
          14.0.1 (Hive) and 23.0. Default is 19.0 due to Cassandra adapter.
@@ -152,6 +153,7 @@ limitations under the License.
     <module>elasticsearch5</module>
     <module>example</module>
     <module>file</module>
+    <module>geode</module>
     <module>linq4j</module>
     <module>mongodb</module>
     <module>pig</module>
@@ -340,6 +342,11 @@ limitations under the License.
         <version>${commons-lang3.version}</version>
       </dependency>
       <dependency>
+        <groupId>org.apache.geode</groupId>
+        <artifactId>geode-core</artifactId>
+        <version>${geode.version}</version>
+      </dependency>
+      <dependency>
         <groupId>org.apache.hadoop</groupId>
         <artifactId>hadoop-common</artifactId>
         <version>${hadoop.version}</version>


[2/7] calcite git commit: In JdbcTest, ensure ResultSet is closed

Posted by jh...@apache.org.
In JdbcTest, ensure ResultSet is closed


Project: http://git-wip-us.apache.org/repos/asf/calcite/repo
Commit: http://git-wip-us.apache.org/repos/asf/calcite/commit/fd68f251
Tree: http://git-wip-us.apache.org/repos/asf/calcite/tree/fd68f251
Diff: http://git-wip-us.apache.org/repos/asf/calcite/diff/fd68f251

Branch: refs/heads/master
Commit: fd68f25135b7f07f724116c715c070cb3990e778
Parents: facd83d
Author: Julian Hyde <jh...@apache.org>
Authored: Sun Dec 24 13:05:59 2017 -0800
Committer: Julian Hyde <jh...@apache.org>
Committed: Fri Feb 16 10:18:01 2018 -0800

----------------------------------------------------------------------
 .../java/org/apache/calcite/test/JdbcTest.java  | 103 ++++++++++---------
 1 file changed, 54 insertions(+), 49 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/calcite/blob/fd68f251/core/src/test/java/org/apache/calcite/test/JdbcTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/calcite/test/JdbcTest.java b/core/src/test/java/org/apache/calcite/test/JdbcTest.java
index b8bffce..ec5118f 100644
--- a/core/src/test/java/org/apache/calcite/test/JdbcTest.java
+++ b/core/src/test/java/org/apache/calcite/test/JdbcTest.java
@@ -5365,70 +5365,75 @@ public class JdbcTest {
               final DatabaseMetaData metaData = a0.getMetaData();
 
               // all table types
-              assertEquals(
-                  "TABLE_CAT=null; TABLE_SCHEM=adhoc; TABLE_NAME=EMPLOYEES; TABLE_TYPE=TABLE; REMARKS=null; TYPE_CAT=null; TYPE_SCHEM=null; TYPE_NAME=null; SELF_REFERENCING_COL_NAME=null; REF_GENERATION=null\n"
-                      + "TABLE_CAT=null; TABLE_SCHEM=adhoc; TABLE_NAME=MUTABLE_EMPLOYEES; TABLE_TYPE=TABLE; REMARKS=null; TYPE_CAT=null; TYPE_SCHEM=null; TYPE_NAME=null; SELF_REFERENCING_COL_NAME=null; REF_GENERATION=null\n"
-                      + "TABLE_CAT=null; TABLE_SCHEM=adhoc; TABLE_NAME=V; TABLE_TYPE=VIEW; REMARKS=null; TYPE_CAT=null; TYPE_SCHEM=null; TYPE_NAME=null; SELF_REFERENCING_COL_NAME=null; REF_GENERATION=null\n",
-                  CalciteAssert.toString(
-                      metaData.getTables(null, "adhoc", null, null)));
+              try (ResultSet r =
+                   metaData.getTables(null, "adhoc", null, null)) {
+                assertEquals(
+                    "TABLE_CAT=null; TABLE_SCHEM=adhoc; TABLE_NAME=EMPLOYEES; TABLE_TYPE=TABLE; REMARKS=null; TYPE_CAT=null; TYPE_SCHEM=null; TYPE_NAME=null; SELF_REFERENCING_COL_NAME=null; REF_GENERATION=null\n"
+                        + "TABLE_CAT=null; TABLE_SCHEM=adhoc; TABLE_NAME=MUTABLE_EMPLOYEES; TABLE_TYPE=TABLE; REMARKS=null; TYPE_CAT=null; TYPE_SCHEM=null; TYPE_NAME=null; SELF_REFERENCING_COL_NAME=null; REF_GENERATION=null\n"
+                        + "TABLE_CAT=null; TABLE_SCHEM=adhoc; TABLE_NAME=V; TABLE_TYPE=VIEW; REMARKS=null; TYPE_CAT=null; TYPE_SCHEM=null; TYPE_NAME=null; SELF_REFERENCING_COL_NAME=null; REF_GENERATION=null\n",
+                    CalciteAssert.toString(r));
+              }
 
               // including system tables; note that table type is "SYSTEM TABLE"
               // not "SYSTEM_TABLE"
-              assertEquals(
-                  "TABLE_CAT=null; TABLE_SCHEM=adhoc; TABLE_NAME=EMPLOYEES; TABLE_TYPE=TABLE; REMARKS=null; TYPE_CAT=null; TYPE_SCHEM=null; TYPE_NAME=null; SELF_REFERENCING_COL_NAME=null; REF_GENERATION=null\n"
-                      + "TABLE_CAT=null; TABLE_SCHEM=adhoc; TABLE_NAME=MUTABLE_EMPLOYEES; TABLE_TYPE=TABLE; REMARKS=null; TYPE_CAT=null; TYPE_SCHEM=null; TYPE_NAME=null; SELF_REFERENCING_COL_NAME=null; REF_GENERATION=null\n"
-                      + "TABLE_CAT=null; TABLE_SCHEM=adhoc; TABLE_NAME=V; TABLE_TYPE=VIEW; REMARKS=null; TYPE_CAT=null; TYPE_SCHEM=null; TYPE_NAME=null; SELF_REFERENCING_COL_NAME=null; REF_GENERATION=null\n"
-                      + "TABLE_CAT=null; TABLE_SCHEM=metadata; TABLE_NAME=COLUMNS; TABLE_TYPE=SYSTEM TABLE; REMARKS=null; TYPE_CAT=null; TYPE_SCHEM=null; TYPE_NAME=null; SELF_REFERENCING_COL_NAME=null; REF_GENERATION=null\n"
-                      + "TABLE_CAT=null; TABLE_SCHEM=metadata; TABLE_NAME=TABLES; TABLE_TYPE=SYSTEM TABLE; REMARKS=null; TYPE_CAT=null; TYPE_SCHEM=null; TYPE_NAME=null; SELF_REFERENCING_COL_NAME=null; REF_GENERATION=null\n",
-                  CalciteAssert.toString(
-                      metaData.getTables(null, null, null, null)));
+              try (ResultSet r = metaData.getTables(null, null, null, null)) {
+                assertEquals(
+                    "TABLE_CAT=null; TABLE_SCHEM=adhoc; TABLE_NAME=EMPLOYEES; TABLE_TYPE=TABLE; REMARKS=null; TYPE_CAT=null; TYPE_SCHEM=null; TYPE_NAME=null; SELF_REFERENCING_COL_NAME=null; REF_GENERATION=null\n"
+                        + "TABLE_CAT=null; TABLE_SCHEM=adhoc; TABLE_NAME=MUTABLE_EMPLOYEES; TABLE_TYPE=TABLE; REMARKS=null; TYPE_CAT=null; TYPE_SCHEM=null; TYPE_NAME=null; SELF_REFERENCING_COL_NAME=null; REF_GENERATION=null\n"
+                        + "TABLE_CAT=null; TABLE_SCHEM=adhoc; TABLE_NAME=V; TABLE_TYPE=VIEW; REMARKS=null; TYPE_CAT=null; TYPE_SCHEM=null; TYPE_NAME=null; SELF_REFERENCING_COL_NAME=null; REF_GENERATION=null\n"
+                        + "TABLE_CAT=null; TABLE_SCHEM=metadata; TABLE_NAME=COLUMNS; TABLE_TYPE=SYSTEM TABLE; REMARKS=null; TYPE_CAT=null; TYPE_SCHEM=null; TYPE_NAME=null; SELF_REFERENCING_COL_NAME=null; REF_GENERATION=null\n"
+                        + "TABLE_CAT=null; TABLE_SCHEM=metadata; TABLE_NAME=TABLES; TABLE_TYPE=SYSTEM TABLE; REMARKS=null; TYPE_CAT=null; TYPE_SCHEM=null; TYPE_NAME=null; SELF_REFERENCING_COL_NAME=null; REF_GENERATION=null\n",
+                    CalciteAssert.toString(r));
+              }
 
               // views only
-              assertEquals(
-                  "TABLE_CAT=null; TABLE_SCHEM=adhoc; TABLE_NAME=V; TABLE_TYPE=VIEW; REMARKS=null; TYPE_CAT=null; TYPE_SCHEM=null; TYPE_NAME=null; SELF_REFERENCING_COL_NAME=null; REF_GENERATION=null\n",
-                  CalciteAssert.toString(
-                      metaData.getTables(
-                          null, "adhoc", null,
-                          new String[]{
-                              Schema.TableType.VIEW.jdbcName
-                          })));
+              try (ResultSet r = metaData.getTables(null, "adhoc", null,
+                  new String[]{Schema.TableType.VIEW.jdbcName})) {
+                assertEquals(
+                    "TABLE_CAT=null; TABLE_SCHEM=adhoc; TABLE_NAME=V; TABLE_TYPE=VIEW; REMARKS=null; TYPE_CAT=null; TYPE_SCHEM=null; TYPE_NAME=null; SELF_REFERENCING_COL_NAME=null; REF_GENERATION=null\n",
+                    CalciteAssert.toString(r));
+              }
 
               // columns
-              assertEquals(
-                  "TABLE_CAT=null; TABLE_SCHEM=adhoc; TABLE_NAME=V; COLUMN_NAME=empid; DATA_TYPE=4; TYPE_NAME=JavaType(int) NOT NULL; COLUMN_SIZE=-1; BUFFER_LENGTH=null; DECIMAL_DIGITS=null; NUM_PREC_RADIX=10; NULLABLE=0; REMARKS=null; COLUMN_DEF=null; SQL_DATA_TYPE=null; SQL_DATETIME_SUB=null; CHAR_OCTET_LENGTH=-1; ORDINAL_POSITION=1; IS_NULLABLE=NO; SCOPE_CATALOG=null; SCOPE_SCHEMA=null; SCOPE_TABLE=null; SOURCE_DATA_TYPE=null; IS_AUTOINCREMENT=; IS_GENERATEDCOLUMN=\n"
-                      + "TABLE_CAT=null; TABLE_SCHEM=adhoc; TABLE_NAME=V; COLUMN_NAME=deptno; DATA_TYPE=4; TYPE_NAME=JavaType(int) NOT NULL; COLUMN_SIZE=-1; BUFFER_LENGTH=null; DECIMAL_DIGITS=null; NUM_PREC_RADIX=10; NULLABLE=0; REMARKS=null; COLUMN_DEF=null; SQL_DATA_TYPE=null; SQL_DATETIME_SUB=null; CHAR_OCTET_LENGTH=-1; ORDINAL_POSITION=2; IS_NULLABLE=NO; SCOPE_CATALOG=null; SCOPE_SCHEMA=null; SCOPE_TABLE=null; SOURCE_DATA_TYPE=null; IS_AUTOINCREMENT=; IS_GENERATEDCOLUMN=\n"
-                      + "TABLE_CAT=null; TABLE_SCHEM=adhoc; TABLE_NAME=V; COLUMN_NAME=name; DATA_TYPE=12; TYPE_NAME=JavaType(class java.lang.String); COLUMN_SIZE=-1; BUFFER_LENGTH=null; DECIMAL_DIGITS=null; NUM_PREC_RADIX=10; NULLABLE=1; REMARKS=null; COLUMN_DEF=null; SQL_DATA_TYPE=null; SQL_DATETIME_SUB=null; CHAR_OCTET_LENGTH=-1; ORDINAL_POSITION=3; IS_NULLABLE=YES; SCOPE_CATALOG=null; SCOPE_SCHEMA=null; SCOPE_TABLE=null; SOURCE_DATA_TYPE=null; IS_AUTOINCREMENT=; IS_GENERATEDCOLUMN=\n"
-                      + "TABLE_CAT=null; TABLE_SCHEM=adhoc; TABLE_NAME=V; COLUMN_NAME=salary; DATA_TYPE=7; TYPE_NAME=JavaType(float) NOT NULL; COLUMN_SIZE=-1; BUFFER_LENGTH=null; DECIMAL_DIGITS=null; NUM_PREC_RADIX=10; NULLABLE=0; REMARKS=null; COLUMN_DEF=null; SQL_DATA_TYPE=null; SQL_DATETIME_SUB=null; CHAR_OCTET_LENGTH=-1; ORDINAL_POSITION=4; IS_NULLABLE=NO; SCOPE_CATALOG=null; SCOPE_SCHEMA=null; SCOPE_TABLE=null; SOURCE_DATA_TYPE=null; IS_AUTOINCREMENT=; IS_GENERATEDCOLUMN=\n"
-                      + "TABLE_CAT=null; TABLE_SCHEM=adhoc; TABLE_NAME=V; COLUMN_NAME=commission; DATA_TYPE=4; TYPE_NAME=JavaType(class java.lang.Integer); COLUMN_SIZE=-1; BUFFER_LENGTH=null; DECIMAL_DIGITS=null; NUM_PREC_RADIX=10; NULLABLE=1; REMARKS=null; COLUMN_DEF=null; SQL_DATA_TYPE=null; SQL_DATETIME_SUB=null; CHAR_OCTET_LENGTH=-1; ORDINAL_POSITION=5; IS_NULLABLE=YES; SCOPE_CATALOG=null; SCOPE_SCHEMA=null; SCOPE_TABLE=null; SOURCE_DATA_TYPE=null; IS_AUTOINCREMENT=; IS_GENERATEDCOLUMN=\n",
-                  CalciteAssert.toString(
-                      metaData.getColumns(
-                          null, "adhoc", "V", null)));
+              try (ResultSet r =
+                       metaData.getColumns(null, "adhoc", "V", null)) {
+                assertEquals(
+                    "TABLE_CAT=null; TABLE_SCHEM=adhoc; TABLE_NAME=V; COLUMN_NAME=empid; DATA_TYPE=4; TYPE_NAME=JavaType(int) NOT NULL; COLUMN_SIZE=-1; BUFFER_LENGTH=null; DECIMAL_DIGITS=null; NUM_PREC_RADIX=10; NULLABLE=0; REMARKS=null; COLUMN_DEF=null; SQL_DATA_TYPE=null; SQL_DATETIME_SUB=null; CHAR_OCTET_LENGTH=-1; ORDINAL_POSITION=1; IS_NULLABLE=NO; SCOPE_CATALOG=null; SCOPE_SCHEMA=null; SCOPE_TABLE=null; SOURCE_DATA_TYPE=null; IS_AUTOINCREMENT=; IS_GENERATEDCOLUMN=\n"
+                        + "TABLE_CAT=null; TABLE_SCHEM=adhoc; TABLE_NAME=V; COLUMN_NAME=deptno; DATA_TYPE=4; TYPE_NAME=JavaType(int) NOT NULL; COLUMN_SIZE=-1; BUFFER_LENGTH=null; DECIMAL_DIGITS=null; NUM_PREC_RADIX=10; NULLABLE=0; REMARKS=null; COLUMN_DEF=null; SQL_DATA_TYPE=null; SQL_DATETIME_SUB=null; CHAR_OCTET_LENGTH=-1; ORDINAL_POSITION=2; IS_NULLABLE=NO; SCOPE_CATALOG=null; SCOPE_SCHEMA=null; SCOPE_TABLE=null; SOURCE_DATA_TYPE=null; IS_AUTOINCREMENT=; IS_GENERATEDCOLUMN=\n"
+                        + "TABLE_CAT=null; TABLE_SCHEM=adhoc; TABLE_NAME=V; COLUMN_NAME=name; DATA_TYPE=12; TYPE_NAME=JavaType(class java.lang.String); COLUMN_SIZE=-1; BUFFER_LENGTH=null; DECIMAL_DIGITS=null; NUM_PREC_RADIX=10; NULLABLE=1; REMARKS=null; COLUMN_DEF=null; SQL_DATA_TYPE=null; SQL_DATETIME_SUB=null; CHAR_OCTET_LENGTH=-1; ORDINAL_POSITION=3; IS_NULLABLE=YES; SCOPE_CATALOG=null; SCOPE_SCHEMA=null; SCOPE_TABLE=null; SOURCE_DATA_TYPE=null; IS_AUTOINCREMENT=; IS_GENERATEDCOLUMN=\n"
+                        + "TABLE_CAT=null; TABLE_SCHEM=adhoc; TABLE_NAME=V; COLUMN_NAME=salary; DATA_TYPE=7; TYPE_NAME=JavaType(float) NOT NULL; COLUMN_SIZE=-1; BUFFER_LENGTH=null; DECIMAL_DIGITS=null; NUM_PREC_RADIX=10; NULLABLE=0; REMARKS=null; COLUMN_DEF=null; SQL_DATA_TYPE=null; SQL_DATETIME_SUB=null; CHAR_OCTET_LENGTH=-1; ORDINAL_POSITION=4; IS_NULLABLE=NO; SCOPE_CATALOG=null; SCOPE_SCHEMA=null; SCOPE_TABLE=null; SOURCE_DATA_TYPE=null; IS_AUTOINCREMENT=; IS_GENERATEDCOLUMN=\n"
+                        + "TABLE_CAT=null; TABLE_SCHEM=adhoc; TABLE_NAME=V; COLUMN_NAME=commission; DATA_TYPE=4; TYPE_NAME=JavaType(class java.lang.Integer); COLUMN_SIZE=-1; BUFFER_LENGTH=null; DECIMAL_DIGITS=null; NUM_PREC_RADIX=10; NULLABLE=1; REMARKS=null; COLUMN_DEF=null; SQL_DATA_TYPE=null; SQL_DATETIME_SUB=null; CHAR_OCTET_LENGTH=-1; ORDINAL_POSITION=5; IS_NULLABLE=YES; SCOPE_CATALOG=null; SCOPE_SCHEMA=null; SCOPE_TABLE=null; SOURCE_DATA_TYPE=null; IS_AUTOINCREMENT=; IS_GENERATEDCOLUMN=\n",
+                    CalciteAssert.toString(r));
+              }
 
               // catalog
-              assertEquals(
-                  "TABLE_CAT=null\n",
-                  CalciteAssert.toString(
-                      metaData.getCatalogs()));
+              try (ResultSet r = metaData.getCatalogs()) {
+                assertEquals(
+                    "TABLE_CAT=null\n",
+                    CalciteAssert.toString(r));
+              }
 
               // schemas
-              assertEquals(
-                  "TABLE_SCHEM=adhoc; TABLE_CATALOG=null\n"
-                      + "TABLE_SCHEM=metadata; TABLE_CATALOG=null\n",
-                  CalciteAssert.toString(
-                      metaData.getSchemas()));
+              try (ResultSet r = metaData.getSchemas()) {
+                assertEquals(
+                    "TABLE_SCHEM=adhoc; TABLE_CATALOG=null\n"
+                        + "TABLE_SCHEM=metadata; TABLE_CATALOG=null\n",
+                    CalciteAssert.toString(r));
+              }
 
               // schemas (qualified)
-              assertEquals(
-                  "TABLE_SCHEM=adhoc; TABLE_CATALOG=null\n",
-                  CalciteAssert.toString(
-                      metaData.getSchemas(null, "adhoc")));
+              try (ResultSet r = metaData.getSchemas(null, "adhoc")) {
+                assertEquals(
+                    "TABLE_SCHEM=adhoc; TABLE_CATALOG=null\n",
+                    CalciteAssert.toString(r));
+              }
 
               // table types
-              assertEquals(
-                  "TABLE_TYPE=TABLE\n"
-                      + "TABLE_TYPE=VIEW\n",
-                  CalciteAssert.toString(
-                      metaData.getTableTypes()));
+              try (ResultSet r = metaData.getTableTypes()) {
+                assertEquals("TABLE_TYPE=TABLE\n"
+                        + "TABLE_TYPE=VIEW\n",
+                    CalciteAssert.toString(r));
+              }
 
               return null;
             } catch (SQLException e) {


[3/7] calcite git commit: [CALCITE-2128] Add SQL dialect for Jethro Data (Jonathan Doron)

Posted by jh...@apache.org.
[CALCITE-2128] Add SQL dialect for Jethro Data (Jonathan Doron)

Before creating a JethroDataSqlDialect, SqlDialectFactoryImpl issues a
"show functions extended" command to Jethro, storing the results in a
cache for next time, and passes the list of supported functions to the
dialect.

Close apache/calcite#602


Project: http://git-wip-us.apache.org/repos/asf/calcite/repo
Commit: http://git-wip-us.apache.org/repos/asf/calcite/commit/facd83d3
Tree: http://git-wip-us.apache.org/repos/asf/calcite/tree/facd83d3
Diff: http://git-wip-us.apache.org/repos/asf/calcite/diff/facd83d3

Branch: refs/heads/master
Commit: facd83d311098dd3f35294ba969a19cbf6d54467
Parents: 0ced3b7
Author: msydoron <ms...@gmail.com>
Authored: Mon Dec 18 15:30:55 2017 +0200
Committer: Julian Hyde <jh...@apache.org>
Committed: Fri Feb 16 10:18:01 2018 -0800

----------------------------------------------------------------------
 .../apache/calcite/adapter/jdbc/JdbcRules.java  | 148 +++++++-----
 .../apache/calcite/adapter/jdbc/JdbcSchema.java |   5 +-
 .../apache/calcite/adapter/jdbc/JdbcTable.java  |   2 +-
 .../calcite/adapter/jdbc/JdbcTableScan.java     |   2 +-
 .../rules/AbstractJoinExtractFilterRule.java    |  85 +++++++
 .../rel/rules/JoinExtractFilterRule.java        |  49 +---
 .../java/org/apache/calcite/sql/SqlDialect.java |  43 +++-
 .../apache/calcite/sql/SqlDialectFactory.java   |   4 +-
 .../calcite/sql/SqlDialectFactoryImpl.java      |  21 ++
 .../sql/dialect/JethroDataSqlDialect.java       | 235 +++++++++++++++++++
 .../rel/rel2sql/RelToSqlConverterTest.java      |  24 ++
 11 files changed, 490 insertions(+), 128 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/calcite/blob/facd83d3/core/src/main/java/org/apache/calcite/adapter/jdbc/JdbcRules.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/adapter/jdbc/JdbcRules.java b/core/src/main/java/org/apache/calcite/adapter/jdbc/JdbcRules.java
index 86b44af..44f9c09 100644
--- a/core/src/main/java/org/apache/calcite/adapter/jdbc/JdbcRules.java
+++ b/core/src/main/java/org/apache/calcite/adapter/jdbc/JdbcRules.java
@@ -35,6 +35,7 @@ import org.apache.calcite.rel.SingleRel;
 import org.apache.calcite.rel.convert.ConverterRule;
 import org.apache.calcite.rel.core.Aggregate;
 import org.apache.calcite.rel.core.AggregateCall;
+import org.apache.calcite.rel.core.Calc;
 import org.apache.calcite.rel.core.CorrelationId;
 import org.apache.calcite.rel.core.Filter;
 import org.apache.calcite.rel.core.Intersect;
@@ -47,16 +48,6 @@ import org.apache.calcite.rel.core.Sort;
 import org.apache.calcite.rel.core.TableModify;
 import org.apache.calcite.rel.core.Union;
 import org.apache.calcite.rel.core.Values;
-import org.apache.calcite.rel.logical.LogicalAggregate;
-import org.apache.calcite.rel.logical.LogicalCalc;
-import org.apache.calcite.rel.logical.LogicalFilter;
-import org.apache.calcite.rel.logical.LogicalIntersect;
-import org.apache.calcite.rel.logical.LogicalJoin;
-import org.apache.calcite.rel.logical.LogicalMinus;
-import org.apache.calcite.rel.logical.LogicalProject;
-import org.apache.calcite.rel.logical.LogicalTableModify;
-import org.apache.calcite.rel.logical.LogicalUnion;
-import org.apache.calcite.rel.logical.LogicalValues;
 import org.apache.calcite.rel.metadata.RelMdUtil;
 import org.apache.calcite.rel.metadata.RelMetadataQuery;
 import org.apache.calcite.rel.rel2sql.SqlImplementor;
@@ -121,23 +112,35 @@ public class JdbcRules {
   }
 
   /** Rule that converts a join to JDBC. */
-  private static class JdbcJoinRule extends JdbcConverterRule {
-    private JdbcJoinRule(JdbcConvention out) {
-      super(LogicalJoin.class, Convention.NONE, out, "JdbcJoinRule");
+  public static class JdbcJoinRule extends JdbcConverterRule {
+    /** Creates a JdbcJoinRule. */
+    public JdbcJoinRule(JdbcConvention out) {
+      super(Join.class, Convention.NONE, out, "JdbcJoinRule");
     }
 
     @Override public RelNode convert(RelNode rel) {
-      LogicalJoin join = (LogicalJoin) rel;
+      return convert((Join) rel, true);
+    }
+
+    /**
+     * Converts a {@code Join} into a {@code JdbcJoin}.
+     *
+     * @param join Join operator to convert
+     * @param convertInputTraits Whether to convert input to {@code join}'s
+     *                            JDBC convention
+     * @return A new JdbcJoin
+     */
+    public RelNode convert(Join join, boolean convertInputTraits) {
       final List<RelNode> newInputs = new ArrayList<>();
       for (RelNode input : join.getInputs()) {
-        if (!(input.getConvention() == getOutTrait())) {
+        if (convertInputTraits && input.getConvention() != getOutTrait()) {
           input =
               convert(input,
                   input.getTraitSet().replace(out));
         }
         newInputs.add(input);
       }
-      if (!canJoinOnCondition(join.getCondition())) {
+      if (convertInputTraits && !canJoinOnCondition(join.getCondition())) {
         return null;
       }
       try {
@@ -200,7 +203,7 @@ public class JdbcRules {
   /** Join operator implemented in JDBC convention. */
   public static class JdbcJoin extends Join implements JdbcRel {
     /** Creates a JdbcJoin. */
-    protected JdbcJoin(RelOptCluster cluster, RelTraitSet traitSet,
+    public JdbcJoin(RelOptCluster cluster, RelTraitSet traitSet,
         RelNode left, RelNode right, RexNode condition,
         Set<CorrelationId> variablesSet, JoinRelType joinType)
         throws InvalidRelException {
@@ -254,16 +257,16 @@ public class JdbcRules {
   }
 
   /**
-   * Rule to convert a {@link org.apache.calcite.rel.logical.LogicalCalc} to an
+   * Rule to convert a {@link org.apache.calcite.rel.core.Calc} to an
    * {@link org.apache.calcite.adapter.jdbc.JdbcRules.JdbcCalc}.
    */
   private static class JdbcCalcRule extends JdbcConverterRule {
     private JdbcCalcRule(JdbcConvention out) {
-      super(LogicalCalc.class, Convention.NONE, out, "JdbcCalcRule");
+      super(Calc.class, Convention.NONE, out, "JdbcCalcRule");
     }
 
     public RelNode convert(RelNode rel) {
-      final LogicalCalc calc = (LogicalCalc) rel;
+      final Calc calc = (Calc) rel;
 
       // If there's a multiset, let FarragoMultisetSplitter work on it
       // first.
@@ -327,16 +330,16 @@ public class JdbcRules {
   }
 
   /**
-   * Rule to convert a {@link org.apache.calcite.rel.logical.LogicalProject} to
+   * Rule to convert a {@link org.apache.calcite.rel.core.Project} to
    * an {@link org.apache.calcite.adapter.jdbc.JdbcRules.JdbcProject}.
    */
-  private static class JdbcProjectRule extends JdbcConverterRule {
-    private JdbcProjectRule(JdbcConvention out) {
-      super(LogicalProject.class, Convention.NONE, out, "JdbcProjectRule");
+  public static class JdbcProjectRule extends JdbcConverterRule {
+    public JdbcProjectRule(JdbcConvention out) {
+      super(Project.class, Convention.NONE, out, "JdbcProjectRule");
     }
 
     public RelNode convert(RelNode rel) {
-      final LogicalProject project = (LogicalProject) rel;
+      final Project project = (Project) rel;
 
       return new JdbcProject(
           rel.getCluster(),
@@ -349,7 +352,7 @@ public class JdbcRules {
     }
   }
 
-  /** Implementation of {@link org.apache.calcite.rel.logical.LogicalProject} in
+  /** Implementation of {@link org.apache.calcite.rel.core.Project} in
    * {@link JdbcConvention jdbc calling convention}. */
   public static class JdbcProject
       extends Project
@@ -388,16 +391,16 @@ public class JdbcRules {
   }
 
   /**
-   * Rule to convert a {@link org.apache.calcite.rel.logical.LogicalFilter} to
+   * Rule to convert a {@link org.apache.calcite.rel.core.Filter} to
    * an {@link org.apache.calcite.adapter.jdbc.JdbcRules.JdbcFilter}.
    */
-  private static class JdbcFilterRule extends JdbcConverterRule {
-    private JdbcFilterRule(JdbcConvention out) {
-      super(LogicalFilter.class, Convention.NONE, out, "JdbcFilterRule");
+  public static class JdbcFilterRule extends JdbcConverterRule {
+    public JdbcFilterRule(JdbcConvention out) {
+      super(Filter.class, Convention.NONE, out, "JdbcFilterRule");
     }
 
     public RelNode convert(RelNode rel) {
-      final LogicalFilter filter = (LogicalFilter) rel;
+      final Filter filter = (Filter) rel;
 
       return new JdbcFilter(
           rel.getCluster(),
@@ -431,16 +434,16 @@ public class JdbcRules {
   }
 
   /**
-   * Rule to convert a {@link org.apache.calcite.rel.logical.LogicalAggregate}
+   * Rule to convert a {@link org.apache.calcite.rel.core.Aggregate}
    * to a {@link org.apache.calcite.adapter.jdbc.JdbcRules.JdbcAggregate}.
    */
-  private static class JdbcAggregateRule extends JdbcConverterRule {
-    private JdbcAggregateRule(JdbcConvention out) {
-      super(LogicalAggregate.class, Convention.NONE, out, "JdbcAggregateRule");
+  public static class JdbcAggregateRule extends JdbcConverterRule {
+    public JdbcAggregateRule(JdbcConvention out) {
+      super(Aggregate.class, Convention.NONE, out, "JdbcAggregateRule");
     }
 
     public RelNode convert(RelNode rel) {
-      final LogicalAggregate agg = (LogicalAggregate) rel;
+      final Aggregate agg = (Aggregate) rel;
       if (agg.getGroupSets().size() != 1) {
         // GROUPING SETS not supported; see
         // [CALCITE-734] Push GROUPING SETS to underlying SQL via JDBC adapter
@@ -511,17 +514,36 @@ public class JdbcRules {
    * Rule to convert a {@link org.apache.calcite.rel.core.Sort} to an
    * {@link org.apache.calcite.adapter.jdbc.JdbcRules.JdbcSort}.
    */
-  private static class JdbcSortRule extends JdbcConverterRule {
-    private JdbcSortRule(JdbcConvention out) {
+  public static class JdbcSortRule extends JdbcConverterRule {
+    /** Creates a JdbcSortRule. */
+    public JdbcSortRule(JdbcConvention out) {
       super(Sort.class, Convention.NONE, out, "JdbcSortRule");
     }
 
     public RelNode convert(RelNode rel) {
-      final Sort sort = (Sort) rel;
+      return convert((Sort) rel, true);
+    }
+
+    /**
+     * Converts a {@code Sort} into a {@code JdbcSort}.
+     *
+     * @param sort Sort operator to convert
+     * @param convertInputTraits Whether to convert input to {@code sort}'s
+     *                            JDBC convention
+     * @return A new JdbcSort
+     */
+    public RelNode convert(Sort sort, boolean convertInputTraits) {
       final RelTraitSet traitSet = sort.getTraitSet().replace(out);
-      return new JdbcSort(rel.getCluster(), traitSet,
-          convert(sort.getInput(), traitSet), sort.getCollation(), sort.offset,
-          sort.fetch);
+
+      final RelNode input;
+      if (convertInputTraits) {
+        input = convert(sort.getInput(), traitSet);
+      } else {
+        input = sort.getInput();
+      }
+
+      return new JdbcSort(sort.getCluster(), traitSet,
+          input, sort.getCollation(), sort.offset, sort.fetch);
     }
   }
 
@@ -553,16 +575,16 @@ public class JdbcRules {
   }
 
   /**
-   * Rule to convert an {@link org.apache.calcite.rel.logical.LogicalUnion} to a
+   * Rule to convert an {@link org.apache.calcite.rel.core.Union} to a
    * {@link org.apache.calcite.adapter.jdbc.JdbcRules.JdbcUnion}.
    */
-  private static class JdbcUnionRule extends JdbcConverterRule {
-    private JdbcUnionRule(JdbcConvention out) {
-      super(LogicalUnion.class, Convention.NONE, out, "JdbcUnionRule");
+  public static class JdbcUnionRule extends JdbcConverterRule {
+    public JdbcUnionRule(JdbcConvention out) {
+      super(Union.class, Convention.NONE, out, "JdbcUnionRule");
     }
 
     public RelNode convert(RelNode rel) {
-      final LogicalUnion union = (LogicalUnion) rel;
+      final Union union = (Union) rel;
       final RelTraitSet traitSet =
           union.getTraitSet().replace(out);
       return new JdbcUnion(rel.getCluster(), traitSet,
@@ -596,16 +618,16 @@ public class JdbcRules {
   }
 
   /**
-   * Rule to convert a {@link org.apache.calcite.rel.logical.LogicalIntersect}
+   * Rule to convert a {@link org.apache.calcite.rel.core.Intersect}
    * to a {@link org.apache.calcite.adapter.jdbc.JdbcRules.JdbcIntersect}.
    */
-  private static class JdbcIntersectRule extends JdbcConverterRule {
+  public static class JdbcIntersectRule extends JdbcConverterRule {
     private JdbcIntersectRule(JdbcConvention out) {
-      super(LogicalIntersect.class, Convention.NONE, out, "JdbcIntersectRule");
+      super(Intersect.class, Convention.NONE, out, "JdbcIntersectRule");
     }
 
     public RelNode convert(RelNode rel) {
-      final LogicalIntersect intersect = (LogicalIntersect) rel;
+      final Intersect intersect = (Intersect) rel;
       if (intersect.all) {
         return null; // INTERSECT ALL not implemented
       }
@@ -640,16 +662,16 @@ public class JdbcRules {
   }
 
   /**
-   * Rule to convert a {@link org.apache.calcite.rel.logical.LogicalMinus} to a
+   * Rule to convert a {@link org.apache.calcite.rel.core.Minus} to a
    * {@link org.apache.calcite.adapter.jdbc.JdbcRules.JdbcMinus}.
    */
-  private static class JdbcMinusRule extends JdbcConverterRule {
+  public static class JdbcMinusRule extends JdbcConverterRule {
     private JdbcMinusRule(JdbcConvention out) {
-      super(LogicalMinus.class, Convention.NONE, out, "JdbcMinusRule");
+      super(Minus.class, Convention.NONE, out, "JdbcMinusRule");
     }
 
     public RelNode convert(RelNode rel) {
-      final LogicalMinus minus = (LogicalMinus) rel;
+      final Minus minus = (Minus) rel;
       if (minus.all) {
         return null; // EXCEPT ALL not implemented
       }
@@ -681,20 +703,16 @@ public class JdbcRules {
   /** Rule that converts a table-modification to JDBC. */
   public static class JdbcTableModificationRule extends JdbcConverterRule {
     private JdbcTableModificationRule(JdbcConvention out) {
-      super(
-          LogicalTableModify.class,
-          Convention.NONE,
-          out,
+      super(TableModify.class, Convention.NONE, out,
           "JdbcTableModificationRule");
     }
 
     @Override public RelNode convert(RelNode rel) {
-      final LogicalTableModify modify =
-          (LogicalTableModify) rel;
+      final TableModify modify =
+          (TableModify) rel;
       final ModifiableTable modifiableTable =
           modify.getTable().unwrap(ModifiableTable.class);
-      if (modifiableTable == null
-          /* || modifiableTable.getExpression(tableInSchema) == null */) {
+      if (modifiableTable == null) {
         return null;
       }
       final RelTraitSet traitSet =
@@ -759,11 +777,11 @@ public class JdbcRules {
   /** Rule that converts a values operator to JDBC. */
   public static class JdbcValuesRule extends JdbcConverterRule {
     private JdbcValuesRule(JdbcConvention out) {
-      super(LogicalValues.class, Convention.NONE, out, "JdbcValuesRule");
+      super(Values.class, Convention.NONE, out, "JdbcValuesRule");
     }
 
     @Override public RelNode convert(RelNode rel) {
-      LogicalValues values = (LogicalValues) rel;
+      Values values = (Values) rel;
       return new JdbcValues(values.getCluster(), values.getRowType(),
           values.getTuples(), values.getTraitSet().replace(out));
     }

http://git-wip-us.apache.org/repos/asf/calcite/blob/facd83d3/core/src/main/java/org/apache/calcite/adapter/jdbc/JdbcSchema.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/adapter/jdbc/JdbcSchema.java b/core/src/main/java/org/apache/calcite/adapter/jdbc/JdbcSchema.java
index 8e7ecdf..eb91b14 100644
--- a/core/src/main/java/org/apache/calcite/adapter/jdbc/JdbcSchema.java
+++ b/core/src/main/java/org/apache/calcite/adapter/jdbc/JdbcSchema.java
@@ -104,7 +104,8 @@ public class JdbcSchema implements Schema {
       DataSource dataSource,
       String catalog,
       String schema) {
-    return create(parentSchema, name, dataSource, new SqlDialectFactoryImpl(), catalog, schema);
+    return create(parentSchema, name, dataSource,
+        SqlDialectFactoryImpl.INSTANCE, catalog, schema);
   }
 
   public static JdbcSchema create(
@@ -174,7 +175,7 @@ public class JdbcSchema implements Schema {
    */
   @Deprecated // to be removed before 2.0
   public static SqlDialect createDialect(DataSource dataSource) {
-    return createDialect(new SqlDialectFactoryImpl(), dataSource);
+    return createDialect(SqlDialectFactoryImpl.INSTANCE, dataSource);
   }
 
   /** Returns a suitable SQL dialect for the given data source. */

http://git-wip-us.apache.org/repos/asf/calcite/blob/facd83d3/core/src/main/java/org/apache/calcite/adapter/jdbc/JdbcTable.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/adapter/jdbc/JdbcTable.java b/core/src/main/java/org/apache/calcite/adapter/jdbc/JdbcTable.java
index b093eb2..02b6207 100644
--- a/core/src/main/java/org/apache/calcite/adapter/jdbc/JdbcTable.java
+++ b/core/src/main/java/org/apache/calcite/adapter/jdbc/JdbcTable.java
@@ -74,7 +74,7 @@ import java.util.List;
  * The resulting queryable can then be converted to a SQL query, which can be
  * executed efficiently on the JDBC server.</p>
  */
-class JdbcTable extends AbstractQueryableTable
+public class JdbcTable extends AbstractQueryableTable
     implements TranslatableTable, ScannableTable, ModifiableTable {
   private RelProtoDataType protoRowType;
   private final JdbcSchema jdbcSchema;

http://git-wip-us.apache.org/repos/asf/calcite/blob/facd83d3/core/src/main/java/org/apache/calcite/adapter/jdbc/JdbcTableScan.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/adapter/jdbc/JdbcTableScan.java b/core/src/main/java/org/apache/calcite/adapter/jdbc/JdbcTableScan.java
index 7ef8938..b621b85 100644
--- a/core/src/main/java/org/apache/calcite/adapter/jdbc/JdbcTableScan.java
+++ b/core/src/main/java/org/apache/calcite/adapter/jdbc/JdbcTableScan.java
@@ -30,7 +30,7 @@ import java.util.List;
  * Relational expression representing a scan of a table in a JDBC data source.
  */
 public class JdbcTableScan extends TableScan implements JdbcRel {
-  final JdbcTable jdbcTable;
+  protected final JdbcTable jdbcTable;
 
   protected JdbcTableScan(
       RelOptCluster cluster,

http://git-wip-us.apache.org/repos/asf/calcite/blob/facd83d3/core/src/main/java/org/apache/calcite/rel/rules/AbstractJoinExtractFilterRule.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/rules/AbstractJoinExtractFilterRule.java b/core/src/main/java/org/apache/calcite/rel/rules/AbstractJoinExtractFilterRule.java
new file mode 100644
index 0000000..106aa28
--- /dev/null
+++ b/core/src/main/java/org/apache/calcite/rel/rules/AbstractJoinExtractFilterRule.java
@@ -0,0 +1,85 @@
+/*
+ * 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.rel.rules;
+
+import org.apache.calcite.plan.RelOptRule;
+import org.apache.calcite.plan.RelOptRuleCall;
+import org.apache.calcite.plan.RelOptRuleOperand;
+import org.apache.calcite.rel.RelNode;
+import org.apache.calcite.rel.core.Join;
+import org.apache.calcite.rel.core.JoinRelType;
+import org.apache.calcite.tools.RelBuilder;
+import org.apache.calcite.tools.RelBuilderFactory;
+
+/**
+ * Rule to convert an
+ * {@link org.apache.calcite.rel.core.Join inner join} to a
+ * {@link org.apache.calcite.rel.core.Filter filter} on top of a
+ * {@link org.apache.calcite.rel.core.Join cartesian inner join}.
+ *
+ * <p>One benefit of this transformation is that after it, the join condition
+ * can be combined with conditions and expressions above the join. It also makes
+ * the <code>FennelCartesianJoinRule</code> applicable.
+ *
+ * <p>The constructor is parameterized to allow any sub-class of
+ * {@link org.apache.calcite.rel.core.Join}.</p>
+ */
+public abstract class AbstractJoinExtractFilterRule extends RelOptRule {
+  /** Creates an AbstractJoinExtractFilterRule. */
+  protected AbstractJoinExtractFilterRule(RelOptRuleOperand operand,
+      RelBuilderFactory relBuilderFactory, String description) {
+    super(operand, relBuilderFactory, description);
+  }
+
+  public void onMatch(RelOptRuleCall call) {
+    final Join join = call.rel(0);
+
+    if (join.getJoinType() != JoinRelType.INNER) {
+      return;
+    }
+
+    if (join.getCondition().isAlwaysTrue()) {
+      return;
+    }
+
+    if (!join.getSystemFieldList().isEmpty()) {
+      // FIXME Enable this rule for joins with system fields
+      return;
+    }
+
+    final RelBuilder builder = call.builder();
+
+    // NOTE jvs 14-Mar-2006:  See JoinCommuteRule for why we
+    // preserve attribute semiJoinDone here.
+
+    final RelNode cartesianJoin =
+        join.copy(
+            join.getTraitSet(),
+            builder.literal(true),
+            join.getLeft(),
+            join.getRight(),
+            join.getJoinType(),
+            join.isSemiJoinDone());
+
+    builder.push(cartesianJoin)
+        .filter(join.getCondition());
+
+    call.transformTo(builder.build());
+  }
+}
+
+// End AbstractJoinExtractFilterRule.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/facd83d3/core/src/main/java/org/apache/calcite/rel/rules/JoinExtractFilterRule.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/rules/JoinExtractFilterRule.java b/core/src/main/java/org/apache/calcite/rel/rules/JoinExtractFilterRule.java
index 8c4a02d..1e7e661 100644
--- a/core/src/main/java/org/apache/calcite/rel/rules/JoinExtractFilterRule.java
+++ b/core/src/main/java/org/apache/calcite/rel/rules/JoinExtractFilterRule.java
@@ -16,11 +16,7 @@
  */
 package org.apache.calcite.rel.rules;
 
-import org.apache.calcite.plan.RelOptRule;
-import org.apache.calcite.plan.RelOptRuleCall;
-import org.apache.calcite.rel.RelNode;
 import org.apache.calcite.rel.core.Join;
-import org.apache.calcite.rel.core.JoinRelType;
 import org.apache.calcite.rel.core.RelFactories;
 import org.apache.calcite.rel.logical.LogicalJoin;
 import org.apache.calcite.tools.RelBuilderFactory;
@@ -39,7 +35,7 @@ import org.apache.calcite.tools.RelBuilderFactory;
  * {@link org.apache.calcite.rel.core.Join}, not just
  * {@link org.apache.calcite.rel.logical.LogicalJoin}.</p>
  */
-public final class JoinExtractFilterRule extends RelOptRule {
+public final class JoinExtractFilterRule extends AbstractJoinExtractFilterRule {
   //~ Static fields/initializers ---------------------------------------------
 
   /** The singleton. */
@@ -50,14 +46,6 @@ public final class JoinExtractFilterRule extends RelOptRule {
   //~ Constructors -----------------------------------------------------------
 
   /**
-   * Creates an JoinExtractFilterRule.
-   */
-  @Deprecated // to be removed before 2.0
-  public JoinExtractFilterRule(Class<? extends Join> clazz) {
-    this(clazz, RelFactories.LOGICAL_BUILDER);
-  }
-
-  /**
    * Creates a JoinExtractFilterRule.
    */
   public JoinExtractFilterRule(Class<? extends Join> clazz,
@@ -67,41 +55,6 @@ public final class JoinExtractFilterRule extends RelOptRule {
 
   //~ Methods ----------------------------------------------------------------
 
-  public void onMatch(RelOptRuleCall call) {
-    final Join join = call.rel(0);
-
-    if (join.getJoinType() != JoinRelType.INNER) {
-      return;
-    }
-
-    if (join.getCondition().isAlwaysTrue()) {
-      return;
-    }
-
-    if (!join.getSystemFieldList().isEmpty()) {
-      // FIXME Enable this rule for joins with system fields
-      return;
-    }
-
-    // NOTE jvs 14-Mar-2006:  See JoinCommuteRule for why we
-    // preserve attribute semiJoinDone here.
-
-    RelNode cartesianJoinRel =
-        join.copy(
-            join.getTraitSet(),
-            join.getCluster().getRexBuilder().makeLiteral(true),
-            join.getLeft(),
-            join.getRight(),
-            join.getJoinType(),
-            join.isSemiJoinDone());
-
-    final RelFactories.FilterFactory factory =
-        RelFactories.DEFAULT_FILTER_FACTORY;
-    RelNode filterRel =
-        factory.createFilter(cartesianJoinRel, join.getCondition());
-
-    call.transformTo(filterRel);
-  }
 }
 
 // End JoinExtractFilterRule.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/facd83d3/core/src/main/java/org/apache/calcite/sql/SqlDialect.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql/SqlDialect.java b/core/src/main/java/org/apache/calcite/sql/SqlDialect.java
index 42cf7b1..07bc60a 100644
--- a/core/src/main/java/org/apache/calcite/sql/SqlDialect.java
+++ b/core/src/main/java/org/apache/calcite/sql/SqlDialect.java
@@ -23,6 +23,7 @@ import org.apache.calcite.rel.RelFieldCollation;
 import org.apache.calcite.rel.type.RelDataType;
 import org.apache.calcite.sql.dialect.AnsiSqlDialect;
 import org.apache.calcite.sql.dialect.CalciteSqlDialect;
+import org.apache.calcite.sql.dialect.JethroDataSqlDialect;
 import org.apache.calcite.sql.fun.SqlStdOperatorTable;
 import org.apache.calcite.sql.parser.SqlParserPos;
 import org.apache.calcite.sql.type.BasicSqlType;
@@ -157,7 +158,7 @@ public class SqlDialect {
   /** Creates an empty context. Use {@link #EMPTY_CONTEXT} if possible. */
   protected static Context emptyContext() {
     return new ContextImpl(DatabaseProduct.UNKNOWN, null, null, -1, -1, null,
-        NullCollation.HIGH);
+        NullCollation.HIGH, JethroDataSqlDialect.JethroInfo.EMPTY);
   }
 
   /**
@@ -502,6 +503,12 @@ public class SqlDialect {
     return false;
   }
 
+  /** Returns whether this dialect supports a given function or operator. */
+  public boolean supportsFunction(SqlOperator operator, RelDataType type,
+      List<RelDataType> paramTypes) {
+    return true;
+  }
+
   public CalendarPolicy getCalendarPolicy() {
     return CalendarPolicy.NULL;
   }
@@ -778,6 +785,7 @@ public class SqlDialect {
     HIVE("Apache Hive", null, NullCollation.LOW),
     INFORMIX("Informix", null, NullCollation.HIGH),
     INGRES("Ingres", null, NullCollation.HIGH),
+    JETHRO("JethroData", "\"", NullCollation.LOW),
     LUCIDDB("LucidDB", "\"", NullCollation.HIGH),
     INTERBASE("Interbase", null, NullCollation.HIGH),
     PHOENIX("Phoenix", "\"", NullCollation.HIGH),
@@ -839,7 +847,7 @@ public class SqlDialect {
      * <p>Since databases have many versions and flavors, this dummy dialect
      * is at best an approximation. If you want exact information, better to
      * use a dialect created from an actual connection's metadata
-     * (see {@link SqlDialect#create(java.sql.DatabaseMetaData)}).
+     * (see {@link SqlDialectFactory#create(java.sql.DatabaseMetaData)}).
      *
      * @return Dialect representing lowest-common-denominator behavior for
      * all versions of this database
@@ -868,6 +876,8 @@ public class SqlDialect {
     Context withIdentifierQuoteString(String identifierQuoteString);
     @Nonnull NullCollation nullCollation();
     Context withNullCollation(@Nonnull NullCollation nullCollation);
+    JethroDataSqlDialect.JethroInfo jethroInfo();
+    Context withJethroInfo(JethroDataSqlDialect.JethroInfo jethroInfo);
   }
 
   /** Implementation of Context. */
@@ -879,11 +889,13 @@ public class SqlDialect {
     private final int databaseMinorVersion;
     private final String identifierQuoteString;
     private final NullCollation nullCollation;
+    private final JethroDataSqlDialect.JethroInfo jethroInfo;
 
     private ContextImpl(DatabaseProduct databaseProduct,
         String databaseProductName, String databaseVersion,
         int databaseMajorVersion, int databaseMinorVersion,
-        String identifierQuoteString, NullCollation nullCollation) {
+        String identifierQuoteString, NullCollation nullCollation,
+        JethroDataSqlDialect.JethroInfo jethroInfo) {
       this.databaseProduct = Preconditions.checkNotNull(databaseProduct);
       this.databaseProductName = databaseProductName;
       this.databaseVersion = databaseVersion;
@@ -891,6 +903,7 @@ public class SqlDialect {
       this.databaseMinorVersion = databaseMinorVersion;
       this.identifierQuoteString = identifierQuoteString;
       this.nullCollation = Preconditions.checkNotNull(nullCollation);
+      this.jethroInfo = Preconditions.checkNotNull(jethroInfo);
     }
 
     @Nonnull public DatabaseProduct databaseProduct() {
@@ -901,7 +914,7 @@ public class SqlDialect {
         @Nonnull DatabaseProduct databaseProduct) {
       return new ContextImpl(databaseProduct, databaseProductName,
           databaseVersion, databaseMajorVersion, databaseMinorVersion,
-          identifierQuoteString, nullCollation);
+          identifierQuoteString, nullCollation, jethroInfo);
     }
 
     public String databaseProductName() {
@@ -911,7 +924,7 @@ public class SqlDialect {
     public Context withDatabaseProductName(String databaseProductName) {
       return new ContextImpl(databaseProduct, databaseProductName,
           databaseVersion, databaseMajorVersion, databaseMinorVersion,
-          identifierQuoteString, nullCollation);
+          identifierQuoteString, nullCollation, jethroInfo);
     }
 
     public String databaseVersion() {
@@ -921,7 +934,7 @@ public class SqlDialect {
     public Context withDatabaseVersion(String databaseVersion) {
       return new ContextImpl(databaseProduct, databaseProductName,
           databaseVersion, databaseMajorVersion, databaseMinorVersion,
-          identifierQuoteString, nullCollation);
+          identifierQuoteString, nullCollation, jethroInfo);
     }
 
     public int databaseMajorVersion() {
@@ -931,7 +944,7 @@ public class SqlDialect {
     public Context withDatabaseMajorVersion(int databaseMajorVersion) {
       return new ContextImpl(databaseProduct, databaseProductName,
           databaseVersion, databaseMajorVersion, databaseMinorVersion,
-          identifierQuoteString, nullCollation);
+          identifierQuoteString, nullCollation, jethroInfo);
     }
 
     public int databaseMinorVersion() {
@@ -941,7 +954,7 @@ public class SqlDialect {
     public Context withDatabaseMinorVersion(int databaseMinorVersion) {
       return new ContextImpl(databaseProduct, databaseProductName,
           databaseVersion, databaseMajorVersion, databaseMinorVersion,
-          identifierQuoteString, nullCollation);
+          identifierQuoteString, nullCollation, jethroInfo);
     }
 
     public String identifierQuoteString() {
@@ -951,7 +964,7 @@ public class SqlDialect {
     public Context withIdentifierQuoteString(String identifierQuoteString) {
       return new ContextImpl(databaseProduct, databaseProductName,
           databaseVersion, databaseMajorVersion, databaseMinorVersion,
-          identifierQuoteString, nullCollation);
+          identifierQuoteString, nullCollation, jethroInfo);
     }
 
     @Nonnull public NullCollation nullCollation() {
@@ -961,7 +974,17 @@ public class SqlDialect {
     public Context withNullCollation(@Nonnull NullCollation nullCollation) {
       return new ContextImpl(databaseProduct, databaseProductName,
           databaseVersion, databaseMajorVersion, databaseMinorVersion,
-          identifierQuoteString, nullCollation);
+          identifierQuoteString, nullCollation, jethroInfo);
+    }
+
+    @Nonnull public JethroDataSqlDialect.JethroInfo jethroInfo() {
+      return jethroInfo;
+    }
+
+    public Context withJethroInfo(JethroDataSqlDialect.JethroInfo jethroInfo) {
+      return new ContextImpl(databaseProduct, databaseProductName,
+          databaseVersion, databaseMajorVersion, databaseMinorVersion,
+          identifierQuoteString, nullCollation, jethroInfo);
     }
   }
 }

http://git-wip-us.apache.org/repos/asf/calcite/blob/facd83d3/core/src/main/java/org/apache/calcite/sql/SqlDialectFactory.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql/SqlDialectFactory.java b/core/src/main/java/org/apache/calcite/sql/SqlDialectFactory.java
index 73de073..202b8fb 100644
--- a/core/src/main/java/org/apache/calcite/sql/SqlDialectFactory.java
+++ b/core/src/main/java/org/apache/calcite/sql/SqlDialectFactory.java
@@ -20,7 +20,7 @@ import java.sql.Connection;
 import java.sql.DatabaseMetaData;
 
 /**
- * <code>SqlDialectFactory</code> constructs a <code>SqlDialect</code> appropriate
+ * Creates a <code>SqlDialect</code> appropriate
  * for a given database metadata object.
  */
 public interface SqlDialectFactory {
@@ -34,6 +34,8 @@ public interface SqlDialectFactory {
    *
    * @param databaseMetaData used to determine which dialect of SQL to
    *                         generate
+   *
+   * @throws RuntimeException if there was an error creating the dialect
    */
   SqlDialect create(DatabaseMetaData databaseMetaData);
 

http://git-wip-us.apache.org/repos/asf/calcite/blob/facd83d3/core/src/main/java/org/apache/calcite/sql/SqlDialectFactoryImpl.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql/SqlDialectFactoryImpl.java b/core/src/main/java/org/apache/calcite/sql/SqlDialectFactoryImpl.java
index c2bf4e3..18de463 100644
--- a/core/src/main/java/org/apache/calcite/sql/SqlDialectFactoryImpl.java
+++ b/core/src/main/java/org/apache/calcite/sql/SqlDialectFactoryImpl.java
@@ -17,6 +17,7 @@
 package org.apache.calcite.sql;
 
 import org.apache.calcite.config.NullCollation;
+
 import org.apache.calcite.sql.dialect.AccessSqlDialect;
 import org.apache.calcite.sql.dialect.AnsiSqlDialect;
 import org.apache.calcite.sql.dialect.CalciteSqlDialect;
@@ -30,6 +31,7 @@ import org.apache.calcite.sql.dialect.InfobrightSqlDialect;
 import org.apache.calcite.sql.dialect.InformixSqlDialect;
 import org.apache.calcite.sql.dialect.IngresSqlDialect;
 import org.apache.calcite.sql.dialect.InterbaseSqlDialect;
+import org.apache.calcite.sql.dialect.JethroDataSqlDialect;
 import org.apache.calcite.sql.dialect.LucidDbSqlDialect;
 import org.apache.calcite.sql.dialect.MssqlSqlDialect;
 import org.apache.calcite.sql.dialect.MysqlSqlDialect;
@@ -44,6 +46,9 @@ import org.apache.calcite.sql.dialect.SybaseSqlDialect;
 import org.apache.calcite.sql.dialect.TeradataSqlDialect;
 import org.apache.calcite.sql.dialect.VerticaSqlDialect;
 
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
 import java.sql.DatabaseMetaData;
 import java.sql.SQLException;
 import java.util.Locale;
@@ -52,14 +57,23 @@ import java.util.Locale;
  * The default implementation of a <code>SqlDialectFactory</code>.
  */
 public class SqlDialectFactoryImpl implements SqlDialectFactory {
+  private static final Logger LOGGER = LoggerFactory.getLogger(SqlDialectFactoryImpl.class);
+
+  public static final SqlDialectFactoryImpl INSTANCE = new SqlDialectFactoryImpl();
+
+  private final JethroDataSqlDialect.JethroInfoCache jethroCache =
+      JethroDataSqlDialect.createCache();
+
   public SqlDialect create(DatabaseMetaData databaseMetaData) {
     String databaseProductName;
     int databaseMajorVersion;
     int databaseMinorVersion;
+    String databaseVersion;
     try {
       databaseProductName = databaseMetaData.getDatabaseProductName();
       databaseMajorVersion = databaseMetaData.getDatabaseMajorVersion();
       databaseMinorVersion = databaseMetaData.getDatabaseMinorVersion();
+      databaseVersion = databaseMetaData.getDatabaseProductVersion();
     } catch (SQLException e) {
       throw new RuntimeException("while detecting database product", e);
     }
@@ -71,6 +85,7 @@ public class SqlDialectFactoryImpl implements SqlDialectFactory {
         .withDatabaseProductName(databaseProductName)
         .withDatabaseMajorVersion(databaseMajorVersion)
         .withDatabaseMinorVersion(databaseMinorVersion)
+        .withDatabaseVersion(databaseVersion)
         .withIdentifierQuoteString(quoteString)
         .withNullCollation(nullCollation);
     switch (upperProductName) {
@@ -86,6 +101,9 @@ public class SqlDialectFactoryImpl implements SqlDialectFactory {
       return new IngresSqlDialect(c);
     case "INTERBASE":
       return new InterbaseSqlDialect(c);
+    case "JETHRODATA":
+      return new JethroDataSqlDialect(
+          c.withJethroInfo(jethroCache.get(databaseMetaData)));
     case "LUCIDDB":
       return new LucidDbSqlDialect(c);
     case "ORACLE":
@@ -184,6 +202,8 @@ public class SqlDialectFactoryImpl implements SqlDialectFactory {
       return IngresSqlDialect.DEFAULT;
     case INTERBASE:
       return InterbaseSqlDialect.DEFAULT;
+    case JETHRO:
+      throw new RuntimeException("Jethro does not support simple creation");
     case LUCIDDB:
       return LucidDbSqlDialect.DEFAULT;
     case MSSQL:
@@ -216,6 +236,7 @@ public class SqlDialectFactoryImpl implements SqlDialectFactory {
       return null;
     }
   }
+
 }
 
 // End SqlDialectFactoryImpl.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/facd83d3/core/src/main/java/org/apache/calcite/sql/dialect/JethroDataSqlDialect.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql/dialect/JethroDataSqlDialect.java b/core/src/main/java/org/apache/calcite/sql/dialect/JethroDataSqlDialect.java
new file mode 100644
index 0000000..26f0148
--- /dev/null
+++ b/core/src/main/java/org/apache/calcite/sql/dialect/JethroDataSqlDialect.java
@@ -0,0 +1,235 @@
+/*
+ * 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.sql.dialect;
+
+import org.apache.calcite.rel.type.RelDataType;
+import org.apache.calcite.sql.SqlDialect;
+import org.apache.calcite.sql.SqlKind;
+import org.apache.calcite.sql.SqlNode;
+import org.apache.calcite.sql.SqlOperator;
+import org.apache.calcite.sql.type.SqlTypeName;
+
+import com.google.common.base.Preconditions;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSetMultimap;
+import com.google.common.collect.LinkedHashMultimap;
+import com.google.common.collect.Multimap;
+
+import java.sql.Connection;
+import java.sql.DatabaseMetaData;
+import java.sql.ResultSet;
+import java.sql.Statement;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Set;
+
+
+/**
+ * A <code>SqlDialect</code> implementation for the JethroData database.
+ */
+public class JethroDataSqlDialect extends SqlDialect {
+  private final JethroInfo info;
+
+  /** Creates a JethroDataSqlDialect. */
+  public JethroDataSqlDialect(Context context) {
+    super(context);
+    this.info = context.jethroInfo();
+  }
+
+  @Override public boolean supportsCharSet() {
+    return false;
+  }
+
+  @Override public SqlNode emulateNullDirection(SqlNode node,
+      boolean nullsFirst, boolean desc) {
+    return node;
+  }
+
+  @Override public boolean supportsAggregateFunction(SqlKind kind) {
+    switch (kind) {
+    case COUNT:
+    case SUM:
+    case AVG:
+    case MIN:
+    case MAX:
+    case STDDEV_POP:
+    case STDDEV_SAMP:
+    case VAR_POP:
+    case VAR_SAMP:
+      return true;
+    default:
+      break;
+    }
+    return false;
+  }
+
+  @Override public boolean supportsFunction(SqlOperator operator,
+      RelDataType type, List<RelDataType> paramTypes) {
+    switch (operator.getKind()) {
+    case IS_NOT_NULL:
+    case IS_NULL:
+    case AND:
+    case OR:
+    case NOT:
+    case BETWEEN:
+    case CASE:
+    case CAST:
+      return true;
+    }
+    final Set<JethroSupportedFunction> functions =
+        info.supportedFunctions.get(operator.getName());
+
+    if (functions != null) {
+      for (JethroSupportedFunction f : functions) {
+        if (f.argumentsMatch(paramTypes)) {
+          return true;
+        }
+      }
+    }
+    LOGGER.debug("Unsupported function in jethro: " + operator + " with params "
+        + paramTypes);
+    return false;
+  }
+
+  @SuppressWarnings("deprecation")
+  @Override public boolean supportsOffsetFetch() {
+    return false;
+  }
+
+  @Override public boolean supportsNestedAggregations() {
+    return false;
+  }
+
+  public static JethroInfoCache createCache() {
+    return new JethroInfoCacheImpl();
+  }
+
+  /** Information about a function supported by Jethro. */
+  static class JethroSupportedFunction {
+    private final List<SqlTypeName> operandTypes;
+
+    JethroSupportedFunction(String name, String operands) {
+      Preconditions.checkNotNull(name); // not currently used
+      final ImmutableList.Builder<SqlTypeName> b = ImmutableList.builder();
+      for (String strType : operands.split(":")) {
+        b.add(parse(strType));
+      }
+      this.operandTypes = b.build();
+    }
+
+    private SqlTypeName parse(String strType) {
+      switch (strType.toLowerCase(Locale.ROOT)) {
+      case "bigint":
+      case "long":
+        return SqlTypeName.BIGINT;
+      case "integer":
+      case "int":
+        return SqlTypeName.INTEGER;
+      case "double":
+        return SqlTypeName.DOUBLE;
+      case "float":
+        return SqlTypeName.FLOAT;
+      case "string":
+        return SqlTypeName.VARCHAR;
+      case "timestamp":
+        return SqlTypeName.TIMESTAMP;
+      default:
+        return SqlTypeName.ANY;
+      }
+    }
+
+    boolean argumentsMatch(List<RelDataType> paramTypes) {
+      if (paramTypes.size() != operandTypes.size()) {
+        return false;
+      }
+      for (int i = 0; i < paramTypes.size(); i++) {
+        if (paramTypes.get(i).getSqlTypeName() != operandTypes.get(i)) {
+          return false;
+        }
+      }
+      return true;
+    }
+  }
+
+  /** Stores information about capabilities of Jethro databases. */
+  public interface JethroInfoCache {
+    JethroInfo get(DatabaseMetaData databaseMetaData);
+  }
+
+  /** Implementation of {@code JethroInfoCache}. */
+  private static class JethroInfoCacheImpl implements JethroInfoCache {
+    final Map<String, JethroInfo> map = new HashMap<>();
+
+    public JethroInfo get(final DatabaseMetaData metaData) {
+      try {
+        assert "JethroData".equals(metaData.getDatabaseProductName());
+        String productVersion = metaData.getDatabaseProductVersion();
+        synchronized (JethroInfoCacheImpl.this) {
+          JethroInfo info = map.get(productVersion);
+          if (info == null) {
+            final Connection c = metaData.getConnection();
+            info = makeInfo(c);
+            map.put(productVersion, info);
+          }
+          return info;
+        }
+      } catch (Exception e) {
+        LOGGER.error("Failed to create JethroDataDialect", e);
+        throw new RuntimeException("Failed to create JethroDataDialect", e);
+      }
+    }
+
+    private JethroInfo makeInfo(Connection jethroConnection) {
+      try (Statement jethroStatement = jethroConnection.createStatement();
+           ResultSet functionsTupleSet =
+               jethroStatement.executeQuery("show functions extended")) {
+        final Multimap<String, JethroSupportedFunction> supportedFunctions =
+            LinkedHashMultimap.create();
+        while (functionsTupleSet.next()) {
+          String functionName = functionsTupleSet.getString(1);
+          String operandsType = functionsTupleSet.getString(3);
+          supportedFunctions.put(functionName,
+              new JethroSupportedFunction(functionName, operandsType));
+        }
+        return new JethroInfo(supportedFunctions);
+      } catch (Exception e) {
+        final String msg =
+            "Jethro server failed to execute 'show functions extended'";
+        LOGGER.error(msg, e);
+        throw new RuntimeException(msg
+            + "; make sure your Jethro server is up to date", e);
+      }
+    }
+  }
+
+  /** Information about the capabilities of a Jethro database. */
+  public static class JethroInfo {
+    public static final JethroInfo EMPTY = new JethroInfo(
+        ImmutableSetMultimap.<String, JethroSupportedFunction>of());
+
+    private final ImmutableSetMultimap<String, JethroSupportedFunction> supportedFunctions;
+
+    public JethroInfo(Multimap<String, JethroSupportedFunction> supportedFunctions) {
+      this.supportedFunctions = ImmutableSetMultimap.copyOf(supportedFunctions);
+    }
+  }
+}
+
+// End JethroDataSqlDialect.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/facd83d3/core/src/test/java/org/apache/calcite/rel/rel2sql/RelToSqlConverterTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/calcite/rel/rel2sql/RelToSqlConverterTest.java b/core/src/test/java/org/apache/calcite/rel/rel2sql/RelToSqlConverterTest.java
index d6bb976..2cdda74 100644
--- a/core/src/test/java/org/apache/calcite/rel/rel2sql/RelToSqlConverterTest.java
+++ b/core/src/test/java/org/apache/calcite/rel/rel2sql/RelToSqlConverterTest.java
@@ -29,9 +29,11 @@ import org.apache.calcite.rel.rules.UnionMergeRule;
 import org.apache.calcite.runtime.FlatLists;
 import org.apache.calcite.schema.SchemaPlus;
 import org.apache.calcite.sql.SqlDialect;
+import org.apache.calcite.sql.SqlDialect.Context;
 import org.apache.calcite.sql.SqlNode;
 import org.apache.calcite.sql.dialect.CalciteSqlDialect;
 import org.apache.calcite.sql.dialect.HiveSqlDialect;
+import org.apache.calcite.sql.dialect.JethroDataSqlDialect;
 import org.apache.calcite.sql.dialect.MysqlSqlDialect;
 import org.apache.calcite.sql.parser.SqlParser;
 import org.apache.calcite.sql2rel.SqlToRelConverter;
@@ -95,6 +97,18 @@ public class RelToSqlConverterTest {
     return Frameworks.getPlanner(config);
   }
 
+  private static JethroDataSqlDialect jethroDataSqlDialect() {
+    Context dummyContext = SqlDialect.EMPTY_CONTEXT
+        .withDatabaseProduct(SqlDialect.DatabaseProduct.JETHRO)
+        .withDatabaseMajorVersion(1)
+        .withDatabaseMinorVersion(0)
+        .withDatabaseVersion("1.0")
+        .withIdentifierQuoteString("\"")
+        .withNullCollation(NullCollation.HIGH)
+        .withJethroInfo(JethroDataSqlDialect.JethroInfo.EMPTY);
+    return new JethroDataSqlDialect(dummyContext);
+  }
+
   private static MysqlSqlDialect mySqlDialect(NullCollation nullCollation) {
     return new MysqlSqlDialect(SqlDialect.EMPTY_CONTEXT
         .withDatabaseProduct(SqlDialect.DatabaseProduct.MYSQL)
@@ -422,6 +436,16 @@ public class RelToSqlConverterTest {
     sql(query).dialect(hive2_1_0_Dialect).ok(expected);
   }
 
+  @Test public void testJethroDataSelectQueryWithOrderByDescAndNullsFirstShouldBeEmulated() {
+    final String query = "select \"product_id\" from \"product\"\n"
+        + "order by \"product_id\" desc nulls first";
+
+    final String expected = "SELECT \"product_id\"\n"
+        + "FROM \"foodmart\".\"product\"\n"
+        + "ORDER BY \"product_id\", \"product_id\" DESC";
+    sql(query).dialect(jethroDataSqlDialect()).ok(expected);
+  }
+
   @Test public void testMySqlSelectQueryWithOrderByDescAndNullsFirstShouldBeEmulated() {
     final String query = "select \"product_id\" from \"product\"\n"
         + "order by \"product_id\" desc nulls first";