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 2014/12/09 22:49:49 UTC

[5/8] incubator-calcite git commit: [CALCITE-93] Calcite RPC server; [CALCITE-94] Remote JDBC driver

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/94752a60/avatica/src/main/java/org/apache/calcite/avatica/util/DateTimeUtils.java
----------------------------------------------------------------------
diff --git a/avatica/src/main/java/org/apache/calcite/avatica/util/DateTimeUtils.java b/avatica/src/main/java/org/apache/calcite/avatica/util/DateTimeUtils.java
new file mode 100644
index 0000000..084c027
--- /dev/null
+++ b/avatica/src/main/java/org/apache/calcite/avatica/util/DateTimeUtils.java
@@ -0,0 +1,748 @@
+/*
+ * 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.avatica.util;
+
+import java.text.NumberFormat;
+import java.text.ParsePosition;
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+import java.util.TimeZone;
+
+/**
+ * Utility functions for datetime types: date, time, timestamp.
+ *
+ * <p>Used by the JDBC driver.
+ *
+ * <p>TODO: review methods for performance. Due to allocations required, it may
+ * be preferable to introduce a "formatter" with the required state.
+ */
+public class DateTimeUtils {
+  /** The julian date of the epoch, 1970-01-01. */
+  public static final int EPOCH_JULIAN = 2440588;
+
+  private DateTimeUtils() {}
+
+  //~ Static fields/initializers ---------------------------------------------
+
+  /** The SimpleDateFormat string for ISO dates, "yyyy-MM-dd". */
+  public static final String DATE_FORMAT_STRING = "yyyy-MM-dd";
+
+  /** The SimpleDateFormat string for ISO times, "HH:mm:ss". */
+  public static final String TIME_FORMAT_STRING = "HH:mm:ss";
+
+  /** The SimpleDateFormat string for ISO timestamps, "yyyy-MM-dd HH:mm:ss". */
+  public static final String TIMESTAMP_FORMAT_STRING =
+      DATE_FORMAT_STRING + " " + TIME_FORMAT_STRING;
+
+  /** The GMT time zone. */
+  public static final TimeZone GMT_ZONE = TimeZone.getTimeZone("GMT");
+
+  /** The Java default time zone. */
+  public static final TimeZone DEFAULT_ZONE = TimeZone.getDefault();
+
+  /**
+   * The number of milliseconds in a second.
+   */
+  public static final long MILLIS_PER_SECOND = 1000L;
+
+  /**
+   * The number of milliseconds in a minute.
+   */
+  public static final long MILLIS_PER_MINUTE = 60000L;
+
+  /**
+   * The number of milliseconds in an hour.
+   */
+  public static final long MILLIS_PER_HOUR = 3600000L; // = 60 * 60 * 1000
+
+  /**
+   * The number of milliseconds in a day.
+   *
+   * <p>This is the modulo 'mask' used when converting
+   * TIMESTAMP values to DATE and TIME values.
+   */
+  public static final long MILLIS_PER_DAY = 86400000; // = 24 * 60 * 60 * 1000
+
+  /**
+   * Calendar set to the epoch (1970-01-01 00:00:00 UTC). Useful for
+   * initializing other values. Calendars are not immutable, so be careful not
+   * to screw up this object for everyone else.
+   */
+  public static final Calendar ZERO_CALENDAR;
+
+  static {
+    ZERO_CALENDAR = Calendar.getInstance(DateTimeUtils.GMT_ZONE);
+    ZERO_CALENDAR.setTimeInMillis(0);
+  }
+
+  /**
+   * Calendar set to local time.
+   */
+  private static final Calendar LOCAL_CALENDAR = Calendar.getInstance();
+
+  //~ Methods ----------------------------------------------------------------
+
+  /**
+   * Parses a string using {@link SimpleDateFormat} and a given pattern. This
+   * method parses a string at the specified parse position and if successful,
+   * updates the parse position to the index after the last character used.
+   * The parsing is strict and requires months to be less than 12, days to be
+   * less than 31, etc.
+   *
+   * @param s       string to be parsed
+   * @param pattern {@link SimpleDateFormat}  pattern
+   * @param tz      time zone in which to interpret string. Defaults to the Java
+   *                default time zone
+   * @param pp      position to start parsing from
+   * @return a Calendar initialized with the parsed value, or null if parsing
+   * failed. If returned, the Calendar is configured to the GMT time zone.
+   * @pre pattern != null
+   */
+  private static Calendar parseDateFormat(
+      String s,
+      String pattern,
+      TimeZone tz,
+      ParsePosition pp) {
+    assert pattern != null;
+    SimpleDateFormat df = new SimpleDateFormat(pattern);
+    if (tz == null) {
+      tz = DEFAULT_ZONE;
+    }
+    Calendar ret = Calendar.getInstance(tz);
+    df.setCalendar(ret);
+    df.setLenient(false);
+
+    java.util.Date d = df.parse(s, pp);
+    if (null == d) {
+      return null;
+    }
+    ret.setTime(d);
+    ret.setTimeZone(GMT_ZONE);
+    return ret;
+  }
+
+  /**
+   * Parses a string using {@link SimpleDateFormat} and a given pattern. The
+   * entire string must match the pattern specified.
+   *
+   * @param s       string to be parsed
+   * @param pattern {@link SimpleDateFormat}  pattern
+   * @param tz      time zone in which to interpret string. Defaults to the Java
+   *                default time zone
+   * @return a Calendar initialized with the parsed value, or null if parsing
+   * failed. If returned, the Calendar is configured to the GMT time zone.
+   */
+  public static Calendar parseDateFormat(
+      String s,
+      String pattern,
+      TimeZone tz) {
+    assert pattern != null;
+    ParsePosition pp = new ParsePosition(0);
+    Calendar ret = parseDateFormat(s, pattern, tz, pp);
+    if (pp.getIndex() != s.length()) {
+      // Didn't consume entire string - not good
+      return null;
+    }
+    return ret;
+  }
+
+  /**
+   * Parses a string using {@link SimpleDateFormat} and a given pattern, and
+   * if present, parses a fractional seconds component. The fractional seconds
+   * component must begin with a decimal point ('.') followed by numeric
+   * digits. The precision is rounded to a maximum of 3 digits of fractional
+   * seconds precision (to obtain milliseconds).
+   *
+   * @param s       string to be parsed
+   * @param pattern {@link SimpleDateFormat}  pattern
+   * @param tz      time zone in which to interpret string. Defaults to the
+   *                local time zone
+   * @return a {@link DateTimeUtils.PrecisionTime PrecisionTime} initialized
+   * with the parsed value, or null if parsing failed. The PrecisionTime
+   * contains a GMT Calendar and a precision.
+   */
+  public static PrecisionTime parsePrecisionDateTimeLiteral(
+      String s,
+      String pattern,
+      TimeZone tz) {
+    assert pattern != null;
+    ParsePosition pp = new ParsePosition(0);
+    Calendar cal = parseDateFormat(s, pattern, tz, pp);
+    if (cal == null) {
+      return null; // Invalid date/time format
+    }
+
+    // Note: the Java SimpleDateFormat 'S' treats any number after
+    // the decimal as milliseconds. That means 12:00:00.9 has 9
+    // milliseconds and 12:00:00.9999 has 9999 milliseconds.
+    int p = 0;
+    if (pp.getIndex() < s.length()) {
+      // Check to see if rest is decimal portion
+      if (s.charAt(pp.getIndex()) != '.') {
+        return null;
+      }
+
+      // Skip decimal sign
+      pp.setIndex(pp.getIndex() + 1);
+
+      // Parse decimal portion
+      if (pp.getIndex() < s.length()) {
+        String secFraction = s.substring(pp.getIndex());
+        if (!secFraction.matches("\\d+")) {
+          return null;
+        }
+        NumberFormat nf = NumberFormat.getIntegerInstance();
+        Number num = nf.parse(s, pp);
+        if ((num == null) || (pp.getIndex() != s.length())) {
+          // Invalid decimal portion
+          return null;
+        }
+
+        // Determine precision - only support prec 3 or lower
+        // (milliseconds) Higher precisions are quietly rounded away
+        p = Math.min(
+            3,
+            secFraction.length());
+
+        // Calculate milliseconds
+        int ms =
+            (int) Math.round(
+                num.longValue()
+                * Math.pow(10, 3 - secFraction.length()));
+        cal.add(Calendar.MILLISECOND, ms);
+      }
+    }
+
+    assert pp.getIndex() == s.length();
+    PrecisionTime ret = new PrecisionTime(cal, p);
+    return ret;
+  }
+
+  /**
+   * Gets the active time zone based on a Calendar argument
+   */
+  public static TimeZone getTimeZone(Calendar cal) {
+    if (cal == null) {
+      return DEFAULT_ZONE;
+    }
+    return cal.getTimeZone();
+  }
+
+  /**
+   * Checks if the date/time format is valid
+   *
+   * @param pattern {@link SimpleDateFormat}  pattern
+   * @throws IllegalArgumentException if the given pattern is invalid
+   */
+  public static void checkDateFormat(String pattern) {
+    new SimpleDateFormat(pattern);
+  }
+
+  /**
+   * Creates a new date formatter with Farrago specific options. Farrago
+   * parsing is strict and does not allow values such as day 0, month 13, etc.
+   *
+   * @param format {@link SimpleDateFormat}  pattern
+   */
+  public static SimpleDateFormat newDateFormat(String format) {
+    SimpleDateFormat sdf = new SimpleDateFormat(format);
+    sdf.setLenient(false);
+    return sdf;
+  }
+
+  /** Helper for CAST({timestamp} AS VARCHAR(n)). */
+  public static String unixTimestampToString(long timestamp) {
+    final StringBuilder buf = new StringBuilder(17);
+    int date = (int) (timestamp / MILLIS_PER_DAY);
+    int time = (int) (timestamp % MILLIS_PER_DAY);
+    if (time < 0) {
+      --date;
+      time += MILLIS_PER_DAY;
+    }
+    unixDateToString(buf, date);
+    buf.append(' ');
+    unixTimeToString(buf, time);
+    return buf.toString();
+  }
+
+  /** Helper for CAST({timestamp} AS VARCHAR(n)). */
+  public static String unixTimeToString(int time) {
+    final StringBuilder buf = new StringBuilder(8);
+    unixTimeToString(buf, time);
+    return buf.toString();
+  }
+
+  private static void unixTimeToString(StringBuilder buf, int time) {
+    int h = time / 3600000;
+    int time2 = time % 3600000;
+    int m = time2 / 60000;
+    int time3 = time2 % 60000;
+    int s = time3 / 1000;
+    int ms = time3 % 1000;
+    int2(buf, h);
+    buf.append(':');
+    int2(buf, m);
+    buf.append(':');
+    int2(buf, s);
+  }
+
+  private static void int2(StringBuilder buf, int i) {
+    buf.append((char) ('0' + (i / 10) % 10));
+    buf.append((char) ('0' + i % 10));
+  }
+
+  private static void int4(StringBuilder buf, int i) {
+    buf.append((char) ('0' + (i / 1000) % 10));
+    buf.append((char) ('0' + (i / 100) % 10));
+    buf.append((char) ('0' + (i / 10) % 10));
+    buf.append((char) ('0' + i % 10));
+  }
+
+  /** Helper for CAST({date} AS VARCHAR(n)). */
+  public static String unixDateToString(int date) {
+    final StringBuilder buf = new StringBuilder(10);
+    unixDateToString(buf, date);
+    return buf.toString();
+  }
+
+  private static void unixDateToString(StringBuilder buf, int date) {
+    julianToString(buf, date + EPOCH_JULIAN);
+  }
+
+  private static void julianToString(StringBuilder buf, int julian) {
+    // this shifts the epoch back to astronomical year -4800 instead of the
+    // start of the Christian era in year AD 1 of the proleptic Gregorian
+    // calendar.
+    int j = julian + 32044;
+    int g = j / 146097;
+    int dg = j % 146097;
+    int c = (dg / 36524 + 1) * 3 / 4;
+    int dc = dg - c * 36524;
+    int b = dc / 1461;
+    int db = dc % 1461;
+    int a = (db / 365 + 1) * 3 / 4;
+    int da = db - a * 365;
+
+    // integer number of full years elapsed since March 1, 4801 BC
+    int y = g * 400 + c * 100 + b * 4 + a;
+    // integer number of full months elapsed since the last March 1
+    int m = (da * 5 + 308) / 153 - 2;
+    // number of days elapsed since day 1 of the month
+    int d = da - (m + 4) * 153 / 5 + 122;
+    int year = y - 4800 + (m + 2) / 12;
+    int month = (m + 2) % 12 + 1;
+    int day = d + 1;
+    int4(buf, year);
+    buf.append('-');
+    int2(buf, month);
+    buf.append('-');
+    int2(buf, day);
+  }
+
+  public static String intervalYearMonthToString(int v, TimeUnitRange range) {
+    final StringBuilder buf = new StringBuilder();
+    if (v >= 0) {
+      buf.append('+');
+    } else {
+      buf.append('-');
+      v = -v;
+    }
+    final int y;
+    final int m;
+    switch (range) {
+    case YEAR:
+      v = roundUp(v, 12);
+      y = v / 12;
+      buf.append(y);
+      break;
+    case YEAR_TO_MONTH:
+      y = v / 12;
+      buf.append(y);
+      buf.append('-');
+      m = v % 12;
+      number(buf, m, 2);
+      break;
+    case MONTH:
+      m = v;
+      buf.append(m);
+      break;
+    default:
+      throw new AssertionError(range);
+    }
+    return buf.toString();
+  }
+
+  public static StringBuilder number(StringBuilder buf, int v, int n) {
+    for (int k = digitCount(v); k < n; k++) {
+      buf.append('0');
+    }
+    return buf.append(v);
+  }
+
+  public static int digitCount(int v) {
+    for (int n = 1;; n++) {
+      v /= 10;
+      if (v == 0) {
+        return n;
+      }
+    }
+  }
+
+  private static int roundUp(int dividend, int divisor) {
+    int remainder = dividend % divisor;
+    dividend -= remainder;
+    if (remainder * 2 > divisor) {
+      dividend += divisor;
+    }
+    return dividend;
+  }
+
+  /** Cheap, unsafe, long power. power(2, 3) returns 8. */
+  public static long powerX(long a, long b) {
+    long x = 1;
+    while (b > 0) {
+      x *= a;
+      --b;
+    }
+    return x;
+  }
+
+  public static String intervalDayTimeToString(long v, TimeUnitRange range,
+      int scale) {
+    final StringBuilder buf = new StringBuilder();
+    if (v >= 0) {
+      buf.append('+');
+    } else {
+      buf.append('-');
+      v = -v;
+    }
+    final long ms;
+    final long s;
+    final long m;
+    final long h;
+    final long d;
+    switch (range) {
+    case DAY_TO_SECOND:
+      v = roundUp(v, powerX(10, 3 - scale));
+      ms = v % 1000;
+      v /= 1000;
+      s = v % 60;
+      v /= 60;
+      m = v % 60;
+      v /= 60;
+      h = v % 24;
+      v /= 24;
+      d = v;
+      buf.append((int) d);
+      buf.append(' ');
+      number(buf, (int) h, 2);
+      buf.append(':');
+      number(buf, (int) m, 2);
+      buf.append(':');
+      number(buf, (int) s, 2);
+      fraction(buf, scale, ms);
+      break;
+    case DAY_TO_MINUTE:
+      v = roundUp(v, 1000 * 60);
+      v /= 1000;
+      v /= 60;
+      m = v % 60;
+      v /= 60;
+      h = v % 24;
+      v /= 24;
+      d = v;
+      buf.append((int) d);
+      buf.append(' ');
+      number(buf, (int) h, 2);
+      buf.append(':');
+      number(buf, (int) m, 2);
+      break;
+    case DAY_TO_HOUR:
+      v = roundUp(v, 1000 * 60 * 60);
+      v /= 1000;
+      v /= 60;
+      v /= 60;
+      h = v % 24;
+      v /= 24;
+      d = v;
+      buf.append((int) d);
+      buf.append(' ');
+      number(buf, (int) h, 2);
+      break;
+    case DAY:
+      v = roundUp(v, 1000 * 60 * 60 * 24);
+      d = v / (1000 * 60 * 60 * 24);
+      buf.append((int) d);
+      break;
+    case HOUR:
+      v = roundUp(v, 1000 * 60 * 60);
+      v /= 1000;
+      v /= 60;
+      v /= 60;
+      h = v;
+      buf.append((int) h);
+      break;
+    case HOUR_TO_MINUTE:
+      v = roundUp(v, 1000 * 60);
+      v /= 1000;
+      v /= 60;
+      m = v % 60;
+      v /= 60;
+      h = v;
+      buf.append((int) h);
+      buf.append(':');
+      number(buf, (int) m, 2);
+      break;
+    case HOUR_TO_SECOND:
+      v = roundUp(v, powerX(10, 3 - scale));
+      ms = v % 1000;
+      v /= 1000;
+      s = v % 60;
+      v /= 60;
+      m = v % 60;
+      v /= 60;
+      h = v;
+      buf.append((int) h);
+      buf.append(':');
+      number(buf, (int) m, 2);
+      buf.append(':');
+      number(buf, (int) s, 2);
+      fraction(buf, scale, ms);
+      break;
+    case MINUTE_TO_SECOND:
+      v = roundUp(v, powerX(10, 3 - scale));
+      ms = v % 1000;
+      v /= 1000;
+      s = v % 60;
+      v /= 60;
+      m = v;
+      buf.append((int) m);
+      buf.append(':');
+      number(buf, (int) s, 2);
+      fraction(buf, scale, ms);
+      break;
+    case MINUTE:
+      v = roundUp(v, 1000 * 60);
+      v /= 1000;
+      v /= 60;
+      m = v;
+      buf.append((int) m);
+      break;
+    case SECOND:
+      v = roundUp(v, powerX(10, 3 - scale));
+      ms = v % 1000;
+      v /= 1000;
+      s = v;
+      buf.append((int) s);
+      fraction(buf, scale, ms);
+      break;
+    default:
+      throw new AssertionError(range);
+    }
+    return buf.toString();
+  }
+
+  /**
+   * Rounds a dividend to the nearest divisor.
+   * For example roundUp(31, 10) yields 30; roundUp(37, 10) yields 40.
+   * @param dividend Number to be divided
+   * @param divisor Number to divide by
+   * @return Rounded dividend
+   */
+  private static long roundUp(long dividend, long divisor) {
+    long remainder = dividend % divisor;
+    dividend -= remainder;
+    if (remainder * 2 > divisor) {
+      dividend += divisor;
+    }
+    return dividend;
+  }
+
+  private static void fraction(StringBuilder buf, int scale, long ms) {
+    if (scale > 0) {
+      buf.append('.');
+      long v1 = scale == 3 ? ms
+          : scale == 2 ? ms / 10
+          : scale == 1 ? ms / 100
+            : 0;
+      number(buf, (int) v1, scale);
+    }
+  }
+
+  public static int dateStringToUnixDate(String s) {
+    int hyphen1 = s.indexOf('-');
+    int y;
+    int m;
+    int d;
+    if (hyphen1 < 0) {
+      y = Integer.parseInt(s.trim());
+      m = 1;
+      d = 1;
+    } else {
+      y = Integer.parseInt(s.substring(0, hyphen1).trim());
+      final int hyphen2 = s.indexOf('-', hyphen1 + 1);
+      if (hyphen2 < 0) {
+        m = Integer.parseInt(s.substring(hyphen1 + 1).trim());
+        d = 1;
+      } else {
+        m = Integer.parseInt(s.substring(hyphen1 + 1, hyphen2).trim());
+        d = Integer.parseInt(s.substring(hyphen2 + 1).trim());
+      }
+    }
+    return ymdToUnixDate(y, m, d);
+  }
+
+  public static int timeStringToUnixDate(String v) {
+    return timeStringToUnixDate(v, 0);
+  }
+
+  public static int timeStringToUnixDate(String v, int start) {
+    final int colon1 = v.indexOf(':', start);
+    int hour;
+    int minute;
+    int second;
+    int milli;
+    if (colon1 < 0) {
+      hour = Integer.parseInt(v.trim());
+      minute = 1;
+      second = 1;
+      milli = 0;
+    } else {
+      hour = Integer.parseInt(v.substring(start, colon1).trim());
+      final int colon2 = v.indexOf(':', colon1 + 1);
+      if (colon2 < 0) {
+        minute = Integer.parseInt(v.substring(colon1 + 1).trim());
+        second = 1;
+        milli = 0;
+      } else {
+        minute = Integer.parseInt(v.substring(colon1 + 1, colon2).trim());
+        int dot = v.indexOf('.', colon2);
+        if (dot < 0) {
+          second = Integer.parseInt(v.substring(colon2 + 1).trim());
+          milli = 0;
+        } else {
+          second = Integer.parseInt(v.substring(colon2 + 1, dot).trim());
+          milli = Integer.parseInt(v.substring(dot + 1).trim());
+        }
+      }
+    }
+    return hour * (int) MILLIS_PER_HOUR
+        + minute * (int) MILLIS_PER_MINUTE
+        + second * (int) MILLIS_PER_SECOND
+        + milli;
+  }
+
+  public static long timestampStringToUnixDate(String s) {
+    final long d;
+    final long t;
+    s = s.trim();
+    int space = s.indexOf(' ');
+    if (space >= 0) {
+      d = dateStringToUnixDate(s.substring(0, space));
+      t = timeStringToUnixDate(s, space + 1);
+    } else {
+      d = dateStringToUnixDate(s);
+      t = 0;
+    }
+    return d * MILLIS_PER_DAY + t;
+  }
+
+  public static long unixDateExtract(TimeUnitRange range, long date) {
+    return julianExtract(range, (int) date + EPOCH_JULIAN);
+  }
+
+  private static int julianExtract(TimeUnitRange range, int julian) {
+    // this shifts the epoch back to astronomical year -4800 instead of the
+    // start of the Christian era in year AD 1 of the proleptic Gregorian
+    // calendar.
+    int j = julian + 32044;
+    int g = j / 146097;
+    int dg = j % 146097;
+    int c = (dg / 36524 + 1) * 3 / 4;
+    int dc = dg - c * 36524;
+    int b = dc / 1461;
+    int db = dc % 1461;
+    int a = (db / 365 + 1) * 3 / 4;
+    int da = db - a * 365;
+
+    // integer number of full years elapsed since March 1, 4801 BC
+    int y = g * 400 + c * 100 + b * 4 + a;
+    // integer number of full months elapsed since the last March 1
+    int m = (da * 5 + 308) / 153 - 2;
+    // number of days elapsed since day 1 of the month
+    int d = da - (m + 4) * 153 / 5 + 122;
+    int year = y - 4800 + (m + 2) / 12;
+    int month = (m + 2) % 12 + 1;
+    int day = d + 1;
+    switch (range) {
+    case YEAR:
+      return year;
+    case MONTH:
+      return month;
+    case DAY:
+      return day;
+    default:
+      throw new AssertionError(range);
+    }
+  }
+
+  public static int ymdToUnixDate(int year, int month, int day) {
+    final int julian = ymdToJulian(year, month, day);
+    return julian - EPOCH_JULIAN;
+  }
+
+  public static int ymdToJulian(int year, int month, int day) {
+    int a = (14 - month) / 12;
+    int y = year + 4800 - a;
+    int m = month + 12 * a - 3;
+    int j = day + (153 * m + 2) / 5
+        + 365 * y
+        + y / 4
+        - y / 100
+        + y / 400
+        - 32045;
+    if (j < 2299161) {
+      j = day + (153 * m + 2) / 5 + 365 * y + y / 4 - 32083;
+    }
+    return j;
+  }
+
+  //~ Inner Classes ----------------------------------------------------------
+
+  /**
+   * Helper class for {@link DateTimeUtils#parsePrecisionDateTimeLiteral}
+   */
+  public static class PrecisionTime {
+    private final Calendar cal;
+    private final int precision;
+
+    public PrecisionTime(Calendar cal, int precision) {
+      this.cal = cal;
+      this.precision = precision;
+    }
+
+    public Calendar getCalendar() {
+      return cal;
+    }
+
+    public int getPrecision() {
+      return precision;
+    }
+  }
+}
+
+// End DateTimeUtils.java

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/94752a60/avatica/src/main/java/org/apache/calcite/avatica/util/IteratorCursor.java
----------------------------------------------------------------------
diff --git a/avatica/src/main/java/org/apache/calcite/avatica/util/IteratorCursor.java b/avatica/src/main/java/org/apache/calcite/avatica/util/IteratorCursor.java
new file mode 100644
index 0000000..781f6c6
--- /dev/null
+++ b/avatica/src/main/java/org/apache/calcite/avatica/util/IteratorCursor.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.avatica.util;
+
+import java.io.Closeable;
+import java.io.IOException;
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+
+/**
+ * Implementation of {@link org.apache.calcite.avatica.util.Cursor}
+ * on top of an {@link Iterator} that
+ * returns a record for each row. The returned record is cached to avoid
+ * multiple computations of current row.
+ *
+ * @param <E> Element type
+ */
+public abstract class IteratorCursor<E> extends PositionedCursor<E> {
+  private Position position = Position.BEFORE_START;
+  private final Iterator<E> iterator;
+  private E current = null;
+
+  /**
+   * Creates an {@code IteratorCursor}.
+   *
+   * @param iterator input iterator
+   */
+  protected IteratorCursor(Iterator<E> iterator) {
+    this.iterator = iterator;
+  }
+
+  public boolean next() {
+    if (iterator.hasNext()) {
+      current = iterator.next();
+      position = Position.OK;
+      return true;
+    }
+    current = null;
+    position = Position.AFTER_END;
+    return false;
+  }
+
+  public void close() {
+    current = null;
+    position = Position.CLOSED;
+    if (iterator instanceof Closeable) {
+      try {
+        ((Closeable) iterator).close();
+      } catch (IOException e) {
+        throw new RuntimeException(e);
+      }
+    }
+  }
+
+  protected E current() {
+    if (position != Position.OK) {
+      throw new NoSuchElementException();
+    }
+    return current;
+  }
+
+  /** Are we positioned on a valid row? */
+  private enum Position {
+    CLOSED,
+    BEFORE_START,
+    OK,
+    AFTER_END
+  }
+}
+
+// End IteratorCursor.java

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/94752a60/avatica/src/main/java/org/apache/calcite/avatica/util/ListIteratorCursor.java
----------------------------------------------------------------------
diff --git a/avatica/src/main/java/org/apache/calcite/avatica/util/ListIteratorCursor.java b/avatica/src/main/java/org/apache/calcite/avatica/util/ListIteratorCursor.java
new file mode 100644
index 0000000..e2801ec
--- /dev/null
+++ b/avatica/src/main/java/org/apache/calcite/avatica/util/ListIteratorCursor.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.avatica.util;
+
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * Implementation of {@link Cursor} on top of an
+ * {@link java.util.Iterator} that
+ * returns a {@link List} for each row.
+ */
+public class ListIteratorCursor extends IteratorCursor<List<Object>> {
+
+  /**
+   * Creates a RecordEnumeratorCursor.
+   *
+   * @param iterator Iterator
+   */
+  public ListIteratorCursor(Iterator<List<Object>> iterator) {
+    super(iterator);
+  }
+
+  protected Getter createGetter(int ordinal) {
+    return new ListGetter(ordinal);
+  }
+}
+
+// End ListIteratorCursor.java

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/94752a60/avatica/src/main/java/org/apache/calcite/avatica/util/MapIteratorCursor.java
----------------------------------------------------------------------
diff --git a/avatica/src/main/java/org/apache/calcite/avatica/util/MapIteratorCursor.java b/avatica/src/main/java/org/apache/calcite/avatica/util/MapIteratorCursor.java
new file mode 100644
index 0000000..9ab6c9c
--- /dev/null
+++ b/avatica/src/main/java/org/apache/calcite/avatica/util/MapIteratorCursor.java
@@ -0,0 +1,51 @@
+/*
+ * 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.avatica.util;
+
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Implementation of {@link Cursor} on top of an
+ * {@link java.util.Iterator} that
+ * returns a {@link Map} for each row.
+ *
+ * <p>The Map contains (field, value) pairs.
+ */
+public class MapIteratorCursor extends IteratorCursor<Map<String, Object>> {
+  private final List<String> fieldNames;
+
+  /**
+   * Creates a MapIteratorCursor.
+   *
+   * @param iterator Iterator
+   * @param fieldNames Field names to project
+   */
+  public MapIteratorCursor(Iterator<Map<String, Object>> iterator,
+      List<String> fieldNames) {
+    super(iterator);
+    assert fieldNames != null;
+    this.fieldNames = fieldNames;
+  }
+
+  protected Getter createGetter(int ordinal) {
+    return new MapGetter<String>(fieldNames.get(ordinal));
+  }
+}
+
+// End MapIteratorCursor.java

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/94752a60/avatica/src/main/java/org/apache/calcite/avatica/util/PositionedCursor.java
----------------------------------------------------------------------
diff --git a/avatica/src/main/java/org/apache/calcite/avatica/util/PositionedCursor.java b/avatica/src/main/java/org/apache/calcite/avatica/util/PositionedCursor.java
new file mode 100644
index 0000000..f60f47d
--- /dev/null
+++ b/avatica/src/main/java/org/apache/calcite/avatica/util/PositionedCursor.java
@@ -0,0 +1,134 @@
+/*
+ * 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.avatica.util;
+
+import java.lang.reflect.Field;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Abstract implementation of {@link org.apache.calcite.avatica.util.Cursor}
+ * that caches its current row.
+ *
+ * @param <T> Element type
+ */
+public abstract class PositionedCursor<T> extends AbstractCursor {
+  /**
+   * Returns the current row.
+   *
+   * @return current row
+   *
+   * @throws java.util.NoSuchElementException if the iteration has no more
+   * elements
+   */
+  protected abstract T current();
+
+  /** Implementation of
+   * {@link org.apache.calcite.avatica.util.AbstractCursor.Getter}
+   * that reads from records that are arrays. */
+  protected class ArrayGetter extends AbstractGetter {
+    protected final int field;
+
+    public ArrayGetter(int field) {
+      this.field = field;
+    }
+
+    public Object getObject() {
+      Object o = ((Object[]) current())[field];
+      wasNull[0] = o == null;
+      return o;
+    }
+  }
+
+  /** Implementation of
+   * {@link org.apache.calcite.avatica.util.AbstractCursor.Getter}
+   * that reads items from a list. */
+  protected class ListGetter extends AbstractGetter {
+    protected final int index;
+
+    public ListGetter(int index) {
+      this.index = index;
+    }
+
+    public Object getObject() {
+      Object o = ((List) current()).get(index);
+      wasNull[0] = o == null;
+      return o;
+    }
+  }
+
+  /** Implementation of
+   * {@link org.apache.calcite.avatica.util.AbstractCursor.Getter}
+   * for records that consist of a single field.
+   *
+   * <p>Each record is represented as an object, and the value of the sole
+   * field is that object. */
+  protected class ObjectGetter extends AbstractGetter {
+    public ObjectGetter(int field) {
+      assert field == 0;
+    }
+
+    public Object getObject() {
+      Object o = current();
+      wasNull[0] = o == null;
+      return o;
+    }
+  }
+
+  /** Implementation of
+   * {@link org.apache.calcite.avatica.util.AbstractCursor.Getter}
+   * that reads fields via reflection. */
+  protected class FieldGetter extends AbstractGetter {
+    protected final Field field;
+
+    public FieldGetter(Field field) {
+      this.field = field;
+    }
+
+    public Object getObject() {
+      Object o;
+      try {
+        o = field.get(current());
+      } catch (IllegalAccessException e) {
+        throw new RuntimeException(e);
+      }
+      wasNull[0] = o == null;
+      return o;
+    }
+  }
+
+  /** Implementation of
+   * {@link org.apache.calcite.avatica.util.AbstractCursor.Getter}
+   * that reads entries from a {@link java.util.Map}. */
+  protected class MapGetter<K> extends AbstractGetter {
+    protected final K key;
+
+    public MapGetter(K key) {
+      this.key = key;
+    }
+
+    public Object getObject() {
+      @SuppressWarnings("unchecked") final Map<K, Object> map =
+          (Map<K, Object>) current();
+      Object o = map.get(key);
+      wasNull[0] = o == null;
+      return o;
+    }
+  }
+}
+
+// End PositionedCursor.java

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/94752a60/avatica/src/main/java/org/apache/calcite/avatica/util/Quoting.java
----------------------------------------------------------------------
diff --git a/avatica/src/main/java/org/apache/calcite/avatica/util/Quoting.java b/avatica/src/main/java/org/apache/calcite/avatica/util/Quoting.java
new file mode 100644
index 0000000..855e4a6
--- /dev/null
+++ b/avatica/src/main/java/org/apache/calcite/avatica/util/Quoting.java
@@ -0,0 +1,37 @@
+/*
+ * 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.avatica.util;
+
+/** Syntax for quoting identifiers in SQL statements. */
+public enum Quoting {
+  /** Quote identifiers in double-quotes. For example, {@code "my id"}. */
+  DOUBLE_QUOTE("\""),
+
+  /** Quote identifiers in back-quotes. For example, {@code `my id`}. */
+  BACK_TICK("`"),
+
+  /** Quote identifiers in brackets. For example, {@code [my id]}. */
+  BRACKET("[");
+
+  public String string;
+
+  Quoting(String string) {
+    this.string = string;
+  }
+}
+
+// End Quoting.java

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/94752a60/avatica/src/main/java/org/apache/calcite/avatica/util/RecordIteratorCursor.java
----------------------------------------------------------------------
diff --git a/avatica/src/main/java/org/apache/calcite/avatica/util/RecordIteratorCursor.java b/avatica/src/main/java/org/apache/calcite/avatica/util/RecordIteratorCursor.java
new file mode 100644
index 0000000..717247d
--- /dev/null
+++ b/avatica/src/main/java/org/apache/calcite/avatica/util/RecordIteratorCursor.java
@@ -0,0 +1,63 @@
+/*
+ * 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.avatica.util;
+
+import java.lang.reflect.Field;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * Implementation of {@link org.apache.calcite.avatica.util.Cursor} on top of an
+ * {@link java.util.Iterator} that
+ * returns a record for each row. The record is a synthetic class whose fields
+ * are all public.
+ *
+ * @param <E> Element type
+ */
+public class RecordIteratorCursor<E> extends IteratorCursor<E> {
+  private final List<Field> fields;
+
+  /**
+   * Creates a RecordIteratorCursor.
+   *
+   * @param iterator Iterator
+   * @param clazz Element type
+   */
+  public RecordIteratorCursor(Iterator<E> iterator, Class<E> clazz) {
+    this(iterator, clazz, Arrays.asList(clazz.getFields()));
+  }
+
+  /**
+   * Creates a RecordIteratorCursor that projects particular fields.
+   *
+   * @param iterator Iterator
+   * @param clazz Element type
+   * @param fields Fields to project
+   */
+  public RecordIteratorCursor(Iterator<E> iterator, Class<E> clazz,
+      List<Field> fields) {
+    super(iterator);
+    this.fields = fields;
+  }
+
+  protected Getter createGetter(int ordinal) {
+    return new FieldGetter(fields.get(ordinal));
+  }
+}
+
+// End RecordIteratorCursor.java

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/94752a60/avatica/src/main/java/org/apache/calcite/avatica/util/Spacer.java
----------------------------------------------------------------------
diff --git a/avatica/src/main/java/org/apache/calcite/avatica/util/Spacer.java b/avatica/src/main/java/org/apache/calcite/avatica/util/Spacer.java
new file mode 100644
index 0000000..cc0a097
--- /dev/null
+++ b/avatica/src/main/java/org/apache/calcite/avatica/util/Spacer.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.avatica.util;
+
+/**
+ * Efficiently writes strings of spaces.
+ */
+public class Spacer {
+  private int n;
+
+  /** Creates a Spacer with zero spaces. */
+  public Spacer() {
+    this(0);
+  }
+
+  /** Creates a Spacer with a given number of spaces. */
+  public Spacer(int n) {
+    set(n);
+  }
+
+  /** Sets the current number of spaces. */
+  public Spacer set(int n) {
+    this.n = n;
+    return this;
+  }
+
+  /** Returns the current number of spaces. */
+  public int get() {
+    return n;
+  }
+
+  /** Increases the current number of spaces by {@code n}. */
+  public Spacer add(int n) {
+    return set(this.n + n);
+  }
+
+  /** Reduces the current number of spaces by {@code n}. */
+  public Spacer subtract(int n) {
+    return set(this.n - n);
+  }
+
+  /** Returns a string of the current number of spaces. */
+  public String toString() {
+    return Spaces.of(n);
+  }
+
+  /** Appends current number of spaces to a {@link StringBuilder}. */
+  public StringBuilder spaces(StringBuilder buf) {
+    return Spaces.append(buf, n);
+  }
+
+  /** Returns a string that is padded on the right with spaces to the current
+   * length. */
+  public String padRight(String string) {
+    Spaces.padRight(string, n);
+    final int x = n - string.length();
+    if (x <= 0) {
+      return string;
+    }
+    // Replacing StringBuffer with String would hurt performance.
+    //noinspection StringBufferReplaceableByString
+    return Spaces.append(new StringBuilder(string), x).toString();
+  }
+}
+
+// End Spacer.java

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/94752a60/avatica/src/main/java/org/apache/calcite/avatica/util/Spaces.java
----------------------------------------------------------------------
diff --git a/avatica/src/main/java/org/apache/calcite/avatica/util/Spaces.java b/avatica/src/main/java/org/apache/calcite/avatica/util/Spaces.java
new file mode 100644
index 0000000..6469400
--- /dev/null
+++ b/avatica/src/main/java/org/apache/calcite/avatica/util/Spaces.java
@@ -0,0 +1,185 @@
+/*
+ * 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.avatica.util;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.AbstractList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+/** Utilities for creating strings of spaces. */
+public class Spaces {
+  /** It doesn't look like this list is ever updated. But it is - when a call to
+   * to {@link SpaceList#get} causes an {@link IndexOutOfBoundsException}. */
+  @SuppressWarnings("MismatchedQueryAndUpdateOfCollection")
+  private static final List<String> SPACE_LIST = new SpaceList();
+
+  /** The longest possible string of spaces. Fine as long as you don't try
+   * to print it.
+   *
+   * <p>Use with {@link StringBuilder#append(CharSequence, int, int)} to
+   * append spaces without doing memory allocation.</p>
+   */
+  public static final CharSequence MAX = sequence(Integer.MAX_VALUE);
+
+  // Utility class. Do not instantiate.
+  private Spaces() {}
+
+  /** Creates a sequence of {@code n} spaces. */
+  public static CharSequence sequence(int n) {
+    return new SpaceString(n);
+  }
+
+  /** Returns a string of {@code n} spaces. */
+  public static String of(int n) {
+    return SPACE_LIST.get(n);
+  }
+
+  /** Appends {@code n} spaces to an {@link Appendable}. */
+  public static Appendable append(Appendable buf, int n) throws IOException {
+    buf.append(MAX, 0, n);
+    return buf;
+  }
+
+  /** Appends {@code n} spaces to a {@link PrintWriter}. */
+  public static PrintWriter append(PrintWriter pw, int n) {
+    pw.append(MAX, 0, n);
+    return pw;
+  }
+
+  /** Appends {@code n} spaces to a {@link StringWriter}. */
+  public static StringWriter append(StringWriter pw, int n) {
+    pw.append(MAX, 0, n);
+    return pw;
+  }
+
+  /** Appends {@code n} spaces to a {@link StringBuilder}. */
+  public static StringBuilder append(StringBuilder buf, int n) {
+    buf.append(MAX, 0, n);
+    return buf;
+  }
+
+  /** Appends {@code n} spaces to a {@link StringBuffer}. */
+  public static StringBuffer append(StringBuffer buf, int n) {
+    buf.append(MAX, 0, n);
+    return buf;
+  }
+
+  /** Returns a string that is padded on the right with spaces to the given
+   * length. */
+  public static String padRight(String string, int n) {
+    final int x = n - string.length();
+    if (x <= 0) {
+      return string;
+    }
+    // Replacing StringBuffer with String would hurt performance.
+    //noinspection StringBufferReplaceableByString
+    return append(new StringBuilder(string), x).toString();
+  }
+
+  /** Returns a string that is padded on the left with spaces to the given
+   * length. */
+  public static String padLeft(String string, int n) {
+    final int x = n - string.length();
+    if (x <= 0) {
+      return string;
+    }
+    // Replacing StringBuffer with String would hurt performance.
+    //noinspection StringBufferReplaceableByString
+    return append(new StringBuilder(), x).append(string).toString();
+  }
+
+  /** A string of spaces. */
+  private static class SpaceString implements CharSequence {
+    private final int length;
+
+    private SpaceString(int length) {
+      this.length = length;
+    }
+
+    // Do not override equals and hashCode to be like String. CharSequence does
+    // not require it.
+
+    @SuppressWarnings("NullableProblems")
+    @Override public String toString() {
+      return of(length);
+    }
+
+    public int length() {
+      return length;
+    }
+
+    public char charAt(int index) {
+      return ' ';
+    }
+
+    public CharSequence subSequence(int start, int end) {
+      return new SpaceString(end - start);
+    }
+  }
+
+  /** List whose {@code i}th entry is a string consisting of {@code i} spaces.
+   * It populates itself the first time you ask for a particular string, and
+   * caches the result. */
+  private static class SpaceList extends CopyOnWriteArrayList<String> {
+    @Override public String get(int index) {
+      for (;;) {
+        try {
+          return super.get(index);
+        } catch (IndexOutOfBoundsException e) {
+          if (index < 0) {
+            throw e;
+          }
+          populate(Math.max(16, index + 1));
+        }
+      }
+    }
+
+    /**
+     * Populates this list with all prefix strings of a given string. All
+     * of the prefix strings share the same backing array of chars.
+     */
+    private synchronized void populate(int newSize) {
+      final int size = size();
+      if (newSize <= size) {
+        return;
+      }
+      final char[] chars = new char[newSize];
+      Arrays.fill(chars, ' ');
+      final int length = newSize - size;
+      final int offset = size;
+
+      // addAll is much more efficient than repeated add for
+      // CopyOnWriteArrayList
+      addAll(
+          new AbstractList<String>() {
+            public String get(int index) {
+              return new String(chars, 0, offset + index);
+            }
+
+            public int size() {
+              return length;
+            }
+          });
+    }
+  }
+}
+
+// End Spaces.java

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/94752a60/avatica/src/main/java/org/apache/calcite/avatica/util/TimeUnit.java
----------------------------------------------------------------------
diff --git a/avatica/src/main/java/org/apache/calcite/avatica/util/TimeUnit.java b/avatica/src/main/java/org/apache/calcite/avatica/util/TimeUnit.java
new file mode 100644
index 0000000..c9a6d42
--- /dev/null
+++ b/avatica/src/main/java/org/apache/calcite/avatica/util/TimeUnit.java
@@ -0,0 +1,70 @@
+/*
+ * 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.avatica.util;
+
+import java.math.BigDecimal;
+
+/**
+ * Enumeration of time units used to construct an interval.
+ */
+public enum TimeUnit {
+  YEAR(true, ' ', 12 /* months */, null),
+  MONTH(true, '-', 1 /* months */, BigDecimal.valueOf(12)),
+  DAY(false, '-', DateTimeUtils.MILLIS_PER_DAY, null),
+  HOUR(false, ' ', DateTimeUtils.MILLIS_PER_HOUR, BigDecimal.valueOf(24)),
+  MINUTE(false, ':', DateTimeUtils.MILLIS_PER_MINUTE, BigDecimal.valueOf(60)),
+  SECOND(false, ':', DateTimeUtils.MILLIS_PER_SECOND, BigDecimal.valueOf(60));
+
+  public final boolean yearMonth;
+  public final char separator;
+  public final long multiplier;
+  private final BigDecimal limit;
+
+  private static final TimeUnit[] CACHED_VALUES = values();
+
+  TimeUnit(boolean yearMonth, char separator, long multiplier,
+      BigDecimal limit) {
+    this.yearMonth = yearMonth;
+    this.separator = separator;
+    this.multiplier = multiplier;
+    this.limit = limit;
+  }
+
+  /**
+   * Returns the TimeUnit associated with an ordinal. The value returned
+   * is null if the ordinal is not a member of the TimeUnit enumeration.
+   */
+  public static TimeUnit getValue(int ordinal) {
+    return ordinal < 0 || ordinal >= CACHED_VALUES.length
+        ? null
+        : CACHED_VALUES[ordinal];
+  }
+
+  /**
+   * Returns whether a given value is valid for a field of this time unit.
+   *
+   * @param field Field value
+   * @return Whether value
+   */
+  public boolean isValidValue(BigDecimal field) {
+    return field.compareTo(BigDecimal.ZERO) >= 0
+        && (limit == null
+        || field.compareTo(limit) < 0);
+  }
+}
+
+// End TimeUnit.java

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/94752a60/avatica/src/main/java/org/apache/calcite/avatica/util/TimeUnitRange.java
----------------------------------------------------------------------
diff --git a/avatica/src/main/java/org/apache/calcite/avatica/util/TimeUnitRange.java b/avatica/src/main/java/org/apache/calcite/avatica/util/TimeUnitRange.java
new file mode 100644
index 0000000..5f91811
--- /dev/null
+++ b/avatica/src/main/java/org/apache/calcite/avatica/util/TimeUnitRange.java
@@ -0,0 +1,111 @@
+/*
+ * 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.avatica.util;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+/** A range of time units. The first is more significant than the
+ * other (e.g. year-to-day) or the same as the other (e.g. month). */
+public enum TimeUnitRange {
+  YEAR(TimeUnit.YEAR, null),
+  YEAR_TO_MONTH(TimeUnit.YEAR, TimeUnit.MONTH),
+  MONTH(TimeUnit.MONTH, null),
+  DAY(TimeUnit.DAY, null),
+  DAY_TO_HOUR(TimeUnit.DAY, TimeUnit.HOUR),
+  DAY_TO_MINUTE(TimeUnit.DAY, TimeUnit.MINUTE),
+  DAY_TO_SECOND(TimeUnit.DAY, TimeUnit.SECOND),
+  HOUR(TimeUnit.HOUR, null),
+  HOUR_TO_MINUTE(TimeUnit.HOUR, TimeUnit.MINUTE),
+  HOUR_TO_SECOND(TimeUnit.HOUR, TimeUnit.SECOND),
+  MINUTE(TimeUnit.MINUTE, null),
+  MINUTE_TO_SECOND(TimeUnit.MINUTE, TimeUnit.SECOND),
+  SECOND(TimeUnit.SECOND, null);
+
+  public final TimeUnit startUnit;
+  public final TimeUnit endUnit;
+
+  private static final Map<Pair<TimeUnit>, TimeUnitRange> MAP = createMap();
+
+  /**
+   * Creates a TimeUnitRange.
+   *
+   * @param startUnit Start time unit
+   * @param endUnit   End time unit
+   */
+  TimeUnitRange(TimeUnit startUnit, TimeUnit endUnit) {
+    assert startUnit != null;
+    this.startUnit = startUnit;
+    this.endUnit = endUnit;
+  }
+
+  /**
+   * Returns a {@code TimeUnitRange} with a given start and end unit.
+   *
+   * @param startUnit Start unit
+   * @param endUnit   End unit
+   * @return Time unit range, or null if not valid
+   */
+  public static TimeUnitRange of(TimeUnit startUnit, TimeUnit endUnit) {
+    return MAP.get(new Pair<TimeUnit>(startUnit, endUnit));
+  }
+
+  private static Map<Pair<TimeUnit>, TimeUnitRange> createMap() {
+    Map<Pair<TimeUnit>, TimeUnitRange> map =
+        new HashMap<Pair<TimeUnit>, TimeUnitRange>();
+    for (TimeUnitRange value : values()) {
+      map.put(new Pair<TimeUnit>(value.startUnit, value.endUnit), value);
+    }
+    return Collections.unmodifiableMap(map);
+  }
+
+  /** Whether this is in the YEAR-TO-MONTH family of intervals. */
+  public boolean monthly() {
+    return ordinal() <= MONTH.ordinal();
+  }
+
+  /** Immutable pair of values of the same type. */
+  private static class Pair<E> {
+    final E left;
+    final E right;
+
+    private Pair(E left, E right) {
+      this.left = left;
+      this.right = right;
+    }
+
+    @Override public int hashCode() {
+      int k = (left == null) ? 0 : left.hashCode();
+      int k1 = (right == null) ? 0 : right.hashCode();
+      return ((k << 4) | k) ^ k1;
+    }
+
+    @Override public boolean equals(Object obj) {
+      return obj == this
+          || obj instanceof Pair
+          && equals(left, ((Pair) obj).left)
+          && equals(right, ((Pair) obj).right);
+    }
+
+    private static boolean equals(Object a, Object b) {
+      return (a == b) || (a != null && a.equals(b));
+    }
+  }
+}
+
+// End TimeUnitRange.java

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/94752a60/avatica/src/main/java/org/apache/calcite/avatica/util/package-info.java
----------------------------------------------------------------------
diff --git a/avatica/src/main/java/org/apache/calcite/avatica/util/package-info.java b/avatica/src/main/java/org/apache/calcite/avatica/util/package-info.java
new file mode 100644
index 0000000..f351e67
--- /dev/null
+++ b/avatica/src/main/java/org/apache/calcite/avatica/util/package-info.java
@@ -0,0 +1,23 @@
+/*
+ * 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.
+ */
+
+/**
+ * Avatica utilities.
+ */
+package org.apache.calcite.avatica.util;
+
+// End package-info.java

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/94752a60/avatica/src/main/resources/META-INF/services/java.sql.Driver
----------------------------------------------------------------------
diff --git a/avatica/src/main/resources/META-INF/services/java.sql.Driver b/avatica/src/main/resources/META-INF/services/java.sql.Driver
new file mode 100644
index 0000000..beb1ac0
--- /dev/null
+++ b/avatica/src/main/resources/META-INF/services/java.sql.Driver
@@ -0,0 +1 @@
+org.apache.calcite.avatica.remote.Driver

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/94752a60/avatica/src/test/java/org/apache/calcite/avatica/test/AvaticaSuite.java
----------------------------------------------------------------------
diff --git a/avatica/src/test/java/org/apache/calcite/avatica/test/AvaticaSuite.java b/avatica/src/test/java/org/apache/calcite/avatica/test/AvaticaSuite.java
index fcb3f16..3f25225 100644
--- a/avatica/src/test/java/org/apache/calcite/avatica/test/AvaticaSuite.java
+++ b/avatica/src/test/java/org/apache/calcite/avatica/test/AvaticaSuite.java
@@ -24,7 +24,8 @@ import org.junit.runners.Suite;
  */
 @RunWith(Suite.class)
 @Suite.SuiteClasses({
-    ConnectStringParserTest.class
+    ConnectStringParserTest.class,
+    RemoteDriverTest.class
 })
 public class AvaticaSuite {
 }

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/94752a60/avatica/src/test/java/org/apache/calcite/avatica/test/RemoteDriverTest.java
----------------------------------------------------------------------
diff --git a/avatica/src/test/java/org/apache/calcite/avatica/test/RemoteDriverTest.java b/avatica/src/test/java/org/apache/calcite/avatica/test/RemoteDriverTest.java
new file mode 100644
index 0000000..5cf1ea5
--- /dev/null
+++ b/avatica/src/test/java/org/apache/calcite/avatica/test/RemoteDriverTest.java
@@ -0,0 +1,86 @@
+/*
+ * 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.avatica.test;
+
+import org.apache.calcite.avatica.remote.LocalJsonService;
+import org.apache.calcite.avatica.remote.MockJsonService;
+
+import org.junit.Ignore;
+import org.junit.Test;
+
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.ResultSet;
+import java.sql.ResultSetMetaData;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertThat;
+
+/**
+ * Unit test for Avatica Remote JDBC driver.
+ */
+public class RemoteDriverTest {
+  public static final String MJS =
+      MockJsonService.Factory.class.getName();
+
+  public static final String LJS =
+      LocalJsonService.Factory.class.getName();
+
+  @Test public void testRegister() throws Exception {
+    final Connection connection =
+        DriverManager.getConnection("jdbc:avatica:remote:");
+    assertThat(connection.isClosed(), is(false));
+    connection.close();
+    assertThat(connection.isClosed(), is(true));
+  }
+
+  @Ignore
+  @Test public void testNoFactory() throws Exception {
+    final Connection connection =
+        DriverManager.getConnection("jdbc:avatica:remote:");
+    assertThat(connection.isClosed(), is(false));
+    final ResultSet resultSet = connection.getMetaData().getSchemas();
+    assertFalse(resultSet.next());
+    final ResultSetMetaData metaData = resultSet.getMetaData();
+    assertEquals(2, metaData.getColumnCount());
+    assertEquals("TABLE_SCHEM", metaData.getColumnName(1));
+    assertEquals("TABLE_CATALOG", metaData.getColumnName(2));
+    resultSet.close();
+    connection.close();
+    assertThat(connection.isClosed(), is(true));
+  }
+
+  @Ignore
+  @Test public void testCatalogsMock() throws Exception {
+    final Connection connection =
+        DriverManager.getConnection("jdbc:avatica:remote:factory=" + MJS);
+    assertThat(connection.isClosed(), is(false));
+    final ResultSet resultSet = connection.getMetaData().getSchemas();
+    assertFalse(resultSet.next());
+    final ResultSetMetaData metaData = resultSet.getMetaData();
+    assertEquals(2, metaData.getColumnCount());
+    assertEquals("TABLE_SCHEM", metaData.getColumnName(1));
+    assertEquals("TABLE_CATALOG", metaData.getColumnName(2));
+    resultSet.close();
+    connection.close();
+    assertThat(connection.isClosed(), is(true));
+  }
+}
+
+// End RemoteDriverTest.java

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/94752a60/core/pom.xml
----------------------------------------------------------------------
diff --git a/core/pom.xml b/core/pom.xml
index 1c5527c..441d8d2 100644
--- a/core/pom.xml
+++ b/core/pom.xml
@@ -46,6 +46,11 @@ limitations under the License.
       <artifactId>calcite-linq4j</artifactId>
     </dependency>
     <dependency>
