You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@calcite.apache.org by el...@apache.org on 2016/03/07 19:28:10 UTC

[32/59] [partial] calcite git commit: [CALCITE-1078] Detach avatica from the core calcite Maven project

http://git-wip-us.apache.org/repos/asf/calcite/blob/5cee486f/avatica/core/src/main/java/org/apache/calcite/avatica/util/ByteString.java
----------------------------------------------------------------------
diff --git a/avatica/core/src/main/java/org/apache/calcite/avatica/util/ByteString.java b/avatica/core/src/main/java/org/apache/calcite/avatica/util/ByteString.java
new file mode 100644
index 0000000..4e606b7
--- /dev/null
+++ b/avatica/core/src/main/java/org/apache/calcite/avatica/util/ByteString.java
@@ -0,0 +1,347 @@
+/*
+ * 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 com.fasterxml.jackson.annotation.JsonValue;
+
+import java.io.IOException;
+import java.io.Serializable;
+import java.util.Arrays;
+
+/**
+ * Collection of bytes.
+ *
+ * <p>ByteString is to bytes what {@link String} is to chars: It is immutable,
+ * implements equality ({@link #hashCode} and {@link #equals}),
+ * comparison ({@link #compareTo}) and
+ * {@link Serializable serialization} correctly.</p>
+ */
+public class ByteString implements Comparable<ByteString>, Serializable {
+  private final byte[] bytes;
+
+  /** An empty byte string. */
+  public static final ByteString EMPTY = new ByteString(new byte[0], false);
+
+  private static final char[] DIGITS = {
+    '0', '1', '2', '3', '4', '5', '6', '7',
+    '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'
+  };
+
+  /**
+   * Creates a ByteString.
+   *
+   * @param bytes Bytes
+   */
+  public ByteString(byte[] bytes) {
+    this(bytes.clone(), false);
+  }
+
+  // private constructor that does not copy
+  private ByteString(byte[] bytes, boolean dummy) {
+    this.bytes = bytes;
+  }
+
+  @Override public int hashCode() {
+    return Arrays.hashCode(bytes);
+  }
+
+  @Override public boolean equals(Object obj) {
+    return this == obj
+        || obj instanceof ByteString
+        && Arrays.equals(bytes, ((ByteString) obj).bytes);
+  }
+
+  public int compareTo(ByteString that) {
+    final byte[] v1 = bytes;
+    final byte[] v2 = that.bytes;
+    final int n = Math.min(v1.length, v2.length);
+    for (int i = 0; i < n; i++) {
+      int c1 = v1[i] & 0xff;
+      int c2 = v2[i] & 0xff;
+      if (c1 != c2) {
+        return c1 - c2;
+      }
+    }
+    return v1.length - v2.length;
+  }
+
+  /**
+   * Returns this byte string in hexadecimal format.
+   *
+   * @return Hexadecimal string
+   */
+  @Override public String toString() {
+    return toString(16);
+  }
+
+  /**
+   * Returns this byte string in a given base.
+   *
+   * @return String in given base
+   */
+  public String toString(int base) {
+    return toString(bytes, base);
+  }
+
+  /**
+   * Returns the given byte array in hexadecimal format.
+   *
+   * <p>For example, <tt>toString(new byte[] {0xDE, 0xAD})</tt>
+   * returns {@code "DEAD"}.</p>
+   *
+   * @param bytes Array of bytes
+   * @param base Base (2 or 16)
+   * @return String
+   */
+  public static String toString(byte[] bytes, int base) {
+    char[] chars;
+    int j = 0;
+    switch (base) {
+    case 2:
+      chars = new char[bytes.length * 8];
+      for (byte b : bytes) {
+        chars[j++] = DIGITS[(b & 0x80) >> 7];
+        chars[j++] = DIGITS[(b & 0x40) >> 6];
+        chars[j++] = DIGITS[(b & 0x20) >> 5];
+        chars[j++] = DIGITS[(b & 0x10) >> 4];
+        chars[j++] = DIGITS[(b & 0x08) >> 3];
+        chars[j++] = DIGITS[(b & 0x04) >> 2];
+        chars[j++] = DIGITS[(b & 0x02) >> 1];
+        chars[j++] = DIGITS[b & 0x01];
+      }
+      break;
+    case 16:
+      chars = new char[bytes.length * 2];
+      for (byte b : bytes) {
+        chars[j++] = DIGITS[(b & 0xF0) >> 4];
+        chars[j++] = DIGITS[b & 0x0F];
+      }
+      break;
+    default:
+      throw new IllegalArgumentException("bad base " + base);
+    }
+    return new String(chars, 0, j);
+  }
+
+  /**
+   * Returns this byte string in Base64 format.
+   *
+   * @return Base64 string
+   */
+  public String toBase64String() {
+    return Base64.encodeBytes(bytes);
+  }
+
+  /**
+   * Creates a byte string from a hexadecimal or binary string.
+   *
+   * <p>For example, <tt>of("DEAD", 16)</tt>
+   * returns the same as {@code ByteString(new byte[] {0xDE, 0xAD})}.
+   *
+   * @param string Array of bytes
+   * @param base Base (2 or 16)
+   * @return String
+   */
+  public static ByteString of(String string, int base) {
+    final byte[] bytes = parse(string, base);
+    return new ByteString(bytes, false);
+  }
+
+  /**
+   * Parses a hexadecimal or binary string to a byte array.
+   *
+   * @param string Hexadecimal or binary string
+   * @param base Base (2 or 16)
+   * @return Byte array
+   */
+  public static byte[] parse(String string, int base) {
+    char[] chars = string.toCharArray();
+    byte[] bytes;
+    int j = 0;
+    byte b = 0;
+    switch (base) {
+    case 2:
+      bytes = new byte[chars.length / 8];
+      for (char c : chars) {
+        b <<= 1;
+        if (c == '1') {
+          b |= 0x1;
+        }
+        if (j % 8 == 7) {
+          bytes[j / 8] = b;
+          b = 0;
+        }
+        ++j;
+      }
+      break;
+    case 16:
+      if (chars.length % 2 != 0) {
+        throw new IllegalArgumentException("hex string has odd length");
+      }
+      bytes = new byte[chars.length / 2];
+      for (char c : chars) {
+        b <<= 4;
+        byte i = decodeHex(c);
+        b |= i & 0x0F;
+        if (j % 2 == 1) {
+          bytes[j / 2] = b;
+          b = 0;
+        }
+        ++j;
+      }
+      break;
+    default:
+      throw new IllegalArgumentException("bad base " + base);
+    }
+    return bytes;
+  }
+
+  private static byte decodeHex(char c) {
+    if (c >= '0' && c <= '9') {
+      return (byte) (c - '0');
+    }
+    if (c >= 'a' && c <= 'f') {
+      return (byte) (c - 'a' + 10);
+    }
+    if (c >= 'A' && c <= 'F') {
+      return (byte) (c - 'A' + 10);
+    }
+    throw new IllegalArgumentException("invalid hex character: " + c);
+  }
+
+  /**
+   * Creates a byte string from a Base64 string.
+   *
+   * @param string Base64 string
+   * @return Byte string
+   */
+  public static ByteString ofBase64(String string) {
+    final byte[] bytes = parseBase64(string);
+    return new ByteString(bytes, false);
+  }
+
+  /**
+   * Parses a Base64 to a byte array.
+   *
+   * @param string Base64 string
+   * @return Byte array
+   */
+  public static byte[] parseBase64(String string) {
+    try {
+      return Base64.decode(string);
+    } catch (IOException e) {
+      throw new IllegalArgumentException("bad base64 string", e);
+    }
+  }
+
+  @SuppressWarnings({
+      "CloneDoesntCallSuperClone",
+      "CloneDoesntDeclareCloneNotSupportedException"
+  })
+  @Override public Object clone() {
+    return this;
+  }
+
+  /**
+   * Returns the number of bytes in this byte string.
+   *
+   * @return Length of this byte string
+   */
+  public int length() {
+    return bytes.length;
+  }
+
+  /**
+   * Returns the byte at a given position in the byte string.
+   *
+   * @param i Index
+   * @throws  IndexOutOfBoundsException
+   *          if the <tt>index</tt> argument is negative or not less than
+   *          <tt>length()</tt>
+   * @return Byte at given position
+   */
+  public byte byteAt(int i) {
+    return bytes[i];
+  }
+
+  /**
+   * Returns a ByteString that consists of a given range.
+   *
+   * @param start Start of range
+   * @param end Position after end of range
+   * @return Substring
+   */
+  public ByteString substring(int start, int end) {
+    byte[] bytes = Arrays.copyOfRange(this.bytes, start, end);
+    return new ByteString(bytes, false);
+  }
+
+  /**
+   * Returns a ByteString that starts at a given position.
+   *
+   * @param start Start of range
+   * @return Substring
+   */
+  public ByteString substring(int start) {
+    return substring(start, length());
+  }
+
+  /**
+   * Returns a copy of the byte array.
+   */
+  @JsonValue
+  public byte[] getBytes() {
+    return bytes.clone();
+  }
+
+  /**
+   * Returns a ByteString consisting of the concatenation of this and another
+   * string.
+   *
+   * @param other Byte string to concatenate
+   * @return Combined byte string
+   */
+  public ByteString concat(ByteString other) {
+    int otherLen = other.length();
+    if (otherLen == 0) {
+      return this;
+    }
+    int len = bytes.length;
+    byte[] buf = Arrays.copyOf(bytes, len + otherLen);
+    System.arraycopy(other.bytes, 0, buf, len, other.bytes.length);
+    return new ByteString(buf, false);
+  }
+
+  /** Returns the position at which {@code seek} first occurs in this byte
+   * string, or -1 if it does not occur. */
+  public int indexOf(ByteString seek) {
+  iLoop:
+    for (int i = 0; i < bytes.length - seek.bytes.length + 1; i++) {
+      for (int j = 0;; j++) {
+        if (j == seek.bytes.length) {
+          return i;
+        }
+        if (bytes[i + j] != seek.bytes[j]) {
+          continue iLoop;
+        }
+      }
+    }
+    return -1;
+  }
+}
+
+// End ByteString.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/5cee486f/avatica/core/src/main/java/org/apache/calcite/avatica/util/Casing.java
----------------------------------------------------------------------
diff --git a/avatica/core/src/main/java/org/apache/calcite/avatica/util/Casing.java b/avatica/core/src/main/java/org/apache/calcite/avatica/util/Casing.java
new file mode 100644
index 0000000..5f13214
--- /dev/null
+++ b/avatica/core/src/main/java/org/apache/calcite/avatica/util/Casing.java
@@ -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.
+ */
+package org.apache.calcite.avatica.util;
+
+/** Policy for converting case of identifiers before storing them.
+ *
+ * <p>A database often has policies for quoted versus unquoted identifiers.
+ * For example, Oracle converts unquoted identifiers to upper-case, but
+ * quoted identifiers are unchanged.</p> */
+public enum Casing {
+  /** The case of identifiers is not changed. */
+  UNCHANGED,
+
+  /** Identifiers are converted to upper-case. */
+  TO_UPPER,
+
+  /** Identifiers are converted to lower-case. */
+  TO_LOWER
+}
+
+// End Casing.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/5cee486f/avatica/core/src/main/java/org/apache/calcite/avatica/util/Cursor.java
----------------------------------------------------------------------
diff --git a/avatica/core/src/main/java/org/apache/calcite/avatica/util/Cursor.java b/avatica/core/src/main/java/org/apache/calcite/avatica/util/Cursor.java
new file mode 100644
index 0000000..8eab72f
--- /dev/null
+++ b/avatica/core/src/main/java/org/apache/calcite/avatica/util/Cursor.java
@@ -0,0 +1,145 @@
+/*
+ * 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 org.apache.calcite.avatica.ColumnMetaData;
+
+import java.io.InputStream;
+import java.io.Reader;
+import java.math.BigDecimal;
+import java.net.URL;
+import java.sql.Array;
+import java.sql.Blob;
+import java.sql.Clob;
+import java.sql.Date;
+import java.sql.NClob;
+import java.sql.Ref;
+import java.sql.SQLException;
+import java.sql.SQLXML;
+import java.sql.Time;
+import java.sql.Timestamp;
+import java.util.Calendar;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Interface to an iteration that is similar to, and can easily support,
+ * a JDBC {@link java.sql.ResultSet}, but is simpler to implement.
+ */
+public interface Cursor extends AutoCloseable {
+  /**
+   * Creates a list of accessors, one per column.
+   *
+   *
+   * @param types List of column types, per {@link java.sql.Types}.
+   * @param localCalendar Calendar in local time zone
+   * @param factory Factory that creates sub-ResultSets when needed
+   * @return List of column accessors
+   */
+  List<Accessor> createAccessors(List<ColumnMetaData> types,
+      Calendar localCalendar, ArrayImpl.Factory factory);
+
+  /**
+   * Moves to the next row.
+   *
+   * @return Whether moved
+   *
+   * @throws SQLException on database error
+   */
+  boolean next() throws SQLException;
+
+  /**
+   * Closes this cursor and releases resources.
+   */
+  void close();
+
+  /**
+   * Returns whether the last value returned was null.
+   *
+   * @throws SQLException on database error
+   */
+  boolean wasNull() throws SQLException;
+
+  /**
+   * Accessor of a column value.
+   */
+  public interface Accessor {
+    boolean wasNull() throws SQLException;
+
+    String getString() throws SQLException;
+
+    boolean getBoolean() throws SQLException;
+
+    byte getByte() throws SQLException;
+
+    short getShort() throws SQLException;
+
+    int getInt() throws SQLException;
+
+    long getLong() throws SQLException;
+
+    float getFloat() throws SQLException;
+
+    double getDouble() throws SQLException;
+
+    BigDecimal getBigDecimal() throws SQLException;
+
+    BigDecimal getBigDecimal(int scale) throws SQLException;
+
+    byte[] getBytes() throws SQLException;
+
+    InputStream getAsciiStream() throws SQLException;
+
+    InputStream getUnicodeStream() throws SQLException;
+
+    InputStream getBinaryStream() throws SQLException;
+
+    Object getObject() throws SQLException;
+
+    Reader getCharacterStream() throws SQLException;
+
+    Object getObject(Map<String, Class<?>> map) throws SQLException;
+
+    Ref getRef() throws SQLException;
+
+    Blob getBlob() throws SQLException;
+
+    Clob getClob() throws SQLException;
+
+    Array getArray() throws SQLException;
+
+    Date getDate(Calendar calendar) throws SQLException;
+
+    Time getTime(Calendar calendar) throws SQLException;
+
+    Timestamp getTimestamp(Calendar calendar) throws SQLException;
+
+    URL getURL() throws SQLException;
+
+    NClob getNClob() throws SQLException;
+
+    SQLXML getSQLXML() throws SQLException;
+
+    String getNString() throws SQLException;
+
+    Reader getNCharacterStream() throws SQLException;
+
+    <T> T getObject(Class<T> type) throws SQLException;
+  }
+}
+
+// End Cursor.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/5cee486f/avatica/core/src/main/java/org/apache/calcite/avatica/util/DateTimeUtils.java
----------------------------------------------------------------------
diff --git a/avatica/core/src/main/java/org/apache/calcite/avatica/util/DateTimeUtils.java b/avatica/core/src/main/java/org/apache/calcite/avatica/util/DateTimeUtils.java
new file mode 100644
index 0000000..67e1245
--- /dev/null
+++ b/avatica/core/src/main/java/org/apache/calcite/avatica/util/DateTimeUtils.java
@@ -0,0 +1,842 @@
+/*
+ * 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 (not null)
+   * @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.
+   */
+  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);
+    }
+  }
+
+  /** Resets to zero the "time" part of a timestamp. */
+  public static long resetTime(long timestamp) {
+    int date = (int) (timestamp / MILLIS_PER_DAY);
+    return (long) date * MILLIS_PER_DAY;
+  }
+
+  /** Resets to epoch (1970-01-01) the "date" part of a timestamp. */
+  public static long resetDate(long timestamp) {
+    return floorMod(timestamp, MILLIS_PER_DAY);
+  }
+
+  public static long unixTimestampFloor(TimeUnitRange range, long timestamp) {
+    int date = (int) (timestamp / MILLIS_PER_DAY);
+    final int f = julianDateFloor(range, date + EPOCH_JULIAN, true);
+    return (long) f * MILLIS_PER_DAY;
+  }
+
+  public static long unixDateFloor(TimeUnitRange range, long date) {
+    return julianDateFloor(range, (int) date + EPOCH_JULIAN, true);
+  }
+
+  public static long unixTimestampCeil(TimeUnitRange range, long timestamp) {
+    int date = (int) (timestamp / MILLIS_PER_DAY);
+    final int f = julianDateFloor(range, date + EPOCH_JULIAN, false);
+    return (long) f * MILLIS_PER_DAY;
+  }
+
+  public static long unixDateCeil(TimeUnitRange range, long date) {
+    return julianDateFloor(range, (int) date + EPOCH_JULIAN, true);
+  }
+
+  private static int julianDateFloor(TimeUnitRange range, int julian,
+      boolean floor) {
+    // 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:
+      if (!floor && (month > 1 || day > 1)) {
+        ++year;
+      }
+      return ymdToUnixDate(year, 1, 1);
+    case MONTH:
+      if (!floor && day > 1) {
+        ++month;
+      }
+      return ymdToUnixDate(year, month, 1);
+    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;
+  }
+
+  public static long unixTimestamp(int year, int month, int day, int hour,
+      int minute, int second) {
+    final int date = ymdToUnixDate(year, month, day);
+    return (long) date * MILLIS_PER_DAY
+        + (long) hour * MILLIS_PER_HOUR
+        + (long) minute * MILLIS_PER_MINUTE
+        + (long) second * MILLIS_PER_SECOND;
+  }
+
+  /** Divide, rounding towards negative infinity. */
+  public static long floorDiv(long x, long y) {
+    long r = x / y;
+    // if the signs are different and modulo not zero, round down
+    if ((x ^ y) < 0 && (r * y != x)) {
+      r--;
+    }
+    return r;
+  }
+
+  /** Modulo, always returning a non-negative result. */
+  public static long floorMod(long x, long y) {
+    return x - floorDiv(x, y) * y;
+  }
+
+  //~ 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/calcite/blob/5cee486f/avatica/core/src/main/java/org/apache/calcite/avatica/util/IteratorCursor.java
----------------------------------------------------------------------
diff --git a/avatica/core/src/main/java/org/apache/calcite/avatica/util/IteratorCursor.java b/avatica/core/src/main/java/org/apache/calcite/avatica/util/IteratorCursor.java
new file mode 100644
index 0000000..c09373b
--- /dev/null
+++ b/avatica/core/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.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 AutoCloseable) {
+      try {
+        ((AutoCloseable) iterator).close();
+      } catch (RuntimeException e) {
+        throw e;
+      } catch (Exception 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/calcite/blob/5cee486f/avatica/core/src/main/java/org/apache/calcite/avatica/util/ListIteratorCursor.java
----------------------------------------------------------------------
diff --git a/avatica/core/src/main/java/org/apache/calcite/avatica/util/ListIteratorCursor.java b/avatica/core/src/main/java/org/apache/calcite/avatica/util/ListIteratorCursor.java
new file mode 100644
index 0000000..e2801ec
--- /dev/null
+++ b/avatica/core/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/calcite/blob/5cee486f/avatica/core/src/main/java/org/apache/calcite/avatica/util/MapIteratorCursor.java
----------------------------------------------------------------------
diff --git a/avatica/core/src/main/java/org/apache/calcite/avatica/util/MapIteratorCursor.java b/avatica/core/src/main/java/org/apache/calcite/avatica/util/MapIteratorCursor.java
new file mode 100644
index 0000000..9ab6c9c
--- /dev/null
+++ b/avatica/core/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/calcite/blob/5cee486f/avatica/core/src/main/java/org/apache/calcite/avatica/util/PackageMarker.java
----------------------------------------------------------------------
diff --git a/avatica/core/src/main/java/org/apache/calcite/avatica/util/PackageMarker.java b/avatica/core/src/main/java/org/apache/calcite/avatica/util/PackageMarker.java
new file mode 100644
index 0000000..3a6a9c6
--- /dev/null
+++ b/avatica/core/src/main/java/org/apache/calcite/avatica/util/PackageMarker.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;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * This is a dummy annotation that forces javac to produce output for
+ * otherwise empty package-info.java.
+ *
+ * <p>The result is maven-compiler-plugin can properly identify the scope of
+ * changed files
+ *
+ * <p>See more details in
+ * <a href="https://jira.codehaus.org/browse/MCOMPILER-205">
+ *   maven-compiler-plugin: incremental compilation broken</a>
+ */
+@Retention(RetentionPolicy.SOURCE)
+public @interface PackageMarker {
+}
+
+// End PackageMarker.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/5cee486f/avatica/core/src/main/java/org/apache/calcite/avatica/util/PositionedCursor.java
----------------------------------------------------------------------
diff --git a/avatica/core/src/main/java/org/apache/calcite/avatica/util/PositionedCursor.java b/avatica/core/src/main/java/org/apache/calcite/avatica/util/PositionedCursor.java
new file mode 100644
index 0000000..f60f47d
--- /dev/null
+++ b/avatica/core/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/calcite/blob/5cee486f/avatica/core/src/main/java/org/apache/calcite/avatica/util/Quoting.java
----------------------------------------------------------------------
diff --git a/avatica/core/src/main/java/org/apache/calcite/avatica/util/Quoting.java b/avatica/core/src/main/java/org/apache/calcite/avatica/util/Quoting.java
new file mode 100644
index 0000000..855e4a6
--- /dev/null
+++ b/avatica/core/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/calcite/blob/5cee486f/avatica/core/src/main/java/org/apache/calcite/avatica/util/RecordIteratorCursor.java
----------------------------------------------------------------------
diff --git a/avatica/core/src/main/java/org/apache/calcite/avatica/util/RecordIteratorCursor.java b/avatica/core/src/main/java/org/apache/calcite/avatica/util/RecordIteratorCursor.java
new file mode 100644
index 0000000..717247d
--- /dev/null
+++ b/avatica/core/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/calcite/blob/5cee486f/avatica/core/src/main/java/org/apache/calcite/avatica/util/Spacer.java
----------------------------------------------------------------------
diff --git a/avatica/core/src/main/java/org/apache/calcite/avatica/util/Spacer.java b/avatica/core/src/main/java/org/apache/calcite/avatica/util/Spacer.java
new file mode 100644
index 0000000..cc0a097
--- /dev/null
+++ b/avatica/core/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/calcite/blob/5cee486f/avatica/core/src/main/java/org/apache/calcite/avatica/util/Spaces.java
----------------------------------------------------------------------
diff --git a/avatica/core/src/main/java/org/apache/calcite/avatica/util/Spaces.java b/avatica/core/src/main/java/org/apache/calcite/avatica/util/Spaces.java
new file mode 100644
index 0000000..6469400
--- /dev/null
+++ b/avatica/core/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/calcite/blob/5cee486f/avatica/core/src/main/java/org/apache/calcite/avatica/util/StructImpl.java
----------------------------------------------------------------------
diff --git a/avatica/core/src/main/java/org/apache/calcite/avatica/util/StructImpl.java b/avatica/core/src/main/java/org/apache/calcite/avatica/util/StructImpl.java
new file mode 100644
index 0000000..b25fce6
--- /dev/null
+++ b/avatica/core/src/main/java/org/apache/calcite/avatica/util/StructImpl.java
@@ -0,0 +1,79 @@
+/*
+ * 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 org.apache.calcite.avatica.ColumnMetaData;
+
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Struct;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+/** Implementation of JDBC {@link Struct}. */
+public class StructImpl implements Struct {
+  private final List list;
+
+  public StructImpl(List list) {
+    this.list = list;
+  }
+
+  @Override public String toString() {
+    final Iterator iterator = list.iterator();
+    if (!iterator.hasNext()) {
+      return "{}";
+    }
+    final StringBuilder buf = new StringBuilder("{");
+    for (;;) {
+      append(buf, iterator.next());
+      if (!iterator.hasNext()) {
+        return buf.append("}").toString();
+      }
+      buf.append(", ");
+    }
+  }
+
+  @Override public String getSQLTypeName() throws SQLException {
+    return "ROW";
+  }
+
+  @Override public Object[] getAttributes() throws SQLException {
+    return list.toArray();
+  }
+
+  @Override public Object[] getAttributes(Map<String, Class<?>> map)
+      throws SQLException {
+    throw new UnsupportedOperationException(); // TODO
+  }
+
+  private void append(StringBuilder buf, Object o) {
+    if (o == null) {
+      buf.append("null");
+    } else {
+      buf.append(o);
+    }
+  }
+
+  /** Factory that can create a result set based on a list of values. */
+  public interface Factory {
+    ResultSet create(ColumnMetaData.AvaticaType elementType,
+        Iterable<Object> iterable);
+  }
+}
+
+// End StructImpl.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/5cee486f/avatica/core/src/main/java/org/apache/calcite/avatica/util/TimeUnit.java
----------------------------------------------------------------------
diff --git a/avatica/core/src/main/java/org/apache/calcite/avatica/util/TimeUnit.java b/avatica/core/src/main/java/org/apache/calcite/avatica/util/TimeUnit.java
new file mode 100644
index 0000000..b249232
--- /dev/null
+++ b/avatica/core/src/main/java/org/apache/calcite/avatica/util/TimeUnit.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.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)),
+
+  /** Unlike the other units, MILLISECOND may not be the unit of a SQL interval.
+   * Still, it is convenient to use it internally, when converting to and from
+   * UNIX timestamps. */
+  MILLISECOND(false, '.', 1, BigDecimal.valueOf(1));
+
+  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/calcite/blob/5cee486f/avatica/core/src/main/java/org/apache/calcite/avatica/util/TimeUnitRange.java
----------------------------------------------------------------------
diff --git a/avatica/core/src/main/java/org/apache/calcite/avatica/util/TimeUnitRange.java b/avatica/core/src/main/java/org/apache/calcite/avatica/util/TimeUnitRange.java
new file mode 100644
index 0000000..4d9b322
--- /dev/null
+++ b/avatica/core/src/main/java/org/apache/calcite/avatica/util/TimeUnitRange.java
@@ -0,0 +1,107 @@
+/*
+ * 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;
+import java.util.Objects;
+
+/** 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<>(startUnit, endUnit));
+  }
+
+  private static Map<Pair<TimeUnit>, TimeUnitRange> createMap() {
+    Map<Pair<TimeUnit>, TimeUnitRange> map = new HashMap<>();
+    for (TimeUnitRange value : values()) {
+      map.put(new Pair<>(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
+          && Objects.equals(left, ((Pair) obj).left)
+          && Objects.equals(right, ((Pair) obj).right);
+    }
+  }
+}
+
+// End TimeUnitRange.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/5cee486f/avatica/core/src/main/java/org/apache/calcite/avatica/util/UnsynchronizedBuffer.java
----------------------------------------------------------------------
diff --git a/avatica/core/src/main/java/org/apache/calcite/avatica/util/UnsynchronizedBuffer.java b/avatica/core/src/main/java/org/apache/calcite/avatica/util/UnsynchronizedBuffer.java
new file mode 100644
index 0000000..8daee60
--- /dev/null
+++ b/avatica/core/src/main/java/org/apache/calcite/avatica/util/UnsynchronizedBuffer.java
@@ -0,0 +1,152 @@
+/*
+ * 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.OutputStream;
+
+/**
+ * A utility class for reading and writing bytes to byte buffers without synchronization. A
+ * reduced variant taken from Apache Accumulo. This class is <b>not</b> thread-safe by design.
+ * It is up to the caller to guarantee mutual exclusion as necessary.
+ */
+public class UnsynchronizedBuffer extends OutputStream {
+  // Anything larger than 64K, reap the backing buffer
+  private static final int LARGE_BUFFER_SIZE = 1024 * 64;
+
+  final int initialCapacity;
+  int offset = 0;
+  byte[] data;
+
+  /**
+   * Creates a new writer.
+   */
+  public UnsynchronizedBuffer() {
+    this(4096);
+  }
+
+  /**
+   * Creates a new writer.
+   *
+   * @param initialCapacity initial byte capacity
+   */
+  public UnsynchronizedBuffer(int initialCapacity) {
+    this.initialCapacity = initialCapacity;
+    data = new byte[initialCapacity];
+  }
+
+  private void reserve(int l) {
+    if (offset + l > data.length) {
+      int newSize = UnsynchronizedBuffer.nextArraySize(offset + l);
+
+      byte[] newData = new byte[newSize];
+      System.arraycopy(data, 0, newData, 0, offset);
+      data = newData;
+    }
+
+  }
+
+  /**
+   * Adds bytes to this writer's buffer.
+   *
+   * @param bytes byte array
+   * @param off offset into array to start copying bytes
+   * @param length number of bytes to add
+   * @throws IndexOutOfBoundsException if off or length are invalid
+   */
+  public void write(byte[] bytes, int off, int length) {
+    reserve(length);
+    System.arraycopy(bytes, off, data, offset, length);
+    offset += length;
+  }
+
+  @Override public void write(int b) throws IOException {
+    reserve(1);
+    data[offset] = (byte) b;
+    offset++;
+  }
+
+  /**
+   * Gets (a copy of) the contents of this writer's buffer.
+   *
+   * @return byte buffer contents
+   */
+  public byte[] toArray() {
+    byte[] ret = new byte[offset];
+    System.arraycopy(data, 0, ret, 0, offset);
+    return ret;
+  }
+
+  /**
+   * Resets the internal pointer into the buffer.
+   */
+  public void reset() {
+    offset = 0;
+    if (data.length >= LARGE_BUFFER_SIZE) {
+      data = new byte[this.initialCapacity];
+    }
+  }
+
+  /**
+   * @return The current offset into the backing array.
+   */
+  public int getOffset() {
+    return offset;
+  }
+
+  /**
+   * @return The current length of the backing array.
+   */
+  public long getSize() {
+    return data.length;
+  }
+
+  /**
+   * Determines what next array size should be by rounding up to next power of two.
+   *
+   * @param i current array size
+   * @return next array size
+   * @throws IllegalArgumentException if i is negative
+   */
+  public static int nextArraySize(int i) {
+    if (i < 0) {
+      throw new IllegalArgumentException();
+    }
+
+    if (i > (1 << 30)) {
+      return Integer.MAX_VALUE; // this is the next power of 2 minus one... a special case
+    }
+
+    if (i == 0) {
+      return 1;
+    }
+
+    // round up to next power of two
+    int ret = i;
+    ret--;
+    ret |= ret >> 1;
+    ret |= ret >> 2;
+    ret |= ret >> 4;
+    ret |= ret >> 8;
+    ret |= ret >> 16;
+    ret++;
+
+    return ret;
+  }
+}
+
+// End UnsynchronizedBuffer.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/5cee486f/avatica/core/src/main/java/org/apache/calcite/avatica/util/package-info.java
----------------------------------------------------------------------
diff --git a/avatica/core/src/main/java/org/apache/calcite/avatica/util/package-info.java b/avatica/core/src/main/java/org/apache/calcite/avatica/util/package-info.java
new file mode 100644
index 0000000..eab457c
--- /dev/null
+++ b/avatica/core/src/main/java/org/apache/calcite/avatica/util/package-info.java
@@ -0,0 +1,24 @@
+/*
+ * 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.
+ */
+@PackageMarker
+package org.apache.calcite.avatica.util;
+
+// End package-info.java