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