+      <groupId>org.apache.calcite</groupId>
+      <artifactId>calcite-avatica-server</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
       <groupId>org.pentaho</groupId>
       <artifactId>pentaho-aggdesigner-algorithm</artifactId>
     </dependency>

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/94752a60/core/src/main/codegen/templates/Parser.jj
----------------------------------------------------------------------
diff --git a/core/src/main/codegen/templates/Parser.jj b/core/src/main/codegen/templates/Parser.jj
index 670608e..23c897e 100644
--- a/core/src/main/codegen/templates/Parser.jj
+++ b/core/src/main/codegen/templates/Parser.jj
@@ -34,7 +34,9 @@ import ${importStr};
 </#list>
 
 
-import org.apache.calcite.avatica.Casing;
+import org.apache.calcite.avatica.util.Casing;
+import org.apache.calcite.avatica.util.DateTimeUtils;
+import org.apache.calcite.avatica.util.TimeUnit;
 import org.apache.calcite.rel.type.RelDataType;
 import org.apache.calcite.runtime.CalciteContextException;
 import org.apache.calcite.sql.JoinConditionType;
@@ -88,7 +90,6 @@ import org.apache.calcite.sql.parser.SqlParserPos;
 import org.apache.calcite.sql.parser.SqlParserUtil;
 import org.apache.calcite.sql.type.SqlTypeName;
 import org.apache.calcite.util.trace.CalciteTrace;
-import org.apache.calcite.util.DateTimeUtil;
 
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Lists;
@@ -400,38 +401,38 @@ JAVACODE void checkNonQueryExpression(ExprContext exprContext)
 
 JAVACODE SqlDateLiteral parseDateLiteral(String s, SqlParserPos pos) {
     String dateStr = SqlParserUtil.parseString(s);
-    Calendar cal = DateTimeUtil.parseDateFormat(
-        dateStr, DateTimeUtil.DATE_FORMAT_STRING, DateTimeUtil.GMT_ZONE);
+    Calendar cal = DateTimeUtils.parseDateFormat(
+        dateStr, DateTimeUtils.DATE_FORMAT_STRING, DateTimeUtils.GMT_ZONE);
     if (null == cal) {
         throw SqlUtil.newContextException(pos,
             RESOURCE.illegalLiteral("DATE", s,
-                RESOURCE.badFormat(DateTimeUtil.DATE_FORMAT_STRING).str()));
+                RESOURCE.badFormat(DateTimeUtils.DATE_FORMAT_STRING).str()));
     }
     return SqlLiteral.createDate(cal, pos);
 }
 
 JAVACODE SqlTimeLiteral parseTimeLiteral(String s, SqlParserPos pos) {
     String dateStr = SqlParserUtil.parseString(s);
-    DateTimeUtil.PrecisionTime pt =
-    DateTimeUtil.parsePrecisionDateTimeLiteral(
-        dateStr, DateTimeUtil.TIME_FORMAT_STRING, DateTimeUtil.GMT_ZONE);
+    DateTimeUtils.PrecisionTime pt =
+    DateTimeUtils.parsePrecisionDateTimeLiteral(
+        dateStr, DateTimeUtils.TIME_FORMAT_STRING, DateTimeUtils.GMT_ZONE);
     if (null == pt) {
         throw SqlUtil.newContextException(pos,
             RESOURCE.illegalLiteral("TIME", s,
-                RESOURCE.badFormat(DateTimeUtil.TIME_FORMAT_STRING).str()));
+                RESOURCE.badFormat(DateTimeUtils.TIME_FORMAT_STRING).str()));
     }
     return SqlLiteral.createTime(pt.getCalendar(), pt.getPrecision(), pos);
 }
 
 JAVACODE SqlTimestampLiteral parseTimestampLiteral(String s, SqlParserPos pos) {
     String dateStr = SqlParserUtil.parseString(s);
-    DateTimeUtil.PrecisionTime pt =
-    DateTimeUtil.parsePrecisionDateTimeLiteral(
-        dateStr, DateTimeUtil.TIMESTAMP_FORMAT_STRING, DateTimeUtil.GMT_ZONE);
+    DateTimeUtils.PrecisionTime pt =
+    DateTimeUtils.parsePrecisionDateTimeLiteral(
+        dateStr, DateTimeUtils.TIMESTAMP_FORMAT_STRING, DateTimeUtils.GMT_ZONE);
     if (null == pt) {
         throw SqlUtil.newContextException(pos,
             RESOURCE.illegalLiteral("TIMESTAMP", s,
-                RESOURCE.badFormat(DateTimeUtil.TIMESTAMP_FORMAT_STRING).str()));
+                RESOURCE.badFormat(DateTimeUtils.TIMESTAMP_FORMAT_STRING).str()));
     }
     return SqlLiteral.createTimestamp(pt.getCalendar(), pt.getPrecision(), pos);
 }
@@ -3203,8 +3204,8 @@ SqlLiteral IntervalLiteral() :
 
 SqlIntervalQualifier IntervalQualifier() :
 {
-    SqlIntervalQualifier.TimeUnit start;
-    SqlIntervalQualifier.TimeUnit end = null;
+    TimeUnit start;
+    TimeUnit end = null;
     int startPrec = RelDataType.PRECISION_NOT_SPECIFIED;
     int secondFracPrec = RelDataType.PRECISION_NOT_SPECIFIED;
 }
@@ -3214,50 +3215,50 @@ SqlIntervalQualifier IntervalQualifier() :
         [
             LOOKAHEAD(2) <TO> <MONTH>
             {
-                end = SqlIntervalQualifier.TimeUnit.MONTH;
+                end = TimeUnit.MONTH;
             }
         ]
-        { start = SqlIntervalQualifier.TimeUnit.YEAR; }
+        { start = TimeUnit.YEAR; }
         |
         <MONTH> [ <LPAREN> startPrec = UnsignedIntLiteral() <RPAREN> ]
-        { start = SqlIntervalQualifier.TimeUnit.MONTH; }
+        { start = TimeUnit.MONTH; }
         |
         <DAY> [ <LPAREN> startPrec = UnsignedIntLiteral() <RPAREN> ]
         [ LOOKAHEAD(2) <TO>
             (
-                <HOUR> { end = SqlIntervalQualifier.TimeUnit.HOUR; }
-                | <MINUTE> { end = SqlIntervalQualifier.TimeUnit.MINUTE; }
-                | <SECOND> { end = SqlIntervalQualifier.TimeUnit.SECOND; }
+                <HOUR> { end = TimeUnit.HOUR; }
+                | <MINUTE> { end = TimeUnit.MINUTE; }
+                | <SECOND> { end = TimeUnit.SECOND; }
                 [ <LPAREN> secondFracPrec = UnsignedIntLiteral() <RPAREN> ]
             )
         ]
-        { start = SqlIntervalQualifier.TimeUnit.DAY; }
+        { start = TimeUnit.DAY; }
         |
         <HOUR> [ <LPAREN> startPrec = UnsignedIntLiteral() <RPAREN> ]
         [ LOOKAHEAD(2) <TO>
             (
-                <MINUTE> { end = SqlIntervalQualifier.TimeUnit.MINUTE; }
-                | <SECOND> { end = SqlIntervalQualifier.TimeUnit.SECOND; }
+                <MINUTE> { end = TimeUnit.MINUTE; }
+                | <SECOND> { end = TimeUnit.SECOND; }
                 [ <LPAREN> secondFracPrec = UnsignedIntLiteral() <RPAREN> ]
             )
         ]
-        { start = SqlIntervalQualifier.TimeUnit.HOUR; }
+        { start = TimeUnit.HOUR; }
         |
         <MINUTE> [ <LPAREN> startPrec = UnsignedIntLiteral() <RPAREN> ]
         [ LOOKAHEAD(2) <TO>
             (
-                <SECOND> { end = SqlIntervalQualifier.TimeUnit.SECOND; }
+                <SECOND> { end = TimeUnit.SECOND; }
                 [ <LPAREN> secondFracPrec = UnsignedIntLiteral() <RPAREN> ]
             )
         ]
-        { start = SqlIntervalQualifier.TimeUnit.MINUTE; }
+        { start = TimeUnit.MINUTE; }
         |
         <SECOND>
         [   <LPAREN> startPrec = UnsignedIntLiteral()
             [ <COMMA> secondFracPrec = UnsignedIntLiteral() ]
             <RPAREN>
         ]
-        { start = SqlIntervalQualifier.TimeUnit.SECOND; }
+        { start = TimeUnit.SECOND; }
     )
     {
         return new SqlIntervalQualifier(start,
@@ -3268,32 +3269,32 @@ SqlIntervalQualifier IntervalQualifier() :
     }
 }
 
-SqlIntervalQualifier.TimeUnit TimeUnit() :
+TimeUnit TimeUnit() :
 {}
 {
     <YEAR>
     {
-        return SqlIntervalQualifier.TimeUnit.YEAR;
+        return TimeUnit.YEAR;
     }
     | <MONTH>
     {
-        return SqlIntervalQualifier.TimeUnit.MONTH;
+        return TimeUnit.MONTH;
     }
     | <DAY>
     {
-        return SqlIntervalQualifier.TimeUnit.DAY;
+        return TimeUnit.DAY;
     }
     | <HOUR>
     {
-        return SqlIntervalQualifier.TimeUnit.HOUR;
+        return TimeUnit.HOUR;
     }
     | <MINUTE>
     {
-        return SqlIntervalQualifier.TimeUnit.MINUTE;
+        return TimeUnit.MINUTE;
     }
     |   <SECOND>
     {
-        return SqlIntervalQualifier.TimeUnit.SECOND;
+        return TimeUnit.SECOND;
     }
 }
 
@@ -3777,7 +3778,7 @@ SqlNode BuiltinFunctionCall() :
         <EXTRACT>
         {
             pos = getPos();
-            SqlIntervalQualifier.TimeUnit unit;
+            TimeUnit unit;
         }
         <LPAREN>
         unit = TimeUnit()

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/94752a60/core/src/main/java/org/apache/calcite/adapter/clone/ColumnLoader.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/adapter/clone/ColumnLoader.java b/core/src/main/java/org/apache/calcite/adapter/clone/ColumnLoader.java
index 1338fce..86bb77f 100644
--- a/core/src/main/java/org/apache/calcite/adapter/clone/ColumnLoader.java
+++ b/core/src/main/java/org/apache/calcite/adapter/clone/ColumnLoader.java
@@ -18,6 +18,7 @@ package org.apache.calcite.adapter.clone;
 
 import org.apache.calcite.adapter.java.JavaTypeFactory;
 import org.apache.calcite.avatica.ColumnMetaData;
+import org.apache.calcite.avatica.util.DateTimeUtils;
 import org.apache.calcite.linq4j.Enumerable;
 import org.apache.calcite.linq4j.Ord;
 import org.apache.calcite.linq4j.function.Function1;
@@ -26,7 +27,6 @@ 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.rel.type.RelProtoDataType;
-import org.apache.calcite.util.DateTimeUtil;
 
 import java.lang.reflect.Type;
 import java.sql.Date;
@@ -64,7 +64,7 @@ class ColumnLoader<T> {
         public Integer apply(Time a0) {
           return a0 == null
               ? null
-              : (int) (a0.getTime() % DateTimeUtil.MILLIS_PER_DAY);
+              : (int) (a0.getTime() % DateTimeUtils.MILLIS_PER_DAY);
         }
       };
 
@@ -73,7 +73,7 @@ class ColumnLoader<T> {
         public Integer apply(Date a0) {
           return a0 == null
               ? null
-              : (int) (a0.getTime() / DateTimeUtil.MILLIS_PER_DAY);
+              : (int) (a0.getTime() / DateTimeUtils.MILLIS_PER_DAY);
         }
       };
 

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/94752a60/core/src/main/java/org/apache/calcite/adapter/clone/ListTable.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/adapter/clone/ListTable.java b/core/src/main/java/org/apache/calcite/adapter/clone/ListTable.java
index cb446ff..e75fb07 100644
--- a/core/src/main/java/org/apache/calcite/adapter/clone/ListTable.java
+++ b/core/src/main/java/org/apache/calcite/adapter/clone/ListTable.java
@@ -83,7 +83,8 @@ class ListTable extends AbstractQueryableTable {
       }
 
       public Iterator<T> iterator() {
-        return Linq4j.enumeratorIterator(enumerator());
+        //noinspection unchecked
+        return list.iterator();
       }
 
       public Enumerator<T> enumerator() {

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/94752a60/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 b3166ec..dba24af 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
@@ -16,6 +16,7 @@
  */
 package org.apache.calcite.adapter.enumerable;
 
+import org.apache.calcite.avatica.util.DateTimeUtils;
 import org.apache.calcite.linq4j.Ord;
 import org.apache.calcite.linq4j.tree.BlockBuilder;
 import org.apache.calcite.linq4j.tree.BlockStatement;
@@ -49,7 +50,6 @@ import org.apache.calcite.sql.type.SqlTypeUtil;
 import org.apache.calcite.sql.validate.SqlUserDefinedAggFunction;
 import org.apache.calcite.sql.validate.SqlUserDefinedFunction;
 import org.apache.calcite.util.BuiltInMethod;
-import org.apache.calcite.util.DateTimeUtil;
 import org.apache.calcite.util.Util;
 
 import com.google.common.base.Supplier;
@@ -1756,7 +1756,7 @@ public class RexImpTable {
         trop1 =
             Expressions.convert_(
                 Expressions.divide(trop1,
-                    Expressions.constant(DateTimeUtil.MILLIS_PER_DAY)),
+                    Expressions.constant(DateTimeUtils.MILLIS_PER_DAY)),
                 int.class);
         break;
       case TIME:

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/94752a60/core/src/main/java/org/apache/calcite/adapter/enumerable/RexToLixTranslator.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/adapter/enumerable/RexToLixTranslator.java b/core/src/main/java/org/apache/calcite/adapter/enumerable/RexToLixTranslator.java
index da29cce..0288e9d 100644
--- a/core/src/main/java/org/apache/calcite/adapter/enumerable/RexToLixTranslator.java
+++ b/core/src/main/java/org/apache/calcite/adapter/enumerable/RexToLixTranslator.java
@@ -18,7 +18,8 @@ package org.apache.calcite.adapter.enumerable;
 
 import org.apache.calcite.DataContext;
 import org.apache.calcite.adapter.java.JavaTypeFactory;
-import org.apache.calcite.avatica.ByteString;
+import org.apache.calcite.avatica.util.ByteString;
+import org.apache.calcite.avatica.util.DateTimeUtils;
 import org.apache.calcite.linq4j.tree.BlockBuilder;
 import org.apache.calcite.linq4j.tree.ConstantExpression;
 import org.apache.calcite.linq4j.tree.Expression;
@@ -43,7 +44,6 @@ import org.apache.calcite.sql.SqlKind;
 import org.apache.calcite.sql.SqlOperator;
 import org.apache.calcite.util.BuiltInMethod;
 import org.apache.calcite.util.ControlFlowException;
-import org.apache.calcite.util.DateTimeUtil;
 import org.apache.calcite.util.NlsString;
 import org.apache.calcite.util.Pair;
 import org.apache.calcite.util.Util;
@@ -268,7 +268,7 @@ public class RexToLixTranslator {
             Expressions.call(
                 BuiltInMethod.INTERVAL_YEAR_MONTH_TO_STRING.method,
                 operand,
-                Expressions.constant(interval.foo())));
+                Expressions.constant(interval.timeUnitRange)));
         break;
       case INTERVAL_DAY_TIME:
         convert = RexImpTable.optimize2(
@@ -276,7 +276,7 @@ public class RexToLixTranslator {
             Expressions.call(
                 BuiltInMethod.INTERVAL_DAY_TIME_TO_STRING.method,
                 operand,
-                Expressions.constant(interval.foo()),
+                Expressions.constant(interval.timeUnitRange),
                 Expressions.constant(
                     interval.getFractionalSecondPrecision(
                         typeFactory.getTypeSystem()))));
@@ -516,12 +516,12 @@ public class RexToLixTranslator {
           Expressions.constant(value.toString()));
     case DATE:
       value2 = (int)
-          (((Calendar) value).getTimeInMillis() / DateTimeUtil.MILLIS_PER_DAY);
+          (((Calendar) value).getTimeInMillis() / DateTimeUtils.MILLIS_PER_DAY);
       javaClass = int.class;
       break;
     case TIME:
       value2 = (int)
-          (((Calendar) value).getTimeInMillis() % DateTimeUtil.MILLIS_PER_DAY);
+          (((Calendar) value).getTimeInMillis() % DateTimeUtils.MILLIS_PER_DAY);
       javaClass = int.class;
       break;
     case TIMESTAMP:

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/94752a60/core/src/main/java/org/apache/calcite/adapter/jdbc/JdbcUtils.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/adapter/jdbc/JdbcUtils.java b/core/src/main/java/org/apache/calcite/adapter/jdbc/JdbcUtils.java
index fddb7d3..d4643af 100644
--- a/core/src/main/java/org/apache/calcite/adapter/jdbc/JdbcUtils.java
+++ b/core/src/main/java/org/apache/calcite/adapter/jdbc/JdbcUtils.java
@@ -16,11 +16,11 @@
  */
 package org.apache.calcite.adapter.jdbc;
 
+import org.apache.calcite.avatica.util.DateTimeUtils;
 import org.apache.calcite.linq4j.function.Function0;
 import org.apache.calcite.linq4j.function.Function1;
 import org.apache.calcite.linq4j.tree.Primitive;
 import org.apache.calcite.sql.SqlDialect;
-import org.apache.calcite.util.DateTimeUtil;
 import org.apache.calcite.util.ImmutableNullableList;
 import org.apache.calcite.util.IntList;
 import org.apache.calcite.util.Pair;
@@ -185,7 +185,7 @@ final class JdbcUtils {
       }
       long time = v.getTime();
       int offset = TimeZone.getDefault().getOffset(time);
-      return new Time((time + offset) % DateTimeUtil.MILLIS_PER_DAY);
+      return new Time((time + offset) % DateTimeUtils.MILLIS_PER_DAY);
     }
 
     private static Date shift(Date v) {

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/94752a60/core/src/main/java/org/apache/calcite/config/CalciteConnectionConfig.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/config/CalciteConnectionConfig.java b/core/src/main/java/org/apache/calcite/config/CalciteConnectionConfig.java
index b47af97..d7440e8 100644
--- a/core/src/main/java/org/apache/calcite/config/CalciteConnectionConfig.java
+++ b/core/src/main/java/org/apache/calcite/config/CalciteConnectionConfig.java
@@ -16,9 +16,9 @@
  */
 package org.apache.calcite.config;
 
-import org.apache.calcite.avatica.Casing;
 import org.apache.calcite.avatica.ConnectionConfig;
-import org.apache.calcite.avatica.Quoting;
+import org.apache.calcite.avatica.util.Casing;
+import org.apache.calcite.avatica.util.Quoting;
 
 /** Interface for reading connection properties within Calcite code. There is
  * a method for every property. At some point there will be similar config

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/94752a60/core/src/main/java/org/apache/calcite/config/CalciteConnectionConfigImpl.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/config/CalciteConnectionConfigImpl.java b/core/src/main/java/org/apache/calcite/config/CalciteConnectionConfigImpl.java
index 442a54f..43d7946 100644
--- a/core/src/main/java/org/apache/calcite/config/CalciteConnectionConfigImpl.java
+++ b/core/src/main/java/org/apache/calcite/config/CalciteConnectionConfigImpl.java
@@ -16,9 +16,9 @@
  */
 package org.apache.calcite.config;
 
-import org.apache.calcite.avatica.Casing;
 import org.apache.calcite.avatica.ConnectionConfigImpl;
-import org.apache.calcite.avatica.Quoting;
+import org.apache.calcite.avatica.util.Casing;
+import org.apache.calcite.avatica.util.Quoting;
 
 import java.util.Properties;
 

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/94752a60/core/src/main/java/org/apache/calcite/config/CalciteConnectionProperty.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/config/CalciteConnectionProperty.java b/core/src/main/java/org/apache/calcite/config/CalciteConnectionProperty.java
index 88b2621..c42e2d7 100644
--- a/core/src/main/java/org/apache/calcite/config/CalciteConnectionProperty.java
+++ b/core/src/main/java/org/apache/calcite/config/CalciteConnectionProperty.java
@@ -30,55 +30,57 @@ import static org.apache.calcite.avatica.ConnectionConfigImpl.parse;
  */
 public enum CalciteConnectionProperty implements ConnectionProperty {
   /** Whether to store query results in temporary tables. */
-  AUTO_TEMP("autoTemp", Type.BOOLEAN, false),
+  AUTO_TEMP("autoTemp", Type.BOOLEAN, false, false),
 
   /** Whether Calcite should use materializations. */
-  MATERIALIZATIONS_ENABLED("materializationsEnabled", Type.BOOLEAN, true),
+  MATERIALIZATIONS_ENABLED("materializationsEnabled", Type.BOOLEAN, true,
+      false),
 
   /** Whether Calcite should create materializations. */
-  CREATE_MATERIALIZATIONS("createMaterializations", Type.BOOLEAN, true),
+  CREATE_MATERIALIZATIONS("createMaterializations", Type.BOOLEAN, true, false),
 
   /** URI of the model. */
-  MODEL("model", Type.STRING, null),
+  MODEL("model", Type.STRING, null, false),
 
   /** Lexical policy. */
-  LEX("lex", Type.ENUM, Lex.ORACLE),
+  LEX("lex", Type.ENUM, Lex.ORACLE, false),
 
   /** How identifiers are quoted.
    *  If not specified, value from {@link #LEX} is used. */
-  QUOTING("quoting", Type.ENUM, null),
+  QUOTING("quoting", Type.ENUM, null, false),
 
   /** How identifiers are stored if they are quoted.
    *  If not specified, value from {@link #LEX} is used. */
-  QUOTED_CASING("quotedCasing", Type.ENUM, null),
+  QUOTED_CASING("quotedCasing", Type.ENUM, null, false),
 
   /** How identifiers are stored if they are not quoted.
    *  If not specified, value from {@link #LEX} is used. */
-  UNQUOTED_CASING("unquotedCasing", Type.ENUM, null),
+  UNQUOTED_CASING("unquotedCasing", Type.ENUM, null, false),
 
   /** Whether identifiers are matched case-sensitively.
    *  If not specified, value from {@link #LEX} is used. */
-  CASE_SENSITIVE("caseSensitive", Type.BOOLEAN, null),
+  CASE_SENSITIVE("caseSensitive", Type.BOOLEAN, null, false),
 
   /** Name of initial schema. */
-  SCHEMA("schema", Type.STRING, null),
+  SCHEMA("schema", Type.STRING, null, false),
 
   /** Specifies whether Spark should be used as the engine for processing that
    * cannot be pushed to the source system. If false (the default), Calcite
    * generates code that implements the Enumerable interface. */
-  SPARK("spark", Type.BOOLEAN, false),
+  SPARK("spark", Type.BOOLEAN, false, false),
 
   /** Timezone, for example 'gmt-3'. Default is the JVM's time zone. */
-  TIMEZONE("timezone", Type.STRING, null),
+  TIMEZONE("timezone", Type.STRING, null, false),
 
   /** Type system. The name of a class that implements
    * {@link org.apache.calcite.rel.type.RelDataTypeSystem} and has a public
    * default constructor or an {@code INSTANCE} constant. */
-  TYPE_SYSTEM("typeSystem", Type.PLUGIN, null);
+  TYPE_SYSTEM("typeSystem", Type.PLUGIN, null, false);
 
   private final String camelName;
   private final Type type;
   private final Object defaultValue;
+  private final boolean required;
 
   private static final Map<String, CalciteConnectionProperty> NAME_TO_PROPS;
 
@@ -90,10 +92,12 @@ public enum CalciteConnectionProperty implements ConnectionProperty {
     }
   }
 
-  CalciteConnectionProperty(String camelName, Type type, Object defaultValue) {
+  CalciteConnectionProperty(String camelName, Type type, Object defaultValue,
+      boolean required) {
     this.camelName = camelName;
     this.type = type;
     this.defaultValue = defaultValue;
+    this.required = required;
     assert defaultValue == null || type.valid(defaultValue);
   }
 
@@ -109,6 +113,9 @@ public enum CalciteConnectionProperty implements ConnectionProperty {
     return type;
   }
 
+  public boolean required() {
+    return required;
+  }
 
   public PropEnv wrap(Properties properties) {
     return new PropEnv(parse(properties, NAME_TO_PROPS), this);

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/94752a60/core/src/main/java/org/apache/calcite/config/Lex.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/config/Lex.java b/core/src/main/java/org/apache/calcite/config/Lex.java
index 51a6b63..c492d1b 100644
--- a/core/src/main/java/org/apache/calcite/config/Lex.java
+++ b/core/src/main/java/org/apache/calcite/config/Lex.java
@@ -16,8 +16,8 @@
  */
 package org.apache.calcite.config;
 
-import org.apache.calcite.avatica.Casing;
-import org.apache.calcite.avatica.Quoting;
+import org.apache.calcite.avatica.util.Casing;
+import org.apache.calcite.avatica.util.Quoting;
 
 /** Named, built-in lexical policy. A lexical policy describes how
  * identifiers are quoted, whether they are converted to upper- or