From ba436a750ecd5d184427b2b4738c2a5b24dbb820 Mon Sep 17 00:00:00 2001 From: Costin Leau Date: Tue, 4 Sep 2018 16:53:45 +0300 Subject: [PATCH 1/6] SQL: Introduce INTERVAL support Introduce INTERVAL as a DataType Add INTERVAL to the grammar which supports the standard SQL declaration (without precision): > INTERVAL '1 23:45:01.123456789' DAY TO SECOND but also number for single unit intervals: > INTERVAL 1 YEAR as well as the plurals of the units: > INTERVAL 2 YEARS Interval are internally supported as just another Literal being backed by java.time.Period and java.time.Duration Move JDBC away from JDBCType enum to SQLType interface Refactor DataType by: - move it into server core - add dedicated (and much simpler) JDBC type Improve internal JDBC conversion by normalizing on the DataType Rename JDBC columnInfo to JdbcColumnInfo to differentiate between it and the SQL ColumnInfo Left to do: 4. add basic operation +/- between intervals 5. add basic operation +/- between timestamps and intervals 6. add conversion of intervals and other datatypes (see what functions make sense) 7. introduce now/current date/current time/current timestamp methods Fix #29990 --- .../sql/jdbc/jdbc/JdbcDatabaseMetaData.java | 35 +- .../sql/jdbc/jdbc/JdbcParameterMetaData.java | 6 +- .../sql/jdbc/jdbc/JdbcPreparedStatement.java | 65 +- .../xpack/sql/jdbc/jdbc/JdbcResultSet.java | 23 +- .../sql/jdbc/jdbc/JdbcResultSetMetaData.java | 12 +- .../xpack/sql/jdbc/jdbc/PreparedQuery.java | 16 +- .../jdbc/jdbc/SqlQueryParameterAnalyzer.java | 2 +- .../xpack/sql/jdbc/jdbc/TypeConverter.java | 626 +++-- .../xpack/sql/jdbc/jdbc/TypeUtils.java | 177 ++ .../xpack/sql/jdbc/net/client/Cursor.java | 4 +- .../sql/jdbc/net/client/DefaultCursor.java | 8 +- .../sql/jdbc/net/client/JdbcHttpClient.java | 20 +- .../{ColumnInfo.java => JdbcColumnInfo.java} | 11 +- .../xpack/sql/jdbc/type/DataType.java | 67 + .../xpack/sql/jdbc/type/ExtraTypes.java | 33 + .../jdbc/jdbc/JdbcPreparedStatementTests.java | 168 +- .../sql/jdbc/jdbc/TypeConverterTests.java | 24 +- .../jdbc/net/protocol/ColumnInfoTests.java | 25 +- .../xpack/sql/qa/jdbc/DataLoader.java | 3 +- .../xpack/sql/qa/jdbc/JdbcAssert.java | 33 +- .../xpack/sql/qa/jdbc/JdbcTestUtils.java | 12 +- .../xpack/sql/qa/jdbc/ResultSetTestCase.java | 72 +- .../sql/qa/jdbc/SimpleExampleTestCase.java | 2 +- .../main/resources/datetime-interval.csv-spec | 133 ++ .../sql/action/AbstractSqlQueryRequest.java | 17 +- .../xpack/sql/action/CliFormatter.java | 13 +- .../xpack/sql/action/SqlQueryRequest.java | 3 +- .../xpack/sql/action/SqlQueryResponse.java | 8 +- .../sql/action/SqlQueryRequestTests.java | 11 +- .../sql/action/SqlQueryResponseTests.java | 5 +- .../command/ServerQueryCliCommandTests.java | 9 +- .../xpack/sql/proto/ColumnInfo.java | 24 +- .../xpack/sql/proto/ProtoUtils.java | 1 - .../xpack/sql/proto/SqlTypedParamValue.java | 21 +- .../xpack/sql/proto/StringUtils.java | 76 + .../sql/proto/type/ExtendedJDBCType.java | 52 + .../xpack/sql/type/DataType.java | 249 -- x-pack/plugin/sql/src/main/antlr/SqlBase.g4 | 52 +- .../plugin/sql/src/main/antlr/SqlBase.tokens | 368 +-- .../sql/src/main/antlr/SqlBaseLexer.tokens | 366 +-- .../xpack/sql/analysis/analyzer/Analyzer.java | 2 +- .../sql/analysis/index/IndexResolver.java | 2 +- .../search/extractor/FieldHitExtractor.java | 17 +- .../xpack/sql/expression/Expressions.java | 2 +- .../xpack/sql/expression/Literal.java | 2 +- .../sql/expression/literal/Interval.java | 74 + .../expression/literal/IntervalDayTime.java | 21 + .../sql/expression/literal/IntervalUtils.java | 372 +++ .../expression/literal/IntervalYearMonth.java | 21 + .../xpack/sql/parser/ExpressionBuilder.java | 194 +- .../xpack/sql/parser/SqlBaseBaseListener.java | 48 + .../xpack/sql/parser/SqlBaseBaseVisitor.java | 28 + .../xpack/sql/parser/SqlBaseLexer.java | 743 +++--- .../xpack/sql/parser/SqlBaseListener.java | 42 + .../xpack/sql/parser/SqlBaseParser.java | 2040 ++++++++++------- .../xpack/sql/parser/SqlBaseVisitor.java | 25 + .../plan/logical/command/sys/SysColumns.java | 2 +- .../plan/logical/command/sys/SysTypes.java | 4 +- .../sql/plugin/TransportSqlQueryAction.java | 2 +- .../sql/querydsl/agg/GroupByScriptKey.java | 4 +- .../xpack/sql/type/DataType.java | 249 ++ .../xpack/sql/type/DataTypeConversion.java | 36 +- .../xpack/sql/type/DataTypes.java | 16 +- .../xpack/sql/type/ExtTypes.java | 52 + .../elasticsearch/xpack/sql/type/Types.java | 2 +- .../xpack/sql/util/StringUtils.java | 36 + .../xpack/sql/action/CliFormatterTests.java | 14 +- .../xpack/sql/action/SqlActionIT.java | 6 +- .../sql/execution/search/CursorTests.java | 5 +- .../xpack/sql/expression/ParameterTests.java | 16 +- .../literal/IntervalUtilsTests.java | 211 ++ .../sql/parser/EscapedFunctionsTests.java | 6 +- .../xpack/sql/parser/ExpressionTests.java | 64 +- .../sql/parser/LikeEscapingParsingTests.java | 2 +- .../logical/command/sys/SysParserTests.java | 6 +- .../logical/command/sys/SysTablesTests.java | 2 +- .../sql/type/DataTypeConversionTests.java | 2 +- .../xpack/sql/type/DataTypesTests.java | 2 +- 78 files changed, 4675 insertions(+), 2549 deletions(-) create mode 100644 x-pack/plugin/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/jdbc/TypeUtils.java rename x-pack/plugin/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/net/protocol/{ColumnInfo.java => JdbcColumnInfo.java} (90%) create mode 100644 x-pack/plugin/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/type/DataType.java create mode 100644 x-pack/plugin/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/type/ExtraTypes.java create mode 100644 x-pack/plugin/sql/qa/src/main/resources/datetime-interval.csv-spec create mode 100644 x-pack/plugin/sql/sql-proto/src/main/java/org/elasticsearch/xpack/sql/proto/StringUtils.java create mode 100644 x-pack/plugin/sql/sql-proto/src/main/java/org/elasticsearch/xpack/sql/proto/type/ExtendedJDBCType.java delete mode 100644 x-pack/plugin/sql/sql-proto/src/main/java/org/elasticsearch/xpack/sql/type/DataType.java create mode 100644 x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/literal/Interval.java create mode 100644 x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/literal/IntervalDayTime.java create mode 100644 x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/literal/IntervalUtils.java create mode 100644 x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/literal/IntervalYearMonth.java create mode 100644 x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/type/DataType.java create mode 100644 x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/type/ExtTypes.java create mode 100644 x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/expression/literal/IntervalUtilsTests.java diff --git a/x-pack/plugin/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/jdbc/JdbcDatabaseMetaData.java b/x-pack/plugin/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/jdbc/JdbcDatabaseMetaData.java index 970a188df7c52..5a044bb35a6af 100644 --- a/x-pack/plugin/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/jdbc/JdbcDatabaseMetaData.java +++ b/x-pack/plugin/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/jdbc/JdbcDatabaseMetaData.java @@ -9,7 +9,8 @@ import org.elasticsearch.xpack.sql.client.Version; import org.elasticsearch.xpack.sql.jdbc.JdbcSQLException; import org.elasticsearch.xpack.sql.jdbc.net.client.Cursor; -import org.elasticsearch.xpack.sql.jdbc.net.protocol.ColumnInfo; +import org.elasticsearch.xpack.sql.jdbc.net.protocol.JdbcColumnInfo; +import org.elasticsearch.xpack.sql.jdbc.type.DataType; import java.sql.Connection; import java.sql.DatabaseMetaData; @@ -19,7 +20,6 @@ import java.sql.RowIdLifetime; import java.sql.SQLException; import java.sql.SQLFeatureNotSupportedException; -import java.sql.SQLType; import java.util.ArrayList; import java.util.List; @@ -764,7 +764,7 @@ public ResultSet getSchemas() throws SQLException { @Override public ResultSet getSchemas(String catalog, String schemaPattern) throws SQLException { - List info = columnInfo("SCHEMATA", + List info = columnInfo("SCHEMATA", "TABLE_SCHEM", "TABLE_CATALOG"); if (!isDefaultCatalog(catalog) || !isDefaultSchema(schemaPattern)) { @@ -1117,23 +1117,28 @@ public boolean generatedKeyAlwaysReturned() throws SQLException { return false; } - private static List columnInfo(String tableName, Object... cols) throws JdbcSQLException { - List columns = new ArrayList<>(); + private static List columnInfo(String tableName, Object... cols) throws JdbcSQLException { + List columns = new ArrayList<>(); for (int i = 0; i < cols.length; i++) { Object obj = cols[i]; if (obj instanceof String) { String name = obj.toString(); - SQLType type = JDBCType.VARCHAR; + DataType type = DataType.KEYWORD; if (i + 1 < cols.length) { + Object next = cols[i + 1]; // check if the next item it's a type - if (cols[i + 1] instanceof SQLType) { - type = (SQLType) cols[i + 1]; - i++; + if (next instanceof DataType || next instanceof JDBCType) { + try { + type = TypeUtils.of((JDBCType) next); + i++; + } catch (SQLException ex) { + throw new JdbcSQLException(ex, "Invalid metadata schema definition"); + } } // it's not, use the default and move on } - columns.add(new ColumnInfo(name, type, tableName, "INFORMATION_SCHEMA", "", "", 0)); + columns.add(new JdbcColumnInfo(name, type, tableName, "INFORMATION_SCHEMA", "", "", 0)); } else { throw new JdbcSQLException("Invalid metadata schema definition"); @@ -1146,28 +1151,28 @@ private static ResultSet emptySet(JdbcConfiguration cfg, String tableName, Objec return new JdbcResultSet(cfg, null, new InMemoryCursor(columnInfo(tableName, cols), null)); } - private static ResultSet emptySet(JdbcConfiguration cfg, List columns) { + private static ResultSet emptySet(JdbcConfiguration cfg, List columns) { return memorySet(cfg, columns, null); } - private static ResultSet memorySet(JdbcConfiguration cfg, List columns, Object[][] data) { + private static ResultSet memorySet(JdbcConfiguration cfg, List columns, Object[][] data) { return new JdbcResultSet(cfg, null, new InMemoryCursor(columns, data)); } static class InMemoryCursor implements Cursor { - private final List columns; + private final List columns; private final Object[][] data; private int row = -1; - InMemoryCursor(List info, Object[][] data) { + InMemoryCursor(List info, Object[][] data) { this.columns = info; this.data = data; } @Override - public List columns() { + public List columns() { return columns; } diff --git a/x-pack/plugin/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/jdbc/JdbcParameterMetaData.java b/x-pack/plugin/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/jdbc/JdbcParameterMetaData.java index 988fa6da047fd..cedc9201e651c 100644 --- a/x-pack/plugin/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/jdbc/JdbcParameterMetaData.java +++ b/x-pack/plugin/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/jdbc/JdbcParameterMetaData.java @@ -32,7 +32,7 @@ public int isNullable(int param) throws SQLException { @Override public boolean isSigned(int param) throws SQLException { - return TypeConverter.isSigned(paramInfo(param).type); + return TypeUtils.isSigned(paramInfo(param).type); } @Override @@ -49,7 +49,7 @@ public int getScale(int param) throws SQLException { @Override public int getParameterType(int param) throws SQLException { - return paramInfo(param).type.getVendorTypeNumber(); + return paramInfo(param).type.getVendorTypeNumber().intValue(); } @Override @@ -59,7 +59,7 @@ public String getParameterTypeName(int param) throws SQLException { @Override public String getParameterClassName(int param) throws SQLException { - return TypeConverter.classNameOf(paramInfo(param).type); + return TypeUtils.classOf(paramInfo(param).type).getName(); } @Override diff --git a/x-pack/plugin/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/jdbc/JdbcPreparedStatement.java b/x-pack/plugin/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/jdbc/JdbcPreparedStatement.java index dc3dac978a728..6e4effb6804d4 100644 --- a/x-pack/plugin/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/jdbc/JdbcPreparedStatement.java +++ b/x-pack/plugin/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/jdbc/JdbcPreparedStatement.java @@ -5,7 +5,7 @@ */ package org.elasticsearch.xpack.sql.jdbc.jdbc; -import org.elasticsearch.xpack.sql.type.DataType; +import org.elasticsearch.xpack.sql.jdbc.type.DataType; import java.io.InputStream; import java.io.Reader; @@ -15,7 +15,6 @@ import java.sql.Blob; import java.sql.Clob; import java.sql.Date; -import java.sql.JDBCType; import java.sql.NClob; import java.sql.ParameterMetaData; import java.sql.PreparedStatement; @@ -23,7 +22,6 @@ import java.sql.ResultSet; import java.sql.ResultSetMetaData; import java.sql.RowId; -import java.sql.SQLDataException; import java.sql.SQLException; import java.sql.SQLFeatureNotSupportedException; import java.sql.SQLType; @@ -70,7 +68,11 @@ public int executeUpdate() throws SQLException { throw new SQLFeatureNotSupportedException("Writes not supported"); } - private void setParam(int parameterIndex, Object value, SQLType type) throws SQLException { + private void setParam(int parameterIndex, Object value, int sqlType) throws SQLException { + setParam(parameterIndex, value, TypeUtils.of(sqlType)); + } + + private void setParam(int parameterIndex, Object value, DataType type) throws SQLException { checkOpen(); if (parameterIndex < 0 || parameterIndex > query.paramCount()) { @@ -83,7 +85,7 @@ private void setParam(int parameterIndex, Object value, SQLType type) throws SQL @Override public void setNull(int parameterIndex, int sqlType) throws SQLException { - setParam(parameterIndex, null, JDBCType.valueOf(sqlType)); + setParam(parameterIndex, null, sqlType); } @Override @@ -179,10 +181,15 @@ public void setObject(int parameterIndex, Object x, int targetSqlType) throws SQ setObject(parameterIndex, x, targetSqlType, 0); } + @Override + public void setObject(int parameterIndex, Object x, SQLType targetSqlType) throws SQLException { + setObject(parameterIndex, x, targetSqlType, 0); + } + @Override public void setObject(int parameterIndex, Object x) throws SQLException { if (x == null) { - setParam(parameterIndex, null, JDBCType.NULL); + setParam(parameterIndex, null, DataType.NULL); return; } @@ -190,9 +197,10 @@ public void setObject(int parameterIndex, Object x) throws SQLException { // {@code java.sql.Array} etc) will generate the correct exception message. Otherwise, the method call // {@code TypeConverter.fromJavaToJDBC(x.getClass())} will report the implementing class as not being supported. checkKnownUnsupportedTypes(x); - setObject(parameterIndex, x, TypeConverter.fromJavaToJDBC(x.getClass()).getVendorTypeNumber(), 0); + setObject(parameterIndex, x, TypeUtils.of(x.getClass()).getVendorTypeNumber(), 0); } + @Override public void addBatch() throws SQLException { throw new SQLFeatureNotSupportedException("Batching not supported"); @@ -327,29 +335,30 @@ public void setSQLXML(int parameterIndex, SQLXML xmlObject) throws SQLException @Override public void setObject(int parameterIndex, Object x, int targetSqlType, int scaleOrLength) throws SQLException { + setObject(parameterIndex, x, TypeUtils.asSqlType(targetSqlType), scaleOrLength); + } + + @Override + public void setObject(int parameterIndex, Object x, SQLType targetSqlType, int scaleOrLength) throws SQLException { + setObject(parameterIndex, x, TypeUtils.of(targetSqlType), targetSqlType.getName()); + } + + private void setObject(int parameterIndex, Object x, DataType dataType, String typeString) throws SQLException { checkOpen(); - JDBCType targetJDBCType; - try { - // this is also a way to check early for the validity of the desired sql type - targetJDBCType = JDBCType.valueOf(targetSqlType); - } catch (IllegalArgumentException e) { - throw new SQLDataException(e.getMessage()); - } - // set the null value on the type and exit if (x == null) { - setParam(parameterIndex, null, JDBCType.valueOf(targetSqlType)); + setParam(parameterIndex, null, dataType); return; } checkKnownUnsupportedTypes(x); if (x instanceof byte[]) { - if (targetJDBCType != JDBCType.VARBINARY) { + if (dataType != DataType.BINARY) { throw new SQLFeatureNotSupportedException( - "Conversion from type byte[] to " + targetJDBCType + " not supported"); + "Conversion from type [byte[]] to [" + typeString + "] not supported"); } - setParam(parameterIndex, x, JDBCType.VARBINARY); + setParam(parameterIndex, x, DataType.BINARY); return; } @@ -360,7 +369,7 @@ public void setObject(int parameterIndex, Object x, int targetSqlType, int scale || x instanceof Time || x instanceof java.util.Date) { - if (targetJDBCType == JDBCType.TIMESTAMP) { + if (dataType == DataType.DATE) { // converting to {@code java.util.Date} because this is the type supported by {@code XContentBuilder} for serialization java.util.Date dateToSet; if (x instanceof Timestamp) { @@ -381,15 +390,15 @@ public void setObject(int parameterIndex, Object x, int targetSqlType, int scale dateToSet = (java.util.Date) x; } - setParam(parameterIndex, dateToSet, JDBCType.TIMESTAMP); + setParam(parameterIndex, dateToSet, dataType); return; - } else if (targetJDBCType == JDBCType.VARCHAR) { - setParam(parameterIndex, String.valueOf(x), JDBCType.VARCHAR); + } else if (TypeUtils.isString(dataType)) { + setParam(parameterIndex, String.valueOf(x), dataType); return; } // anything else other than VARCHAR and TIMESTAMP is not supported in this JDBC driver throw new SQLFeatureNotSupportedException( - "Conversion from type " + x.getClass().getName() + " to " + targetJDBCType + " not supported"); + "Conversion from type [" + x.getClass().getName() + "] to [" + typeString + "] not supported"); } if (x instanceof Boolean @@ -401,13 +410,13 @@ public void setObject(int parameterIndex, Object x, int targetSqlType, int scale || x instanceof Double || x instanceof String) { setParam(parameterIndex, - TypeConverter.convert(x, TypeConverter.fromJavaToJDBC(x.getClass()), DataType.fromJdbcTypeToJava(targetJDBCType)), - JDBCType.valueOf(targetSqlType)); + TypeConverter.convert(x, TypeUtils.of(x.getClass()), (Class) TypeUtils.classOf(dataType), typeString), + dataType); return; } throw new SQLFeatureNotSupportedException( - "Conversion from type " + x.getClass().getName() + " to " + targetJDBCType + " not supported"); + "Conversion from type [" + x.getClass().getName() + "] to [" + typeString + "] not supported"); } private void checkKnownUnsupportedTypes(Object x) throws SQLFeatureNotSupportedException { @@ -417,7 +426,7 @@ private void checkKnownUnsupportedTypes(Object x) throws SQLFeatureNotSupportedE for (Class clazz:unsupportedTypes) { if (clazz.isAssignableFrom(x.getClass())) { - throw new SQLFeatureNotSupportedException("Objects of type " + clazz.getName() + " are not supported"); + throw new SQLFeatureNotSupportedException("Objects of type [" + clazz.getName() + "] are not supported"); } } } diff --git a/x-pack/plugin/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/jdbc/JdbcResultSet.java b/x-pack/plugin/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/jdbc/JdbcResultSet.java index a289991853fde..60c699d547e13 100644 --- a/x-pack/plugin/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/jdbc/JdbcResultSet.java +++ b/x-pack/plugin/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/jdbc/JdbcResultSet.java @@ -6,8 +6,9 @@ package org.elasticsearch.xpack.sql.jdbc.jdbc; import org.elasticsearch.xpack.sql.jdbc.net.client.Cursor; -import org.elasticsearch.xpack.sql.jdbc.net.protocol.ColumnInfo; +import org.elasticsearch.xpack.sql.jdbc.net.protocol.JdbcColumnInfo; import org.elasticsearch.xpack.sql.jdbc.net.protocol.Nullable; +import org.elasticsearch.xpack.sql.jdbc.type.DataType; import java.io.InputStream; import java.io.Reader; @@ -17,7 +18,6 @@ import java.sql.Blob; import java.sql.Clob; import java.sql.Date; -import java.sql.JDBCType; import java.sql.NClob; import java.sql.Ref; import java.sql.ResultSet; @@ -25,7 +25,6 @@ import java.sql.RowId; import java.sql.SQLException; import java.sql.SQLFeatureNotSupportedException; -import java.sql.SQLType; import java.sql.SQLWarning; import java.sql.SQLXML; import java.sql.Statement; @@ -63,7 +62,7 @@ class JdbcResultSet implements ResultSet, JdbcWrapper { // TODO: should we consider the locale as well? this.defaultCalendar = Calendar.getInstance(cfg.timeZone(), Locale.ROOT); - List columns = cursor.columns(); + List columns = cursor.columns(); for (int i = 0; i < columns.size(); i++) { nameToIndex.put(columns.get(i).name, Integer.valueOf(i + 1)); } @@ -246,12 +245,12 @@ public Date getDate(String columnLabel) throws SQLException { private Long dateTime(int columnIndex) throws SQLException { Object val = column(columnIndex); - SQLType type = cursor.columns().get(columnIndex - 1).type; + DataType type = cursor.columns().get(columnIndex - 1).type; try { // TODO: the B6 appendix of the jdbc spec does mention CHAR, VARCHAR, LONGVARCHAR, DATE, TIMESTAMP as supported // jdbc types that should be handled by getDate and getTime methods. From all of those we support VARCHAR and // TIMESTAMP. Should we consider the VARCHAR conversion as a later enhancement? - if (JDBCType.TIMESTAMP.equals(type)) { + if (DataType.DATE == type) { // the cursor can return an Integer if the date-since-epoch is small enough, XContentParser (Jackson) will // return the "smallest" data type for numbers when parsing // TODO: this should probably be handled server side @@ -328,20 +327,16 @@ public T getObject(int columnIndex, Class type) throws SQLException { } private T convert(int columnIndex, Class type) throws SQLException { - checkOpen(); - if (columnIndex < 1 || columnIndex > cursor.columnSize()) { - throw new SQLException("Invalid column index [" + columnIndex + "]"); - } - Object val = column(columnIndex); if (val == null) { return null; } - SQLType columnType = cursor.columns().get(columnIndex - 1).type; - - return TypeConverter.convert(val, columnType, type); + DataType columnType = cursor.columns().get(columnIndex - 1).type; + String typeString = type != null ? type.getSimpleName() : columnType.getName(); + + return TypeConverter.convert(val, columnType, type, typeString); } @Override diff --git a/x-pack/plugin/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/jdbc/JdbcResultSetMetaData.java b/x-pack/plugin/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/jdbc/JdbcResultSetMetaData.java index ed2b899e22a52..1fbbedc7f6ea0 100644 --- a/x-pack/plugin/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/jdbc/JdbcResultSetMetaData.java +++ b/x-pack/plugin/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/jdbc/JdbcResultSetMetaData.java @@ -5,7 +5,7 @@ */ package org.elasticsearch.xpack.sql.jdbc.jdbc; -import org.elasticsearch.xpack.sql.jdbc.net.protocol.ColumnInfo; +import org.elasticsearch.xpack.sql.jdbc.net.protocol.JdbcColumnInfo; import java.sql.ResultSetMetaData; import java.sql.SQLException; @@ -17,9 +17,9 @@ class JdbcResultSetMetaData implements ResultSetMetaData, JdbcWrapper { private final JdbcResultSet rs; - private final List columns; + private final List columns; - JdbcResultSetMetaData(JdbcResultSet rs, List columns) { + JdbcResultSetMetaData(JdbcResultSet rs, List columns) { this.rs = rs; this.columns = columns; } @@ -62,7 +62,7 @@ public int isNullable(int column) throws SQLException { @Override public boolean isSigned(int column) throws SQLException { - return TypeConverter.isSigned(column(column).type); + return TypeUtils.isSigned(column(column).type); } @Override @@ -137,7 +137,7 @@ public boolean isDefinitelyWritable(int column) throws SQLException { @Override public String getColumnClassName(int column) throws SQLException { - return TypeConverter.classNameOf(column(column).type); + return TypeUtils.classOf(column(column).type).getName(); } private void checkOpen() throws SQLException { @@ -146,7 +146,7 @@ private void checkOpen() throws SQLException { } } - private ColumnInfo column(int column) throws SQLException { + private JdbcColumnInfo column(int column) throws SQLException { checkOpen(); if (column < 1 || column > columns.size()) { throw new SQLException("Invalid column index [" + column + "]"); diff --git a/x-pack/plugin/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/jdbc/PreparedQuery.java b/x-pack/plugin/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/jdbc/PreparedQuery.java index ab459e90d969f..5ab862579888c 100644 --- a/x-pack/plugin/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/jdbc/PreparedQuery.java +++ b/x-pack/plugin/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/jdbc/PreparedQuery.java @@ -6,12 +6,10 @@ package org.elasticsearch.xpack.sql.jdbc.jdbc; import org.elasticsearch.xpack.sql.jdbc.JdbcSQLException; +import org.elasticsearch.xpack.sql.jdbc.type.DataType; import org.elasticsearch.xpack.sql.proto.SqlTypedParamValue; -import org.elasticsearch.xpack.sql.type.DataType; -import java.sql.JDBCType; import java.sql.SQLException; -import java.sql.SQLType; import java.util.Arrays; import java.util.List; import java.util.stream.Collectors; @@ -19,10 +17,10 @@ class PreparedQuery { static class ParamInfo { - SQLType type; + DataType type; Object value; - ParamInfo(Object value, SQLType type) { + ParamInfo(Object value, DataType type) { this.value = value; this.type = type; } @@ -44,7 +42,7 @@ ParamInfo getParam(int param) throws JdbcSQLException { return params[param - 1]; } - void setParam(int param, Object value, SQLType type) throws JdbcSQLException { + void setParam(int param, Object value, DataType type) throws JdbcSQLException { if (param < 1 || param > params.length) { throw new JdbcSQLException("Invalid parameter index [" + param + "]"); } @@ -58,7 +56,7 @@ int paramCount() { void clearParams() { for (int i = 0; i < params.length; i++) { - params[i] = new ParamInfo(null, JDBCType.VARCHAR); + params[i] = new ParamInfo(null, DataType.KEYWORD); } } @@ -74,7 +72,7 @@ String sql() { */ List params() { return Arrays.stream(this.params).map( - paramInfo -> new SqlTypedParamValue(DataType.fromJdbcType(paramInfo.type), paramInfo.value) + paramInfo -> new SqlTypedParamValue(paramInfo.type.name(), paramInfo.value) ).collect(Collectors.toList()); } @@ -87,4 +85,4 @@ public String toString() { static PreparedQuery prepare(String sql) throws SQLException { return new PreparedQuery(sql, SqlQueryParameterAnalyzer.parametersCount(sql)); } -} +} \ No newline at end of file diff --git a/x-pack/plugin/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/jdbc/SqlQueryParameterAnalyzer.java b/x-pack/plugin/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/jdbc/SqlQueryParameterAnalyzer.java index 8278857bcfa75..e87ad75e16f19 100644 --- a/x-pack/plugin/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/jdbc/SqlQueryParameterAnalyzer.java +++ b/x-pack/plugin/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/jdbc/SqlQueryParameterAnalyzer.java @@ -14,7 +14,7 @@ * in a sense that it can accept ? in the places where it's not allowed by the server (for example as a index name), but that's * a reasonable compromise to avoid sending the prepared statement server to the server for extra validation. */ -public final class SqlQueryParameterAnalyzer { +final class SqlQueryParameterAnalyzer { private SqlQueryParameterAnalyzer() { diff --git a/x-pack/plugin/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/jdbc/TypeConverter.java b/x-pack/plugin/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/jdbc/TypeConverter.java index 940e2c757df44..60f7fd5777cc9 100644 --- a/x-pack/plugin/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/jdbc/TypeConverter.java +++ b/x-pack/plugin/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/jdbc/TypeConverter.java @@ -5,30 +5,25 @@ */ package org.elasticsearch.xpack.sql.jdbc.jdbc; -import org.elasticsearch.xpack.sql.jdbc.JdbcSQLException; -import org.elasticsearch.xpack.sql.type.DataType; +import org.elasticsearch.xpack.sql.jdbc.type.DataType; import java.sql.Date; -import java.sql.JDBCType; import java.sql.SQLException; import java.sql.SQLFeatureNotSupportedException; -import java.sql.SQLType; import java.sql.Time; import java.sql.Timestamp; +import java.time.Duration; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.LocalTime; import java.time.OffsetDateTime; import java.time.OffsetTime; +import java.time.Period; import java.time.ZoneOffset; import java.time.ZonedDateTime; -import java.util.Arrays; import java.util.Calendar; -import java.util.Collections; import java.util.GregorianCalendar; import java.util.Locale; -import java.util.Map; -import java.util.Map.Entry; import java.util.function.Function; import static java.lang.String.format; @@ -40,7 +35,6 @@ import static java.util.Calendar.MONTH; import static java.util.Calendar.SECOND; import static java.util.Calendar.YEAR; -import static java.util.stream.Collectors.toMap; /** * Conversion utilities for conversion of JDBC types to Java type and back @@ -55,24 +49,6 @@ final class TypeConverter { private TypeConverter() {} private static final long DAY_IN_MILLIS = 60 * 60 * 24 * 1000; - private static final Map, SQLType> javaToJDBC; - - - static { - Map, SQLType> aMap = Arrays.stream(DataType.values()) - .filter(dataType -> dataType.javaClass() != null - && dataType != DataType.HALF_FLOAT - && dataType != DataType.IP - && dataType != DataType.SCALED_FLOAT - && dataType != DataType.TEXT) - .collect(toMap(dataType -> dataType.javaClass(), dataType -> dataType.jdbcType)); - // apart from the mappings in {@code DataType} three more Java classes can be mapped to a {@code JDBCType.TIMESTAMP} - // according to B-4 table from the jdbc4.2 spec - aMap.put(Calendar.class, JDBCType.TIMESTAMP); - aMap.put(java.util.Date.class, JDBCType.TIMESTAMP); - aMap.put(LocalDateTime.class, JDBCType.TIMESTAMP); - javaToJDBC = Collections.unmodifiableMap(aMap); - } /** * Converts millisecond after epoc to date @@ -139,164 +115,126 @@ static long convertFromCalendarToUTC(long value, Calendar cal) { * Converts object val from columnType to type */ @SuppressWarnings("unchecked") - static T convert(Object val, SQLType columnType, Class type) throws SQLException { + static T convert(Object val, DataType columnType, Class type, String typeString) throws SQLException { if (type == null) { - return (T) convert(val, columnType); + return (T) convert(val, columnType, typeString); } // converting a Long to a Timestamp shouldn't be possible according to the spec, // it feels a little brittle to check this scenario here and I don't particularly like it // TODO: can we do any better or should we go over the spec and allow getLong(date) to be valid? - if (!(type == Long.class && columnType == JDBCType.TIMESTAMP) && type.isInstance(val)) { + if (!(type == Long.class && columnType == DataType.DATE) && type.isInstance(val)) { try { return type.cast(val); } catch (ClassCastException cce) { - throw new SQLException(format(Locale.ROOT, "Unable to convert value [%.128s] of type [%s] to a %s", val, - columnType.getName(), type.getName()), cce); + failConversion(val, columnType, typeString, type, cce); } } if (type == String.class) { - return (T) asString(convert(val, columnType)); + return (T) asString(convert(val, columnType, typeString)); } if (type == Boolean.class) { - return (T) asBoolean(val, columnType); + return (T) asBoolean(val, columnType, typeString); } if (type == Byte.class) { - return (T) asByte(val, columnType); + return (T) asByte(val, columnType, typeString); } if (type == Short.class) { - return (T) asShort(val, columnType); + return (T) asShort(val, columnType, typeString); } if (type == Integer.class) { - return (T) asInteger(val, columnType); + return (T) asInteger(val, columnType, typeString); } if (type == Long.class) { - return (T) asLong(val, columnType); + return (T) asLong(val, columnType, typeString); } if (type == Float.class) { - return (T) asFloat(val, columnType); + return (T) asFloat(val, columnType, typeString); } if (type == Double.class) { - return (T) asDouble(val, columnType); + return (T) asDouble(val, columnType, typeString); } if (type == Date.class) { - return (T) asDate(val, columnType); + return (T) asDate(val, columnType, typeString); } if (type == Time.class) { - return (T) asTime(val, columnType); + return (T) asTime(val, columnType, typeString); } if (type == Timestamp.class) { - return (T) asTimestamp(val, columnType); + return (T) asTimestamp(val, columnType, typeString); } if (type == byte[].class) { - return (T) asByteArray(val, columnType); + return (T) asByteArray(val, columnType, typeString); } // // JDK 8 types // if (type == LocalDate.class) { - return (T) asLocalDate(val, columnType); + return (T) asLocalDate(val, columnType, typeString); } if (type == LocalTime.class) { - return (T) asLocalTime(val, columnType); + return (T) asLocalTime(val, columnType, typeString); } if (type == LocalDateTime.class) { - return (T) asLocalDateTime(val, columnType); + return (T) asLocalDateTime(val, columnType, typeString); } if (type == OffsetTime.class) { - return (T) asOffsetTime(val, columnType); + return (T) asOffsetTime(val, columnType, typeString); } if (type == OffsetDateTime.class) { - return (T) asOffsetDateTime(val, columnType); + return (T) asOffsetDateTime(val, columnType, typeString); } - throw new SQLException(format(Locale.ROOT, "Unable to convert value [%.128s] of type [%s] to a %s", val, - columnType.getName(), type.getName())); - } - /** - * Translates numeric JDBC type into corresponding Java class - *

- * See {@link javax.sql.rowset.RowSetMetaDataImpl#getColumnClassName} and - * https://db.apache.org/derby/docs/10.5/ref/rrefjdbc20377.html - */ - public static String classNameOf(SQLType jdbcType) throws JdbcSQLException { - final DataType dataType; - try { - dataType = DataType.fromJdbcType(jdbcType); - } catch (IllegalArgumentException ex) { - // Convert unsupported exception to JdbcSQLException - throw new JdbcSQLException(ex, ex.getMessage()); - } - if (dataType.javaClass() == null) { - throw new JdbcSQLException("Unsupported JDBC type [" + jdbcType + "]"); - } - return dataType.javaClass().getName(); + return failConversion(val, columnType, typeString, type); } /** * Converts the object from JSON representation to the specified JDBCType - *

- * The returned types needs to correspond to ES-portion of classes returned by {@link TypeConverter#classNameOf} - */ - static Object convert(Object v, SQLType columnType) throws SQLException { - if (columnType instanceof JDBCType) { - switch ((JDBCType) columnType) { - case NULL: - return null; - case BOOLEAN: - case VARCHAR: - return v; // These types are already represented correctly in JSON - case TINYINT: - return ((Number) v).byteValue(); // Parser might return it as integer or long - need to update to the correct type - case SMALLINT: - return ((Number) v).shortValue(); // Parser might return it as integer or long - need to update to the correct type - case INTEGER: - return ((Number) v).intValue(); - case BIGINT: - return ((Number) v).longValue(); - case FLOAT: - case DOUBLE: - return doubleValue(v); // Double might be represented as string for infinity and NaN values - case REAL: - return floatValue(v); // Float might be represented as string for infinity and NaN values - case TIMESTAMP: - return new Timestamp(((Number) v).longValue()); - default: - throw new SQLException("Unexpected column type [" + columnType.getName() + "]"); - - } - } else { - throw new SQLException("Unexpected column type [" + columnType.getName() + "]"); - } - } - - /** - * Returns true if the type represents a signed number, false otherwise - *

- * It needs to support both params and column types */ - static boolean isSigned(SQLType jdbcType) throws SQLException { - final DataType dataType; - try { - dataType = DataType.fromJdbcType(jdbcType); - } catch (IllegalArgumentException ex) { - // Convert unsupported exception to JdbcSQLException - throw new JdbcSQLException(ex, ex.getMessage()); - } - return dataType.isSigned(); - } - + static Object convert(Object v, DataType columnType, String typeString) throws SQLException { + switch (columnType) { + case NULL: + return null; + case BOOLEAN: + case TEXT: + case KEYWORD: + return v; // These types are already represented correctly in JSON + case BYTE: + return ((Number) v).byteValue(); // Parser might return it as integer or long - need to update to the correct type + case SHORT: + return ((Number) v).shortValue(); // Parser might return it as integer or long - need to update to the correct type + case INTEGER: + return ((Number) v).intValue(); + case LONG: + return ((Number) v).longValue(); + case HALF_FLOAT: + case SCALED_FLOAT: + case DOUBLE: + return doubleValue(v); // Double might be represented as string for infinity and NaN values + case FLOAT: + return floatValue(v); // Float might be represented as string for infinity and NaN values + case DATE: + return new Timestamp(((Number) v).longValue()); + case INTERVAL_YEAR: + case INTERVAL_MONTH: + case INTERVAL_YEAR_TO_MONTH: + return Period.parse(v.toString()); + case INTERVAL_DAY: + case INTERVAL_HOUR: + case INTERVAL_MINUTE: + case INTERVAL_SECOND: + case INTERVAL_DAY_TO_HOUR: + case INTERVAL_DAY_TO_MINUTE: + case INTERVAL_DAY_TO_SECOND: + case INTERVAL_HOUR_TO_MINUTE: + case INTERVAL_HOUR_TO_SECOND: + case INTERVAL_MINUTE_TO_SECOND: + return Duration.parse(v.toString()); + default: + throw new SQLException("Unexpected column type [" + typeString + "]"); - static SQLType fromJavaToJDBC(Class clazz) throws SQLException { - for (Entry, SQLType> e : javaToJDBC.entrySet()) { - // java.util.Calendar from {@code javaToJDBC} is an abstract class and this method can be used with concrete classes as well - if (e.getKey().isAssignableFrom(clazz)) { - return e.getValue(); - } } - - throw new SQLFeatureNotSupportedException("Objects of type " + clazz.getName() + " are not supported"); } private static Double doubleValue(Object v) { @@ -335,247 +273,245 @@ private static String asString(Object nativeValue) { return nativeValue == null ? null : String.valueOf(nativeValue); } - private static Boolean asBoolean(Object val, SQLType columnType) throws SQLException { - if (columnType instanceof JDBCType) { - switch ((JDBCType) columnType) { - case BOOLEAN: - case TINYINT: - case SMALLINT: - case INTEGER: - case BIGINT: - case REAL: - case FLOAT: - case DOUBLE: - return Boolean.valueOf(Integer.signum(((Number) val).intValue()) != 0); - case VARCHAR: - return Boolean.valueOf((String) val); - default: - throw new SQLException( - format(Locale.ROOT, "Unable to convert value [%.128s] of type [%s] to a Boolean", val, columnType.getName())); - - } - } else { - throw new SQLException("Unexpected column type [" + columnType.getName() + "]"); - } - } - - private static Byte asByte(Object val, SQLType columnType) throws SQLException { - if (columnType instanceof JDBCType) { - switch ((JDBCType) columnType) { - case BOOLEAN: - return Byte.valueOf(((Boolean) val).booleanValue() ? (byte) 1 : (byte) 0); - case TINYINT: - case SMALLINT: - case INTEGER: - case BIGINT: - return safeToByte(((Number) val).longValue()); - case REAL: - case FLOAT: - case DOUBLE: - return safeToByte(safeToLong(((Number) val).doubleValue())); - case VARCHAR: - try { - return Byte.valueOf((String) val); - } catch (NumberFormatException e) { - throw new SQLException(format(Locale.ROOT, "Unable to convert value [%.128s] of type [VARCHAR] to a Byte", val), e); - } - default: - } - } - - throw new SQLException(format(Locale.ROOT, "Unable to convert value [%.128s] of type [%s] to a Byte", val, columnType.getName())); - } - - private static Short asShort(Object val, SQLType columnType) throws SQLException { - if (columnType instanceof JDBCType) { - switch ((JDBCType) columnType) { - case BOOLEAN: - return Short.valueOf(((Boolean) val).booleanValue() ? (short) 1 : (short) 0); - case TINYINT: - case SMALLINT: - case INTEGER: - case BIGINT: - return safeToShort(((Number) val).longValue()); - case REAL: - case FLOAT: - case DOUBLE: - return safeToShort(safeToLong(((Number) val).doubleValue())); - case VARCHAR: - try { - return Short.valueOf((String) val); - } catch (NumberFormatException e) { - throw new SQLException(format(Locale.ROOT, "Unable to convert value [%.128s] of type [VARCHAR] to a Short", val), - e); - } - default: - } - } - throw new SQLException(format(Locale.ROOT, "Unable to convert value [%.128s] of type [%s] to a Short", val, columnType.getName())); - } - - private static Integer asInteger(Object val, SQLType columnType) throws SQLException { - if (columnType instanceof JDBCType) { - switch ((JDBCType) columnType) { - case BOOLEAN: - return Integer.valueOf(((Boolean) val).booleanValue() ? 1 : 0); - case TINYINT: - case SMALLINT: - case INTEGER: - case BIGINT: - return safeToInt(((Number) val).longValue()); - case REAL: - case FLOAT: - case DOUBLE: - return safeToInt(safeToLong(((Number) val).doubleValue())); - case VARCHAR: - try { - return Integer.valueOf((String) val); - } catch (NumberFormatException e) { - throw new SQLException( - format(Locale.ROOT, "Unable to convert value [%.128s] of type [VARCHAR] to an Integer", val), e); - } - default: - } - - } - throw new SQLException( - format(Locale.ROOT, "Unable to convert value [%.128s] of type [%s] to an Integer", val, columnType.getName())); - } - - private static Long asLong(Object val, SQLType columnType) throws SQLException { - if (columnType instanceof JDBCType) { - switch ((JDBCType) columnType) { - case BOOLEAN: - return Long.valueOf(((Boolean) val).booleanValue() ? 1 : 0); - case TINYINT: - case SMALLINT: - case INTEGER: - case BIGINT: - return Long.valueOf(((Number) val).longValue()); - case REAL: - case FLOAT: - case DOUBLE: - return safeToLong(((Number) val).doubleValue()); - //TODO: should we support conversion to TIMESTAMP? - //The spec says that getLong() should support the following types conversions: - //TINYINT, SMALLINT, INTEGER, BIGINT, REAL, FLOAT, DOUBLE, DECIMAL, NUMERIC, BIT, BOOLEAN, CHAR, VARCHAR, LONGVARCHAR - //case TIMESTAMP: - // return ((Number) val).longValue(); - case VARCHAR: - try { - return Long.valueOf((String) val); - } catch (NumberFormatException e) { - throw new SQLException(format(Locale.ROOT, "Unable to convert value [%.128s] of type [VARCHAR] to a Long", val), e); - } - default: - } - } - throw new SQLException(format(Locale.ROOT, "Unable to convert value [%.128s] of type [%s] to a Long", val, columnType.getName())); - } - - private static Float asFloat(Object val, SQLType columnType) throws SQLException { - if (columnType instanceof JDBCType) { - switch ((JDBCType) columnType) { - case BOOLEAN: - return Float.valueOf(((Boolean) val).booleanValue() ? 1 : 0); - case TINYINT: - case SMALLINT: - case INTEGER: - case BIGINT: - return Float.valueOf(((Number) val).longValue()); - case REAL: - case FLOAT: - case DOUBLE: - return Float.valueOf((((float) ((Number) val).doubleValue()))); - case VARCHAR: - try { - return Float.valueOf((String) val); - } catch (NumberFormatException e) { - throw new SQLException(format(Locale.ROOT, "Unable to convert value [%.128s] of type [VARCHAR] to a Float", val), - e); - } - default: - } - } - throw new SQLException(format(Locale.ROOT, "Unable to convert value [%.128s] of type [%s] to a Float", val, columnType.getName())); - } - - private static Double asDouble(Object val, SQLType columnType) throws SQLException { - if (columnType instanceof JDBCType) { - switch ((JDBCType) columnType) { - case BOOLEAN: - return Double.valueOf(((Boolean) val).booleanValue() ? 1 : 0); - case TINYINT: - case SMALLINT: - case INTEGER: - case BIGINT: - return Double.valueOf(((Number) val).longValue()); - case REAL: - case FLOAT: - case DOUBLE: - - return Double.valueOf(((Number) val).doubleValue()); - case VARCHAR: - try { - return Double.valueOf((String) val); - } catch (NumberFormatException e) { - throw new SQLException(format(Locale.ROOT, "Unable to convert value [%.128s] of type [VARCHAR] to a Double", val), - e); - } - default: - } - } - throw new SQLException( - format(Locale.ROOT, "Unable to convert value [%.128s] of type [%s] to a Double", val, columnType.getName())); - } - - private static Date asDate(Object val, SQLType columnType) throws SQLException { - if (columnType == JDBCType.TIMESTAMP) { + private static T failConversion(Object value, DataType columnType, String typeString, Class target) throws SQLException { + return failConversion(value, columnType, typeString, target, null); + } + + private static T failConversion(Object value, DataType columnType, String typeString, Class target, Exception e) + throws SQLException { + String message = format(Locale.ROOT, "Unable to convert value [%.128s] of type [%s] to [%s]", value, columnType, + typeString); + throw e != null ? new SQLException(message, e) : new SQLException(message); + } + + private static Boolean asBoolean(Object val, DataType columnType, String typeString) throws SQLException { + switch (columnType) { + case BOOLEAN: + case BYTE: + case SHORT: + case INTEGER: + case LONG: + case FLOAT: + case HALF_FLOAT: + case SCALED_FLOAT: + case DOUBLE: + return Boolean.valueOf(Integer.signum(((Number) val).intValue()) != 0); + case KEYWORD: + case TEXT: + return Boolean.valueOf((String) val); + default: + return failConversion(val, columnType, typeString, Boolean.class); + } + } + + private static Byte asByte(Object val, DataType columnType, String typeString) throws SQLException { + switch (columnType) { + case BOOLEAN: + return Byte.valueOf(((Boolean) val).booleanValue() ? (byte) 1 : (byte) 0); + case BYTE: + case SHORT: + case INTEGER: + case LONG: + return safeToByte(((Number) val).longValue()); + case FLOAT: + case HALF_FLOAT: + case SCALED_FLOAT: + case DOUBLE: + return safeToByte(safeToLong(((Number) val).doubleValue())); + case KEYWORD: + case TEXT: + try { + return Byte.valueOf((String) val); + } catch (NumberFormatException e) { + return failConversion(val, columnType, typeString, Byte.class, e); + } + default: + } + + return failConversion(val, columnType, typeString, Byte.class); + } + + private static Short asShort(Object val, DataType columnType, String typeString) throws SQLException { + switch (columnType) { + case BOOLEAN: + return Short.valueOf(((Boolean) val).booleanValue() ? (short) 1 : (short) 0); + case BYTE: + case SHORT: + case INTEGER: + case LONG: + return safeToShort(((Number) val).longValue()); + case FLOAT: + case HALF_FLOAT: + case SCALED_FLOAT: + case DOUBLE: + return safeToShort(safeToLong(((Number) val).doubleValue())); + case KEYWORD: + case TEXT: + try { + return Short.valueOf((String) val); + } catch (NumberFormatException e) { + return failConversion(val, columnType, typeString, Short.class, e); + } + default: + } + return failConversion(val, columnType, typeString, Short.class); + } + + private static Integer asInteger(Object val, DataType columnType, String typeString) throws SQLException { + switch (columnType) { + case BOOLEAN: + return Integer.valueOf(((Boolean) val).booleanValue() ? 1 : 0); + case BYTE: + case SHORT: + case INTEGER: + case LONG: + return safeToInt(((Number) val).longValue()); + case FLOAT: + case HALF_FLOAT: + case SCALED_FLOAT: + case DOUBLE: + return safeToInt(safeToLong(((Number) val).doubleValue())); + case KEYWORD: + case TEXT: + try { + return Integer.valueOf((String) val); + } catch (NumberFormatException e) { + return failConversion(val, columnType, typeString, Integer.class, e); + } + default: + } + return failConversion(val, columnType, typeString, Integer.class); + } + + private static Long asLong(Object val, DataType columnType, String typeString) throws SQLException { + switch (columnType) { + case BOOLEAN: + return Long.valueOf(((Boolean) val).booleanValue() ? 1 : 0); + case BYTE: + case SHORT: + case INTEGER: + case LONG: + return Long.valueOf(((Number) val).longValue()); + case FLOAT: + case HALF_FLOAT: + case SCALED_FLOAT: + case DOUBLE: + return safeToLong(((Number) val).doubleValue()); + //TODO: should we support conversion to TIMESTAMP? + //The spec says that getLong() should support the following types conversions: + //TINYINT, SMALLINT, INTEGER, BIGINT, REAL, FLOAT, DOUBLE, DECIMAL, NUMERIC, BIT, BOOLEAN, CHAR, VARCHAR, LONGVARCHAR + //case TIMESTAMP: + // return ((Number) val).longValue(); + case KEYWORD: + case TEXT: + try { + return Long.valueOf((String) val); + } catch (NumberFormatException e) { + return failConversion(val, columnType, typeString, Long.class, e); + } + default: + } + + return failConversion(val, columnType, typeString, Long.class); + } + + private static Float asFloat(Object val, DataType columnType, String typeString) throws SQLException { + switch (columnType) { + case BOOLEAN: + return Float.valueOf(((Boolean) val).booleanValue() ? 1 : 0); + case BYTE: + case SHORT: + case INTEGER: + case LONG: + return Float.valueOf(((Number) val).longValue()); + case FLOAT: + case HALF_FLOAT: + case SCALED_FLOAT: + case DOUBLE: + return Float.valueOf(((Number) val).floatValue()); + case KEYWORD: + case TEXT: + try { + return Float.valueOf((String) val); + } catch (NumberFormatException e) { + return failConversion(val, columnType, typeString, Float.class, e); + } + default: + } + return failConversion(val, columnType, typeString, Float.class); + } + + private static Double asDouble(Object val, DataType columnType, String typeString) throws SQLException { + switch (columnType) { + case BOOLEAN: + return Double.valueOf(((Boolean) val).booleanValue() ? 1 : 0); + case BYTE: + case SHORT: + case INTEGER: + case LONG: + return Double.valueOf(((Number) val).longValue()); + case FLOAT: + case HALF_FLOAT: + case SCALED_FLOAT: + case DOUBLE: + return Double.valueOf(((Number) val).doubleValue()); + case KEYWORD: + case TEXT: + try { + return Double.valueOf((String) val); + } catch (NumberFormatException e) { + return failConversion(val, columnType, typeString, Double.class, e); + } + default: + } + return failConversion(val, columnType, typeString, Double.class); + } + + private static Date asDate(Object val, DataType columnType, String typeString) throws SQLException { + if (columnType == DataType.DATE) { return new Date(utcMillisRemoveTime(((Number) val).longValue())); } - throw new SQLException(format(Locale.ROOT, "Unable to convert value [%.128s] of type [%s] to a Date", val, columnType.getName())); + return failConversion(val, columnType, typeString, Date.class); } - private static Time asTime(Object val, SQLType columnType) throws SQLException { - if (columnType == JDBCType.TIMESTAMP) { + private static Time asTime(Object val, DataType columnType, String typeString) throws SQLException { + if (columnType == DataType.DATE) { return new Time(utcMillisRemoveDate(((Number) val).longValue())); } - throw new SQLException(format(Locale.ROOT, "Unable to convert value [%.128s] of type [%s] to a Time", val, columnType.getName())); + return failConversion(val, columnType, typeString, Time.class); } - private static Timestamp asTimestamp(Object val, SQLType columnType) throws SQLException { - if (columnType == JDBCType.TIMESTAMP) { + private static Timestamp asTimestamp(Object val, DataType columnType, String typeString) throws SQLException { + if (columnType == DataType.DATE) { return new Timestamp(((Number) val).longValue()); } - throw new SQLException( - format(Locale.ROOT, "Unable to convert value [%.128s] of type [%s] to a Timestamp", val, columnType.getName())); + return failConversion(val, columnType, typeString, Timestamp.class); } - private static byte[] asByteArray(Object val, SQLType columnType) { - throw new UnsupportedOperationException(); + private static byte[] asByteArray(Object val, DataType columnType, String typeString) throws SQLException { + throw new SQLFeatureNotSupportedException(); } - private static LocalDate asLocalDate(Object val, SQLType columnType) { - throw new UnsupportedOperationException(); + private static LocalDate asLocalDate(Object val, DataType columnType, String typeString) throws SQLException { + throw new SQLFeatureNotSupportedException(); } - private static LocalTime asLocalTime(Object val, SQLType columnType) { - throw new UnsupportedOperationException(); + private static LocalTime asLocalTime(Object val, DataType columnType, String typeString) throws SQLException { + throw new SQLFeatureNotSupportedException(); } - private static LocalDateTime asLocalDateTime(Object val, SQLType columnType) { - throw new UnsupportedOperationException(); + private static LocalDateTime asLocalDateTime(Object val, DataType columnType, String typeString) throws SQLException { + throw new SQLFeatureNotSupportedException(); } - private static OffsetTime asOffsetTime(Object val, SQLType columnType) { - throw new UnsupportedOperationException(); + private static OffsetTime asOffsetTime(Object val, DataType columnType, String typeString) throws SQLException { + throw new SQLFeatureNotSupportedException(); } - private static OffsetDateTime asOffsetDateTime(Object val, SQLType columnType) { - throw new UnsupportedOperationException(); + private static OffsetDateTime asOffsetDateTime(Object val, DataType columnType, String typeString) throws SQLException { + throw new SQLFeatureNotSupportedException(); } - private static long utcMillisRemoveTime(long l) { return l - (l % DAY_IN_MILLIS); } diff --git a/x-pack/plugin/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/jdbc/TypeUtils.java b/x-pack/plugin/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/jdbc/TypeUtils.java new file mode 100644 index 0000000000000..ec01d4510dd5f --- /dev/null +++ b/x-pack/plugin/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/jdbc/TypeUtils.java @@ -0,0 +1,177 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +package org.elasticsearch.xpack.sql.jdbc.jdbc; + +import org.elasticsearch.xpack.sql.jdbc.type.DataType; + +import java.sql.JDBCType; +import java.sql.SQLException; +import java.sql.SQLFeatureNotSupportedException; +import java.sql.SQLType; +import java.sql.Timestamp; +import java.time.Duration; +import java.time.LocalDateTime; +import java.time.Period; +import java.util.Calendar; +import java.util.Collections; +import java.util.EnumSet; +import java.util.GregorianCalendar; +import java.util.LinkedHashMap; +import java.util.Locale; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; + +import static java.util.Collections.unmodifiableMap; + +public class TypeUtils { + + private TypeUtils() {} + + private static final Map, DataType> CLASS_TO_TYPE; + private static final Map> TYPE_TO_CLASS; + private static final Map ENUM_NAME_TO_TYPE; + private static final Map SQL_TO_TYPE; + + private static final Set SIGNED_TYPE = EnumSet.of(DataType.BYTE, + DataType.SHORT, DataType.INTEGER, DataType.LONG, + DataType.FLOAT, DataType.HALF_FLOAT, DataType.SCALED_FLOAT, DataType.DOUBLE, DataType.DATE); + + + static { + Map, DataType> aMap = new LinkedHashMap<>(); + aMap.put(Boolean.class, DataType.BOOLEAN); + aMap.put(Byte.class, DataType.BYTE); + aMap.put(Short.class, DataType.SHORT); + aMap.put(Integer.class, DataType.INTEGER); + aMap.put(Long.class, DataType.LONG); + aMap.put(Float.class, DataType.FLOAT); + aMap.put(Double.class, DataType.DOUBLE); + aMap.put(String.class, DataType.KEYWORD); + aMap.put(byte[].class, DataType.BINARY); + aMap.put(String.class, DataType.KEYWORD); + aMap.put(Timestamp.class, DataType.DATE); + + // apart from the mappings in {@code DataType} three more Java classes can be mapped to a {@code JDBCType.TIMESTAMP} + // according to B-4 table from the jdbc4.2 spec + aMap.put(Calendar.class, DataType.DATE); + aMap.put(GregorianCalendar.class, DataType.DATE); + aMap.put(java.util.Date.class, DataType.DATE); + aMap.put(java.sql.Date.class, DataType.DATE); + aMap.put(java.sql.Time.class, DataType.DATE); + aMap.put(LocalDateTime.class, DataType.DATE); + CLASS_TO_TYPE = Collections.unmodifiableMap(aMap); + + Map> types = new LinkedHashMap<>(); + types.put(DataType.BOOLEAN, Boolean.class); + types.put(DataType.BYTE, Byte.class); + types.put(DataType.SHORT, Short.class); + types.put(DataType.INTEGER, Integer.class); + types.put(DataType.LONG, Long.class); + types.put(DataType.DOUBLE, Double.class); + types.put(DataType.FLOAT, Float.class); + types.put(DataType.HALF_FLOAT, Double.class); + types.put(DataType.SCALED_FLOAT, Double.class); + types.put(DataType.KEYWORD, String.class); + types.put(DataType.TEXT, String.class); + types.put(DataType.BINARY, byte[].class); + types.put(DataType.DATE, Timestamp.class); + types.put(DataType.IP, String.class); + types.put(DataType.INTERVAL_YEAR, Period.class); + types.put(DataType.INTERVAL_MONTH, Period.class); + types.put(DataType.INTERVAL_YEAR_TO_MONTH, Period.class); + types.put(DataType.INTERVAL_DAY, Duration.class); + types.put(DataType.INTERVAL_HOUR, Duration.class); + types.put(DataType.INTERVAL_MINUTE, Duration.class); + types.put(DataType.INTERVAL_SECOND, Duration.class); + types.put(DataType.INTERVAL_DAY_TO_HOUR, Duration.class); + types.put(DataType.INTERVAL_DAY_TO_MINUTE, Duration.class); + types.put(DataType.INTERVAL_DAY_TO_SECOND, Duration.class); + types.put(DataType.INTERVAL_HOUR_TO_MINUTE, Duration.class); + types.put(DataType.INTERVAL_HOUR_TO_SECOND, Duration.class); + types.put(DataType.INTERVAL_MINUTE_TO_SECOND, Duration.class); + + TYPE_TO_CLASS = unmodifiableMap(types); + + + Map strings = new LinkedHashMap<>(); + Map numbers = new LinkedHashMap<>(); + + for (DataType dataType : DataType.values()) { + strings.put(dataType.getName().toLowerCase(Locale.ROOT), dataType); + numbers.putIfAbsent(dataType.getVendorTypeNumber(), dataType); + } + + ENUM_NAME_TO_TYPE = unmodifiableMap(strings); + SQL_TO_TYPE = unmodifiableMap(numbers); + } + + static boolean isSigned(DataType type) { + return SIGNED_TYPE.contains(type); + } + + static Class classOf(DataType type) { + return TYPE_TO_CLASS.get(type); + } + + static SQLType asSqlType(int sqlType) throws SQLException { + for (JDBCType jdbcType : JDBCType.class.getEnumConstants()) { + if (sqlType == jdbcType.getVendorTypeNumber().intValue()) { + return jdbcType; + } + } + // fallback to DataType + return of(sqlType); + } + + static DataType of(SQLType sqlType) throws SQLException { + if (sqlType instanceof DataType) { + return (DataType) sqlType; + } + DataType dataType = SQL_TO_TYPE.get(Integer.valueOf(sqlType.getVendorTypeNumber())); + if (dataType == null) { + throw new SQLFeatureNotSupportedException("Unsupported SQL type [" + sqlType + "]"); + } + return dataType; + } + + static DataType of(int sqlType) throws SQLException { + DataType dataType = SQL_TO_TYPE.get(Integer.valueOf(sqlType)); + if (dataType == null) { + throw new SQLFeatureNotSupportedException("Unsupported SQL type [" + sqlType + "]"); + } + return dataType; + } + + public static DataType of(String name) throws SQLException { + DataType dataType = ENUM_NAME_TO_TYPE.get(name); + if (dataType == null) { + throw new SQLFeatureNotSupportedException("Unsupported Data type [" + name + "]"); + } + return dataType; + } + + static boolean isString(DataType dataType) { + return dataType == DataType.KEYWORD || dataType == DataType.TEXT; + } + + static DataType of(Class clazz) throws SQLException { + DataType dataType = CLASS_TO_TYPE.get(clazz); + + if (dataType == null) { + // fall-back to iteration for checking class hierarchies (in case of custom objects) + for (Entry, DataType> e : CLASS_TO_TYPE.entrySet()) { + if (e.getKey().isAssignableFrom(clazz)) { + return e.getValue(); + } + } + + throw new SQLFeatureNotSupportedException("Objects of type [" + clazz.getName() + "] are not supported"); + } + return dataType; + } +} \ No newline at end of file diff --git a/x-pack/plugin/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/net/client/Cursor.java b/x-pack/plugin/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/net/client/Cursor.java index 5549bc57d63e2..6097f51f7dd1e 100644 --- a/x-pack/plugin/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/net/client/Cursor.java +++ b/x-pack/plugin/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/net/client/Cursor.java @@ -8,11 +8,11 @@ import java.sql.SQLException; import java.util.List; -import org.elasticsearch.xpack.sql.jdbc.net.protocol.ColumnInfo; +import org.elasticsearch.xpack.sql.jdbc.net.protocol.JdbcColumnInfo; public interface Cursor { - List columns(); + List columns(); default int columnSize() { return columns().size(); diff --git a/x-pack/plugin/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/net/client/DefaultCursor.java b/x-pack/plugin/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/net/client/DefaultCursor.java index ebc025d79694d..a626646e376a1 100644 --- a/x-pack/plugin/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/net/client/DefaultCursor.java +++ b/x-pack/plugin/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/net/client/DefaultCursor.java @@ -6,7 +6,7 @@ package org.elasticsearch.xpack.sql.jdbc.net.client; import org.elasticsearch.common.collect.Tuple; -import org.elasticsearch.xpack.sql.jdbc.net.protocol.ColumnInfo; +import org.elasticsearch.xpack.sql.jdbc.net.protocol.JdbcColumnInfo; import java.sql.SQLException; import java.util.List; @@ -16,12 +16,12 @@ class DefaultCursor implements Cursor { private final JdbcHttpClient client; private final RequestMeta meta; - private final List columnInfos; + private final List columnInfos; private List> rows; private int row = -1; private String cursor; - DefaultCursor(JdbcHttpClient client, String cursor, List columnInfos, List> rows, RequestMeta meta) { + DefaultCursor(JdbcHttpClient client, String cursor, List columnInfos, List> rows, RequestMeta meta) { this.client = client; this.meta = meta; this.cursor = cursor; @@ -30,7 +30,7 @@ class DefaultCursor implements Cursor { } @Override - public List columns() { + public List columns() { return columnInfos; } diff --git a/x-pack/plugin/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/net/client/JdbcHttpClient.java b/x-pack/plugin/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/net/client/JdbcHttpClient.java index e32aadcd7fa2b..8b2d21524bc34 100644 --- a/x-pack/plugin/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/net/client/JdbcHttpClient.java +++ b/x-pack/plugin/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/net/client/JdbcHttpClient.java @@ -10,18 +10,20 @@ import org.elasticsearch.xpack.sql.client.HttpClient; import org.elasticsearch.xpack.sql.client.Version; import org.elasticsearch.xpack.sql.jdbc.jdbc.JdbcConfiguration; -import org.elasticsearch.xpack.sql.jdbc.net.protocol.ColumnInfo; +import org.elasticsearch.xpack.sql.jdbc.jdbc.TypeUtils; import org.elasticsearch.xpack.sql.jdbc.net.protocol.InfoResponse; +import org.elasticsearch.xpack.sql.jdbc.net.protocol.JdbcColumnInfo; +import org.elasticsearch.xpack.sql.proto.ColumnInfo; +import org.elasticsearch.xpack.sql.proto.MainResponse; import org.elasticsearch.xpack.sql.proto.Mode; import org.elasticsearch.xpack.sql.proto.Protocol; import org.elasticsearch.xpack.sql.proto.SqlQueryRequest; -import org.elasticsearch.xpack.sql.proto.MainResponse; import org.elasticsearch.xpack.sql.proto.SqlQueryResponse; import org.elasticsearch.xpack.sql.proto.SqlTypedParamValue; import java.sql.SQLException; +import java.util.ArrayList; import java.util.List; -import java.util.stream.Collectors; import static org.elasticsearch.xpack.sql.client.StringUtils.EMPTY; @@ -87,9 +89,11 @@ private InfoResponse fetchServerInfo() throws SQLException { /** * Converts REST column metadata into JDBC column metadata */ - private List toJdbcColumnInfo(List columns) { - return columns.stream().map(columnInfo -> - new ColumnInfo(columnInfo.name(), columnInfo.jdbcType(), EMPTY, EMPTY, EMPTY, EMPTY, columnInfo.displaySize()) - ).collect(Collectors.toList()); + private List toJdbcColumnInfo(List columns) throws SQLException { + List cols = new ArrayList<>(columns.size()); + for (ColumnInfo info : columns) { + cols.add(new JdbcColumnInfo(info.name(), TypeUtils.of(info.esType()), EMPTY, EMPTY, EMPTY, EMPTY, info.displaySize())); + } + return cols; } -} +} \ No newline at end of file diff --git a/x-pack/plugin/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/net/protocol/ColumnInfo.java b/x-pack/plugin/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/net/protocol/JdbcColumnInfo.java similarity index 90% rename from x-pack/plugin/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/net/protocol/ColumnInfo.java rename to x-pack/plugin/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/net/protocol/JdbcColumnInfo.java index b8582d8e9b386..e4124c001e199 100644 --- a/x-pack/plugin/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/net/protocol/ColumnInfo.java +++ b/x-pack/plugin/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/net/protocol/JdbcColumnInfo.java @@ -5,19 +5,20 @@ */ package org.elasticsearch.xpack.sql.jdbc.net.protocol; -import java.sql.SQLType; +import org.elasticsearch.xpack.sql.jdbc.type.DataType; + import java.util.Objects; -public class ColumnInfo { +public class JdbcColumnInfo { public final String catalog; public final String schema; public final String table; public final String label; public final String name; public final int displaySize; - public final SQLType type; + public final DataType type; - public ColumnInfo(String name, SQLType type, String table, String catalog, String schema, String label, int displaySize) { + public JdbcColumnInfo(String name, DataType type, String table, String catalog, String schema, String label, int displaySize) { if (name == null) { throw new IllegalArgumentException("[name] must not be null"); } @@ -74,7 +75,7 @@ public boolean equals(Object obj) { if (obj == null || obj.getClass() != getClass()) { return false; } - ColumnInfo other = (ColumnInfo) obj; + JdbcColumnInfo other = (JdbcColumnInfo) obj; return name.equals(other.name) && type.equals(other.type) && table.equals(other.table) diff --git a/x-pack/plugin/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/type/DataType.java b/x-pack/plugin/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/type/DataType.java new file mode 100644 index 0000000000000..4dd349e10b827 --- /dev/null +++ b/x-pack/plugin/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/type/DataType.java @@ -0,0 +1,67 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +package org.elasticsearch.xpack.sql.jdbc.type; + + +import java.sql.SQLType; +import java.sql.Types; + +public enum DataType implements SQLType { + + NULL( Types.NULL), + UNSUPPORTED( Types.OTHER), + BOOLEAN( Types.BOOLEAN), + BYTE( Types.TINYINT), + SHORT( Types.SMALLINT), + INTEGER( Types.INTEGER), + LONG( Types.BIGINT), + DOUBLE( Types.DOUBLE), + FLOAT( Types.REAL), + HALF_FLOAT( Types.FLOAT), + SCALED_FLOAT( Types.FLOAT), + KEYWORD( Types.VARCHAR), + TEXT( Types.VARCHAR), + OBJECT( Types.STRUCT), + NESTED( Types.STRUCT), + BINARY( Types.VARBINARY), + DATE( Types.TIMESTAMP), + IP( Types.VARCHAR), + INTERVAL_YEAR( ExtraTypes.INTERVAL_YEAR), + INTERVAL_MONTH( ExtraTypes.INTERVAL_MONTH), + INTERVAL_YEAR_TO_MONTH( ExtraTypes.INTERVAL_YEAR_MONTH), + INTERVAL_DAY( ExtraTypes.INTERVAL_DAY), + INTERVAL_HOUR( ExtraTypes.INTERVAL_HOUR), + INTERVAL_MINUTE( ExtraTypes.INTERVAL_MINUTE), + INTERVAL_SECOND( ExtraTypes.INTERVAL_SECOND), + INTERVAL_DAY_TO_HOUR( ExtraTypes.INTERVAL_DAY_HOUR), + INTERVAL_DAY_TO_MINUTE( ExtraTypes.INTERVAL_DAY_MINUTE), + INTERVAL_DAY_TO_SECOND( ExtraTypes.INTERVAL_DAY_SECOND), + INTERVAL_HOUR_TO_MINUTE( ExtraTypes.INTERVAL_HOUR_MINUTE), + INTERVAL_HOUR_TO_SECOND( ExtraTypes.INTERVAL_HOUR_SECOND), + INTERVAL_MINUTE_TO_SECOND(ExtraTypes.INTERVAL_MINUTE_SECOND); + + private final Integer type; + + DataType(int type) { + this.type = Integer.valueOf(type); + } + + @Override + public String getName() { + return name(); + } + + @Override + public String getVendor() { + return "org.elasticsearch"; + } + + @Override + public Integer getVendorTypeNumber() { + return type; + } +} \ No newline at end of file diff --git a/x-pack/plugin/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/type/ExtraTypes.java b/x-pack/plugin/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/type/ExtraTypes.java new file mode 100644 index 0000000000000..effb09718a311 --- /dev/null +++ b/x-pack/plugin/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/type/ExtraTypes.java @@ -0,0 +1,33 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +package org.elasticsearch.xpack.sql.jdbc.type; + +import java.sql.JDBCType; +import java.sql.Types; + +/** + * Provides ODBC-based codes for the missing SQL data types from {@link Types}/{@link JDBCType}. + */ +class ExtraTypes { + + private ExtraTypes() {} + + static final int INTERVAL_YEAR = 101; + static final int INTERVAL_MONTH = 102; + static final int INTERVAL_DAY = 103; + static final int INTERVAL_HOUR = 104; + static final int INTERVAL_MINUTE = 105; + static final int INTERVAL_SECOND = 106; + static final int INTERVAL_YEAR_MONTH = 107; + static final int INTERVAL_DAY_HOUR = 108; + static final int INTERVAL_DAY_MINUTE = 109; + static final int INTERVAL_DAY_SECOND = 110; + static final int INTERVAL_HOUR_MINUTE = 111; + static final int INTERVAL_HOUR_SECOND = 112; + static final int INTERVAL_MINUTE_SECOND = 113; + +} diff --git a/x-pack/plugin/sql/jdbc/src/test/java/org/elasticsearch/xpack/sql/jdbc/jdbc/JdbcPreparedStatementTests.java b/x-pack/plugin/sql/jdbc/src/test/java/org/elasticsearch/xpack/sql/jdbc/jdbc/JdbcPreparedStatementTests.java index 229c7e8182ca3..ac7ad22db7550 100644 --- a/x-pack/plugin/sql/jdbc/src/test/java/org/elasticsearch/xpack/sql/jdbc/jdbc/JdbcPreparedStatementTests.java +++ b/x-pack/plugin/sql/jdbc/src/test/java/org/elasticsearch/xpack/sql/jdbc/jdbc/JdbcPreparedStatementTests.java @@ -6,12 +6,12 @@ package org.elasticsearch.xpack.sql.jdbc.jdbc; import org.elasticsearch.test.ESTestCase; +import org.elasticsearch.xpack.sql.jdbc.type.DataType; import java.net.URL; import java.nio.charset.StandardCharsets; import java.sql.SQLException; import java.sql.SQLFeatureNotSupportedException; -import java.sql.SQLType; import java.sql.Struct; import java.sql.Time; import java.sql.Timestamp; @@ -26,17 +26,17 @@ import java.util.Map; import static java.lang.String.format; -import static java.sql.JDBCType.BIGINT; -import static java.sql.JDBCType.BOOLEAN; -import static java.sql.JDBCType.DOUBLE; -import static java.sql.JDBCType.FLOAT; -import static java.sql.JDBCType.INTEGER; -import static java.sql.JDBCType.REAL; -import static java.sql.JDBCType.SMALLINT; -import static java.sql.JDBCType.TIMESTAMP; -import static java.sql.JDBCType.TINYINT; -import static java.sql.JDBCType.VARBINARY; -import static java.sql.JDBCType.VARCHAR; +import static org.elasticsearch.xpack.sql.jdbc.type.DataType.BINARY; +import static org.elasticsearch.xpack.sql.jdbc.type.DataType.BOOLEAN; +import static org.elasticsearch.xpack.sql.jdbc.type.DataType.BYTE; +import static org.elasticsearch.xpack.sql.jdbc.type.DataType.DATE; +import static org.elasticsearch.xpack.sql.jdbc.type.DataType.DOUBLE; +import static org.elasticsearch.xpack.sql.jdbc.type.DataType.FLOAT; +import static org.elasticsearch.xpack.sql.jdbc.type.DataType.HALF_FLOAT; +import static org.elasticsearch.xpack.sql.jdbc.type.DataType.INTEGER; +import static org.elasticsearch.xpack.sql.jdbc.type.DataType.KEYWORD; +import static org.elasticsearch.xpack.sql.jdbc.type.DataType.LONG; +import static org.elasticsearch.xpack.sql.jdbc.type.DataType.SHORT; public class JdbcPreparedStatementTests extends ESTestCase { @@ -62,14 +62,14 @@ public void testSettingBooleanValues() throws SQLException { jps.setObject(1, true, Types.VARCHAR); assertEquals("true", value(jps)); - assertEquals(VARCHAR, jdbcType(jps)); + assertEquals(KEYWORD, jdbcType(jps)); } public void testThrownExceptionsWhenSettingBooleanValues() throws SQLException { JdbcPreparedStatement jps = createJdbcPreparedStatement(); SQLException sqle = expectThrows(SQLException.class, () -> jps.setObject(1, true, Types.TIMESTAMP)); - assertEquals("Unable to convert value [true] of type [BOOLEAN] to a Timestamp", sqle.getMessage()); + assertEquals("Unable to convert value [true] of type [BOOLEAN] to [TIMESTAMP]", sqle.getMessage()); } public void testSettingStringValues() throws SQLException { @@ -77,15 +77,15 @@ public void testSettingStringValues() throws SQLException { jps.setString(1, "foo bar"); assertEquals("foo bar", value(jps)); - assertEquals(VARCHAR, jdbcType(jps)); + assertEquals(KEYWORD, jdbcType(jps)); jps.setObject(1, "foo bar"); assertEquals("foo bar", value(jps)); - assertEquals(VARCHAR, jdbcType(jps)); + assertEquals(KEYWORD, jdbcType(jps)); jps.setObject(1, "foo bar", Types.VARCHAR); assertEquals("foo bar", value(jps)); - assertEquals(VARCHAR, jdbcType(jps)); + assertEquals(KEYWORD, jdbcType(jps)); assertTrue(value(jps) instanceof String); } @@ -93,7 +93,7 @@ public void testThrownExceptionsWhenSettingStringValues() throws SQLException { JdbcPreparedStatement jps = createJdbcPreparedStatement(); SQLException sqle = expectThrows(SQLException.class, () -> jps.setObject(1, "foo bar", Types.INTEGER)); - assertEquals("Unable to convert value [foo bar] of type [VARCHAR] to an Integer", sqle.getMessage()); + assertEquals("Unable to convert value [foo bar] of type [KEYWORD] to [INTEGER]", sqle.getMessage()); } public void testSettingByteTypeValues() throws SQLException { @@ -101,11 +101,11 @@ public void testSettingByteTypeValues() throws SQLException { jps.setByte(1, (byte) 6); assertEquals((byte) 6, value(jps)); - assertEquals(TINYINT, jdbcType(jps)); + assertEquals(BYTE, jdbcType(jps)); jps.setObject(1, (byte) 6); assertEquals((byte) 6, value(jps)); - assertEquals(TINYINT, jdbcType(jps)); + assertEquals(BYTE, jdbcType(jps)); assertTrue(value(jps) instanceof Byte); jps.setObject(1, (byte) 0, Types.BOOLEAN); @@ -129,7 +129,7 @@ public void testThrownExceptionsWhenSettingByteTypeValues() throws SQLException JdbcPreparedStatement jps = createJdbcPreparedStatement(); SQLException sqle = expectThrows(SQLException.class, () -> jps.setObject(1, (byte) 6, Types.TIMESTAMP)); - assertEquals("Unable to convert value [6] of type [TINYINT] to a Timestamp", sqle.getMessage()); + assertEquals("Unable to convert value [6] of type [BYTE] to [TIMESTAMP]", sqle.getMessage()); } public void testSettingShortTypeValues() throws SQLException { @@ -138,11 +138,11 @@ public void testSettingShortTypeValues() throws SQLException { short someShort = randomShort(); jps.setShort(1, someShort); assertEquals(someShort, value(jps)); - assertEquals(SMALLINT, jdbcType(jps)); + assertEquals(SHORT, jdbcType(jps)); jps.setObject(1, someShort); assertEquals(someShort, value(jps)); - assertEquals(SMALLINT, jdbcType(jps)); + assertEquals(SHORT, jdbcType(jps)); assertTrue(value(jps) instanceof Short); jps.setObject(1, (short) 1, Types.BOOLEAN); @@ -162,7 +162,7 @@ public void testThrownExceptionsWhenSettingShortTypeValues() throws SQLException JdbcPreparedStatement jps = createJdbcPreparedStatement(); SQLException sqle = expectThrows(SQLException.class, () -> jps.setObject(1, (short) 6, Types.TIMESTAMP)); - assertEquals("Unable to convert value [6] of type [SMALLINT] to a Timestamp", sqle.getMessage()); + assertEquals("Unable to convert value [6] of type [SHORT] to [TIMESTAMP]", sqle.getMessage()); sqle = expectThrows(SQLException.class, () -> jps.setObject(1, 256, Types.TINYINT)); assertEquals("Numeric " + 256 + " out of range", sqle.getMessage()); @@ -183,12 +183,12 @@ public void testSettingIntegerValues() throws SQLException { jps.setObject(1, someInt, Types.VARCHAR); assertEquals(String.valueOf(someInt), value(jps)); - assertEquals(VARCHAR, jdbcType(jps)); + assertEquals(KEYWORD, jdbcType(jps)); jps.setObject(1, someInt, Types.FLOAT); assertEquals(Double.valueOf(someInt), value(jps)); assertTrue(value(jps) instanceof Double); - assertEquals(FLOAT, jdbcType(jps)); + assertEquals(HALF_FLOAT, jdbcType(jps)); } public void testThrownExceptionsWhenSettingIntegerValues() throws SQLException { @@ -196,7 +196,8 @@ public void testThrownExceptionsWhenSettingIntegerValues() throws SQLException { int someInt = randomInt(); SQLException sqle = expectThrows(SQLException.class, () -> jps.setObject(1, someInt, Types.TIMESTAMP)); - assertEquals(format(Locale.ROOT, "Unable to convert value [%.128s] of type [INTEGER] to a Timestamp", someInt), sqle.getMessage()); + assertEquals(format(Locale.ROOT, "Unable to convert value [%.128s] of type [INTEGER] to [TIMESTAMP]", someInt), + sqle.getMessage()); Integer randomIntNotShort = randomIntBetween(32768, Integer.MAX_VALUE); sqle = expectThrows(SQLException.class, () -> jps.setObject(1, randomIntNotShort, Types.SMALLINT)); @@ -212,16 +213,16 @@ public void testSettingLongValues() throws SQLException { long someLong = randomLong(); jps.setLong(1, someLong); assertEquals(someLong, value(jps)); - assertEquals(BIGINT, jdbcType(jps)); + assertEquals(LONG, jdbcType(jps)); jps.setObject(1, someLong); assertEquals(someLong, value(jps)); - assertEquals(BIGINT, jdbcType(jps)); + assertEquals(LONG, jdbcType(jps)); assertTrue(value(jps) instanceof Long); jps.setObject(1, someLong, Types.VARCHAR); assertEquals(String.valueOf(someLong), value(jps)); - assertEquals(VARCHAR, jdbcType(jps)); + assertEquals(KEYWORD, jdbcType(jps)); jps.setObject(1, someLong, Types.DOUBLE); assertEquals((double) someLong, value(jps)); @@ -229,7 +230,7 @@ public void testSettingLongValues() throws SQLException { jps.setObject(1, someLong, Types.FLOAT); assertEquals((double) someLong, value(jps)); - assertEquals(FLOAT, jdbcType(jps)); + assertEquals(HALF_FLOAT, jdbcType(jps)); } public void testThrownExceptionsWhenSettingLongValues() throws SQLException { @@ -237,7 +238,8 @@ public void testThrownExceptionsWhenSettingLongValues() throws SQLException { long someLong = randomLong(); SQLException sqle = expectThrows(SQLException.class, () -> jps.setObject(1, someLong, Types.TIMESTAMP)); - assertEquals(format(Locale.ROOT, "Unable to convert value [%.128s] of type [BIGINT] to a Timestamp", someLong), sqle.getMessage()); + assertEquals(format(Locale.ROOT, "Unable to convert value [%.128s] of type [LONG] to [TIMESTAMP]", someLong), + sqle.getMessage()); Long randomLongNotShort = randomLongBetween(Integer.MAX_VALUE + 1, Long.MAX_VALUE); sqle = expectThrows(SQLException.class, () -> jps.setObject(1, randomLongNotShort, Types.INTEGER)); @@ -253,16 +255,16 @@ public void testSettingFloatValues() throws SQLException { float someFloat = randomFloat(); jps.setFloat(1, someFloat); assertEquals(someFloat, value(jps)); - assertEquals(REAL, jdbcType(jps)); + assertEquals(FLOAT, jdbcType(jps)); jps.setObject(1, someFloat); assertEquals(someFloat, value(jps)); - assertEquals(REAL, jdbcType(jps)); + assertEquals(FLOAT, jdbcType(jps)); assertTrue(value(jps) instanceof Float); jps.setObject(1, someFloat, Types.VARCHAR); assertEquals(String.valueOf(someFloat), value(jps)); - assertEquals(VARCHAR, jdbcType(jps)); + assertEquals(KEYWORD, jdbcType(jps)); jps.setObject(1, someFloat, Types.DOUBLE); assertEquals((double) someFloat, value(jps)); @@ -270,7 +272,7 @@ public void testSettingFloatValues() throws SQLException { jps.setObject(1, someFloat, Types.FLOAT); assertEquals((double) someFloat, value(jps)); - assertEquals(FLOAT, jdbcType(jps)); + assertEquals(HALF_FLOAT, jdbcType(jps)); } public void testThrownExceptionsWhenSettingFloatValues() throws SQLException { @@ -278,7 +280,8 @@ public void testThrownExceptionsWhenSettingFloatValues() throws SQLException { float someFloat = randomFloat(); SQLException sqle = expectThrows(SQLException.class, () -> jps.setObject(1, someFloat, Types.TIMESTAMP)); - assertEquals(format(Locale.ROOT, "Unable to convert value [%.128s] of type [REAL] to a Timestamp", someFloat), sqle.getMessage()); + assertEquals(format(Locale.ROOT, "Unable to convert value [%.128s] of type [FLOAT] to [TIMESTAMP]", someFloat), + sqle.getMessage()); Float floatNotInt = 5_155_000_000f; sqle = expectThrows(SQLException.class, () -> jps.setObject(1, floatNotInt, Types.INTEGER)); @@ -305,11 +308,11 @@ public void testSettingDoubleValues() throws SQLException { jps.setObject(1, someDouble, Types.VARCHAR); assertEquals(String.valueOf(someDouble), value(jps)); - assertEquals(VARCHAR, jdbcType(jps)); + assertEquals(KEYWORD, jdbcType(jps)); jps.setObject(1, someDouble, Types.REAL); assertEquals((float) someDouble, value(jps)); - assertEquals(REAL, jdbcType(jps)); + assertEquals(FLOAT, jdbcType(jps)); } public void testThrownExceptionsWhenSettingDoubleValues() throws SQLException { @@ -318,7 +321,8 @@ public void testThrownExceptionsWhenSettingDoubleValues() throws SQLException { SQLException sqle = expectThrows(SQLException.class, () -> jps.setObject(1, someDouble, Types.TIMESTAMP)); assertEquals( - format(Locale.ROOT, "Unable to convert value [%.128s] of type [DOUBLE] to a Timestamp", someDouble), sqle.getMessage()); + format(Locale.ROOT, "Unable to convert value [%.128s] of type [DOUBLE] to [TIMESTAMP]", someDouble), + sqle.getMessage()); Double doubleNotInt = 5_155_000_000d; sqle = expectThrows(SQLException.class, () -> jps.setObject(1, doubleNotInt, Types.INTEGER)); @@ -342,22 +346,23 @@ public Object[] getAttributes() throws SQLException { return null; } })); - assertEquals("Objects of type java.sql.Struct are not supported", sfnse.getMessage()); + assertEquals("Objects of type [java.sql.Struct] are not supported", sfnse.getMessage()); sfnse = expectThrows(SQLFeatureNotSupportedException.class, () -> jps.setObject(1, new URL("http://test"))); - assertEquals("Objects of type java.net.URL are not supported", sfnse.getMessage()); + assertEquals("Objects of type [java.net.URL] are not supported", sfnse.getMessage()); sfnse = expectThrows(SQLFeatureNotSupportedException.class, () -> jps.setURL(1, new URL("http://test"))); - assertEquals("Objects of type java.net.URL are not supported", sfnse.getMessage()); + assertEquals("Objects of type [java.net.URL] are not supported", sfnse.getMessage()); sfnse = expectThrows(SQLFeatureNotSupportedException.class, () -> jps.setObject(1, this, Types.TIMESTAMP)); - assertEquals("Conversion from type " + this.getClass().getName() + " to TIMESTAMP not supported", sfnse.getMessage()); + assertEquals("Conversion from type [" + this.getClass().getName() + "] to [TIMESTAMP] not supported", sfnse.getMessage()); SQLException se = expectThrows(SQLException.class, () -> jps.setObject(1, this, 1_000_000)); - assertEquals("Type:1000000 is not a valid Types.java value.", se.getMessage()); + assertEquals("Unsupported SQL type [1000000]", se.getMessage()); - IllegalArgumentException iae = expectThrows(IllegalArgumentException.class, () -> jps.setObject(1, randomShort(), Types.CHAR)); - assertEquals("Unsupported JDBC type [CHAR]", iae.getMessage()); + SQLFeatureNotSupportedException iae = expectThrows(SQLFeatureNotSupportedException.class, + () -> jps.setObject(1, randomShort(), Types.CHAR)); + assertEquals("Unsupported SQL type [CHAR]", iae.getMessage()); } public void testSettingTimestampValues() throws SQLException { @@ -366,13 +371,13 @@ public void testSettingTimestampValues() throws SQLException { Timestamp someTimestamp = new Timestamp(randomLong()); jps.setTimestamp(1, someTimestamp); assertEquals(someTimestamp.getTime(), ((Date)value(jps)).getTime()); - assertEquals(TIMESTAMP, jdbcType(jps)); + assertEquals(DATE, jdbcType(jps)); Calendar nonDefaultCal = randomCalendar(); // February 29th, 2016. 01:17:55 GMT = 1456708675000 millis since epoch jps.setTimestamp(1, new Timestamp(1456708675000L), nonDefaultCal); assertEquals(1456708675000L, convertFromUTCtoCalendar(((Date)value(jps)), nonDefaultCal)); - assertEquals(TIMESTAMP, jdbcType(jps)); + assertEquals(DATE, jdbcType(jps)); long beforeEpochTime = randomLongBetween(Long.MIN_VALUE, 0); jps.setTimestamp(1, new Timestamp(beforeEpochTime), nonDefaultCal); @@ -381,7 +386,7 @@ public void testSettingTimestampValues() throws SQLException { jps.setObject(1, someTimestamp, Types.VARCHAR); assertEquals(someTimestamp.toString(), value(jps).toString()); - assertEquals(VARCHAR, jdbcType(jps)); + assertEquals(KEYWORD, jdbcType(jps)); } public void testThrownExceptionsWhenSettingTimestampValues() throws SQLException { @@ -389,7 +394,7 @@ public void testThrownExceptionsWhenSettingTimestampValues() throws SQLException Timestamp someTimestamp = new Timestamp(randomLong()); SQLException sqle = expectThrows(SQLFeatureNotSupportedException.class, () -> jps.setObject(1, someTimestamp, Types.INTEGER)); - assertEquals("Conversion from type java.sql.Timestamp to INTEGER not supported", sqle.getMessage()); + assertEquals("Conversion from type [java.sql.Timestamp] to [INTEGER] not supported", sqle.getMessage()); } public void testSettingTimeValues() throws SQLException { @@ -399,12 +404,12 @@ public void testSettingTimeValues() throws SQLException { Calendar nonDefaultCal = randomCalendar(); jps.setTime(1, time, nonDefaultCal); assertEquals(4675000, convertFromUTCtoCalendar(((Date)value(jps)), nonDefaultCal)); - assertEquals(TIMESTAMP, jdbcType(jps)); + assertEquals(DATE, jdbcType(jps)); assertTrue(value(jps) instanceof java.util.Date); jps.setObject(1, time, Types.VARCHAR); assertEquals(time.toString(), value(jps).toString()); - assertEquals(VARCHAR, jdbcType(jps)); + assertEquals(KEYWORD, jdbcType(jps)); } public void testThrownExceptionsWhenSettingTimeValues() throws SQLException { @@ -412,7 +417,7 @@ public void testThrownExceptionsWhenSettingTimeValues() throws SQLException { Time time = new Time(4675000); SQLException sqle = expectThrows(SQLFeatureNotSupportedException.class, () -> jps.setObject(1, time, Types.INTEGER)); - assertEquals("Conversion from type java.sql.Time to INTEGER not supported", sqle.getMessage()); + assertEquals("Conversion from type [java.sql.Time] to [INTEGER] not supported", sqle.getMessage()); } public void testSettingSqlDateValues() throws SQLException { @@ -421,18 +426,18 @@ public void testSettingSqlDateValues() throws SQLException { java.sql.Date someSqlDate = new java.sql.Date(randomLong()); jps.setDate(1, someSqlDate); assertEquals(someSqlDate.getTime(), ((Date)value(jps)).getTime()); - assertEquals(TIMESTAMP, jdbcType(jps)); + assertEquals(DATE, jdbcType(jps)); someSqlDate = new java.sql.Date(randomLong()); Calendar nonDefaultCal = randomCalendar(); jps.setDate(1, someSqlDate, nonDefaultCal); assertEquals(someSqlDate.getTime(), convertFromUTCtoCalendar(((Date)value(jps)), nonDefaultCal)); - assertEquals(TIMESTAMP, jdbcType(jps)); + assertEquals(DATE, jdbcType(jps)); assertTrue(value(jps) instanceof java.util.Date); jps.setObject(1, someSqlDate, Types.VARCHAR); assertEquals(someSqlDate.toString(), value(jps).toString()); - assertEquals(VARCHAR, jdbcType(jps)); + assertEquals(KEYWORD, jdbcType(jps)); } public void testThrownExceptionsWhenSettingSqlDateValues() throws SQLException { @@ -441,7 +446,7 @@ public void testThrownExceptionsWhenSettingSqlDateValues() throws SQLException { SQLException sqle = expectThrows(SQLFeatureNotSupportedException.class, () -> jps.setObject(1, new java.sql.Date(randomLong()), Types.DOUBLE)); - assertEquals("Conversion from type " + someSqlDate.getClass().getName() + " to DOUBLE not supported", sqle.getMessage()); + assertEquals("Conversion from type [" + someSqlDate.getClass().getName() + "] to [DOUBLE] not supported", sqle.getMessage()); } public void testSettingCalendarValues() throws SQLException { @@ -451,17 +456,17 @@ public void testSettingCalendarValues() throws SQLException { jps.setObject(1, someCalendar); assertEquals(someCalendar.getTime(), value(jps)); - assertEquals(TIMESTAMP, jdbcType(jps)); + assertEquals(DATE, jdbcType(jps)); assertTrue(value(jps) instanceof java.util.Date); jps.setObject(1, someCalendar, Types.VARCHAR); assertEquals(someCalendar.toString(), value(jps).toString()); - assertEquals(VARCHAR, jdbcType(jps)); + assertEquals(KEYWORD, jdbcType(jps)); Calendar nonDefaultCal = randomCalendar(); jps.setObject(1, nonDefaultCal); assertEquals(nonDefaultCal.getTime(), value(jps)); - assertEquals(TIMESTAMP, jdbcType(jps)); + assertEquals(DATE, jdbcType(jps)); } public void testThrownExceptionsWhenSettingCalendarValues() throws SQLException { @@ -469,7 +474,7 @@ public void testThrownExceptionsWhenSettingCalendarValues() throws SQLException Calendar someCalendar = randomCalendar(); SQLException sqle = expectThrows(SQLFeatureNotSupportedException.class, () -> jps.setObject(1, someCalendar, Types.DOUBLE)); - assertEquals("Conversion from type " + someCalendar.getClass().getName() + " to DOUBLE not supported", sqle.getMessage()); + assertEquals("Conversion from type [" + someCalendar.getClass().getName() + "] to [DOUBLE] not supported", sqle.getMessage()); } public void testSettingDateValues() throws SQLException { @@ -478,12 +483,12 @@ public void testSettingDateValues() throws SQLException { jps.setObject(1, someDate); assertEquals(someDate, value(jps)); - assertEquals(TIMESTAMP, jdbcType(jps)); + assertEquals(DATE, jdbcType(jps)); assertTrue(value(jps) instanceof java.util.Date); jps.setObject(1, someDate, Types.VARCHAR); assertEquals(someDate.toString(), value(jps).toString()); - assertEquals(VARCHAR, jdbcType(jps)); + assertEquals(KEYWORD, jdbcType(jps)); } public void testThrownExceptionsWhenSettingDateValues() throws SQLException { @@ -491,7 +496,7 @@ public void testThrownExceptionsWhenSettingDateValues() throws SQLException { Date someDate = new Date(randomLong()); SQLException sqle = expectThrows(SQLFeatureNotSupportedException.class, () -> jps.setObject(1, someDate, Types.BIGINT)); - assertEquals("Conversion from type " + someDate.getClass().getName() + " to BIGINT not supported", sqle.getMessage()); + assertEquals("Conversion from type [" + someDate.getClass().getName() + "] to [BIGINT] not supported", sqle.getMessage()); } public void testSettingLocalDateTimeValues() throws SQLException { @@ -500,12 +505,12 @@ public void testSettingLocalDateTimeValues() throws SQLException { jps.setObject(1, ldt); assertEquals(Date.class, value(jps).getClass()); - assertEquals(TIMESTAMP, jdbcType(jps)); + assertEquals(DATE, jdbcType(jps)); assertTrue(value(jps) instanceof java.util.Date); jps.setObject(1, ldt, Types.VARCHAR); assertEquals(ldt.toString(), value(jps).toString()); - assertEquals(VARCHAR, jdbcType(jps)); + assertEquals(KEYWORD, jdbcType(jps)); } public void testThrownExceptionsWhenSettingLocalDateTimeValues() throws SQLException { @@ -513,7 +518,7 @@ public void testThrownExceptionsWhenSettingLocalDateTimeValues() throws SQLExcep LocalDateTime ldt = LocalDateTime.now(Clock.systemDefaultZone()); SQLException sqle = expectThrows(SQLFeatureNotSupportedException.class, () -> jps.setObject(1, ldt, Types.BIGINT)); - assertEquals("Conversion from type " + ldt.getClass().getName() + " to BIGINT not supported", sqle.getMessage()); + assertEquals("Conversion from type [" + ldt.getClass().getName() + "] to [BIGINT] not supported", sqle.getMessage()); } public void testSettingByteArrayValues() throws SQLException { @@ -522,22 +527,22 @@ public void testSettingByteArrayValues() throws SQLException { byte[] buffer = "some data".getBytes(StandardCharsets.UTF_8); jps.setBytes(1, buffer); assertEquals(byte[].class, value(jps).getClass()); - assertEquals(VARBINARY, jdbcType(jps)); + assertEquals(BINARY, jdbcType(jps)); jps.setObject(1, buffer); assertEquals(byte[].class, value(jps).getClass()); - assertEquals(VARBINARY, jdbcType(jps)); + assertEquals(BINARY, jdbcType(jps)); assertTrue(value(jps) instanceof byte[]); jps.setObject(1, buffer, Types.VARBINARY); assertEquals(value(jps), buffer); - assertEquals(VARBINARY, jdbcType(jps)); + assertEquals(BINARY, jdbcType(jps)); SQLException sqle = expectThrows(SQLFeatureNotSupportedException.class, () -> jps.setObject(1, buffer, Types.VARCHAR)); - assertEquals("Conversion from type byte[] to VARCHAR not supported", sqle.getMessage()); + assertEquals("Conversion from type [byte[]] to [VARCHAR] not supported", sqle.getMessage()); sqle = expectThrows(SQLFeatureNotSupportedException.class, () -> jps.setObject(1, buffer, Types.DOUBLE)); - assertEquals("Conversion from type byte[] to DOUBLE not supported", sqle.getMessage()); + assertEquals("Conversion from type [byte[]] to [DOUBLE] not supported", sqle.getMessage()); } public void testThrownExceptionsWhenSettingByteArrayValues() throws SQLException { @@ -545,17 +550,28 @@ public void testThrownExceptionsWhenSettingByteArrayValues() throws SQLException byte[] buffer = "foo".getBytes(StandardCharsets.UTF_8); SQLException sqle = expectThrows(SQLFeatureNotSupportedException.class, () -> jps.setObject(1, buffer, Types.VARCHAR)); - assertEquals("Conversion from type byte[] to VARCHAR not supported", sqle.getMessage()); + assertEquals("Conversion from type [byte[]] to [VARCHAR] not supported", sqle.getMessage()); sqle = expectThrows(SQLFeatureNotSupportedException.class, () -> jps.setObject(1, buffer, Types.DOUBLE)); - assertEquals("Conversion from type byte[] to DOUBLE not supported", sqle.getMessage()); + assertEquals("Conversion from type [byte[]] to [DOUBLE] not supported", sqle.getMessage()); + } + + public void testThrownExceptionsWhenSettingByteArrayValuesToEsTypes() throws SQLException { + JdbcPreparedStatement jps = createJdbcPreparedStatement(); + byte[] buffer = "foo".getBytes(StandardCharsets.UTF_8); + + SQLException sqle = expectThrows(SQLFeatureNotSupportedException.class, () -> jps.setObject(1, buffer, DataType.KEYWORD)); + assertEquals("Conversion from type [byte[]] to [KEYWORD] not supported", sqle.getMessage()); + + sqle = expectThrows(SQLFeatureNotSupportedException.class, () -> jps.setObject(1, buffer, DataType.INTERVAL_DAY_TO_MINUTE)); + assertEquals("Conversion from type [byte[]] to [INTERVAL_DAY_TO_MINUTE] not supported", sqle.getMessage()); } private JdbcPreparedStatement createJdbcPreparedStatement() throws SQLException { return new JdbcPreparedStatement(null, JdbcConfiguration.create("jdbc:es://l:1", null, 0), "?"); } - private SQLType jdbcType(JdbcPreparedStatement jps) throws SQLException { + private DataType jdbcType(JdbcPreparedStatement jps) throws SQLException { return jps.query.getParam(1).type; } diff --git a/x-pack/plugin/sql/jdbc/src/test/java/org/elasticsearch/xpack/sql/jdbc/jdbc/TypeConverterTests.java b/x-pack/plugin/sql/jdbc/src/test/java/org/elasticsearch/xpack/sql/jdbc/jdbc/TypeConverterTests.java index 51c130a39118e..a8e5539613e34 100644 --- a/x-pack/plugin/sql/jdbc/src/test/java/org/elasticsearch/xpack/sql/jdbc/jdbc/TypeConverterTests.java +++ b/x-pack/plugin/sql/jdbc/src/test/java/org/elasticsearch/xpack/sql/jdbc/jdbc/TypeConverterTests.java @@ -10,10 +10,10 @@ import org.elasticsearch.common.xcontent.XContentHelper; import org.elasticsearch.common.xcontent.json.JsonXContent; import org.elasticsearch.test.ESTestCase; +import org.elasticsearch.xpack.sql.jdbc.type.DataType; import org.joda.time.DateTime; import org.joda.time.ReadableDateTime; -import java.sql.JDBCType; import java.sql.Timestamp; import static org.hamcrest.Matchers.instanceOf; @@ -23,16 +23,16 @@ public class TypeConverterTests extends ESTestCase { public void testFloatAsNative() throws Exception { - assertThat(convertAsNative(42.0f, JDBCType.REAL), instanceOf(Float.class)); - assertThat(convertAsNative(42.0, JDBCType.REAL), instanceOf(Float.class)); - assertEquals(42.0f, (float) convertAsNative(42.0, JDBCType.REAL), 0.001f); - assertEquals(Float.NaN, convertAsNative(Float.NaN, JDBCType.REAL)); - assertEquals(Float.NEGATIVE_INFINITY, convertAsNative(Float.NEGATIVE_INFINITY, JDBCType.REAL)); - assertEquals(Float.POSITIVE_INFINITY, convertAsNative(Float.POSITIVE_INFINITY, JDBCType.REAL)); + assertThat(convertAsNative(42.0f, DataType.FLOAT), instanceOf(Float.class)); + assertThat(convertAsNative(42.0, DataType.FLOAT), instanceOf(Float.class)); + assertEquals(42.0f, (float) convertAsNative(42.0, DataType.FLOAT), 0.001f); + assertEquals(Float.NaN, convertAsNative(Float.NaN, DataType.FLOAT)); + assertEquals(Float.NEGATIVE_INFINITY, convertAsNative(Float.NEGATIVE_INFINITY, DataType.FLOAT)); + assertEquals(Float.POSITIVE_INFINITY, convertAsNative(Float.POSITIVE_INFINITY, DataType.FLOAT)); } public void testDoubleAsNative() throws Exception { - JDBCType type = randomFrom(JDBCType.FLOAT, JDBCType.DOUBLE); + DataType type = randomFrom(DataType.HALF_FLOAT, DataType.SCALED_FLOAT, DataType.DOUBLE); assertThat(convertAsNative(42.0, type), instanceOf(Double.class)); assertEquals(42.0f, (double) convertAsNative(42.0, type), 0.001f); assertEquals(Double.NaN, convertAsNative(Double.NaN, type)); @@ -42,11 +42,11 @@ public void testDoubleAsNative() throws Exception { public void testTimestampAsNative() throws Exception { DateTime now = DateTime.now(); - assertThat(convertAsNative(now, JDBCType.TIMESTAMP), instanceOf(Timestamp.class)); - assertEquals(now.getMillis(), ((Timestamp) convertAsNative(now, JDBCType.TIMESTAMP)).getTime()); + assertThat(convertAsNative(now, DataType.DATE), instanceOf(Timestamp.class)); + assertEquals(now.getMillis(), ((Timestamp) convertAsNative(now, DataType.DATE)).getTime()); } - private Object convertAsNative(Object value, JDBCType type) throws Exception { + private Object convertAsNative(Object value, DataType type) throws Exception { // Simulate sending over XContent XContentBuilder builder = JsonXContent.contentBuilder(); builder.startObject(); @@ -59,7 +59,7 @@ private Object convertAsNative(Object value, JDBCType type) throws Exception { builder.endObject(); builder.close(); Object copy = XContentHelper.convertToMap(BytesReference.bytes(builder), false, builder.contentType()).v2().get("value"); - return TypeConverter.convert(copy, type); + return TypeConverter.convert(copy, type, type.toString()); } } diff --git a/x-pack/plugin/sql/jdbc/src/test/java/org/elasticsearch/xpack/sql/jdbc/net/protocol/ColumnInfoTests.java b/x-pack/plugin/sql/jdbc/src/test/java/org/elasticsearch/xpack/sql/jdbc/net/protocol/ColumnInfoTests.java index 680284fae4f89..a58a4ffd3858e 100644 --- a/x-pack/plugin/sql/jdbc/src/test/java/org/elasticsearch/xpack/sql/jdbc/net/protocol/ColumnInfoTests.java +++ b/x-pack/plugin/sql/jdbc/src/test/java/org/elasticsearch/xpack/sql/jdbc/net/protocol/ColumnInfoTests.java @@ -6,28 +6,27 @@ package org.elasticsearch.xpack.sql.jdbc.net.protocol; import org.elasticsearch.test.ESTestCase; - -import java.sql.JDBCType; +import org.elasticsearch.xpack.sql.jdbc.type.DataType; public class ColumnInfoTests extends ESTestCase { - static ColumnInfo varcharInfo(String name) { - return new ColumnInfo(name, JDBCType.VARCHAR, "", "", "", "", 0); + static JdbcColumnInfo varcharInfo(String name) { + return new JdbcColumnInfo(name, DataType.KEYWORD, "", "", "", "", 0); } - static ColumnInfo intInfo(String name) { - return new ColumnInfo(name, JDBCType.INTEGER, "", "", "", "", 11); + static JdbcColumnInfo intInfo(String name) { + return new JdbcColumnInfo(name, DataType.INTEGER, "", "", "", "", 11); } - static ColumnInfo doubleInfo(String name) { - return new ColumnInfo(name, JDBCType.DOUBLE, "", "", "", "", 25); + static JdbcColumnInfo doubleInfo(String name) { + return new JdbcColumnInfo(name, DataType.DOUBLE, "", "", "", "", 25); } public void testToString() { - assertEquals("test.doc.a", - new ColumnInfo("a", JDBCType.VARCHAR, "test.doc", "as", "ads", "lab", 0).toString()); - assertEquals("test.doc.a", - new ColumnInfo("a", JDBCType.VARCHAR, "test.doc", "", "", "", 0).toString()); - assertEquals("string", varcharInfo("string").toString()); + assertEquals("test.doc.a", + new JdbcColumnInfo("a", DataType.KEYWORD, "test.doc", "as", "ads", "lab", 0).toString()); + assertEquals("test.doc.a", + new JdbcColumnInfo("a", DataType.KEYWORD, "test.doc", "", "", "", 0).toString()); + assertEquals("string", varcharInfo("string").toString()); assertEquals("int", intInfo("int").toString()); assertEquals("d", doubleInfo("d").toString()); } diff --git a/x-pack/plugin/sql/qa/src/main/java/org/elasticsearch/xpack/sql/qa/jdbc/DataLoader.java b/x-pack/plugin/sql/qa/src/main/java/org/elasticsearch/xpack/sql/qa/jdbc/DataLoader.java index 337706b5a5a19..3736453f94bc0 100644 --- a/x-pack/plugin/sql/qa/src/main/java/org/elasticsearch/xpack/sql/qa/jdbc/DataLoader.java +++ b/x-pack/plugin/sql/qa/src/main/java/org/elasticsearch/xpack/sql/qa/jdbc/DataLoader.java @@ -33,7 +33,8 @@ public class DataLoader { public static void main(String[] args) throws Exception { try (RestClient client = RestClient.builder(new HttpHost("localhost", 9200)).build()) { - loadEmpDatasetIntoEs(client); + //loadEmpDatasetIntoEs(client); + loadDocsDatasetIntoEs(client); LogManager.getLogger(DataLoader.class).info("Data loaded"); } } diff --git a/x-pack/plugin/sql/qa/src/main/java/org/elasticsearch/xpack/sql/qa/jdbc/JdbcAssert.java b/x-pack/plugin/sql/qa/src/main/java/org/elasticsearch/xpack/sql/qa/jdbc/JdbcAssert.java index 5b0a2e226abe8..cfb95374b27f1 100644 --- a/x-pack/plugin/sql/qa/src/main/java/org/elasticsearch/xpack/sql/qa/jdbc/JdbcAssert.java +++ b/x-pack/plugin/sql/qa/src/main/java/org/elasticsearch/xpack/sql/qa/jdbc/JdbcAssert.java @@ -5,14 +5,18 @@ */ package org.elasticsearch.xpack.sql.qa.jdbc; +import com.carrotsearch.hppc.IntObjectHashMap; + import org.apache.logging.log4j.Logger; +import org.elasticsearch.xpack.sql.jdbc.type.DataType; +import org.elasticsearch.xpack.sql.proto.StringUtils; import org.relique.jdbc.csv.CsvResultSet; -import java.sql.JDBCType; import java.sql.ResultSet; import java.sql.ResultSetMetaData; import java.sql.SQLException; import java.sql.Types; +import java.time.temporal.TemporalAmount; import java.util.ArrayList; import java.util.List; import java.util.Locale; @@ -34,6 +38,15 @@ * Utility class for doing JUnit-style asserts over JDBC. */ public class JdbcAssert { + + private static final IntObjectHashMap SQL_TO_TYPE = new IntObjectHashMap<>(); + + static { + for (DataType type : DataType.values()) { + SQL_TO_TYPE.putIfAbsent(type.getVendorTypeNumber().intValue(), type); + } + } + public static void assertResultSets(ResultSet expected, ResultSet actual) throws SQLException { assertResultSets(expected, actual, null); } @@ -114,16 +127,26 @@ public static void assertResultSetMetadata(ResultSet expected, ResultSet actual, if (expectedType == Types.FLOAT && expected instanceof CsvResultSet) { expectedType = Types.REAL; } + // handle intervals + if ((expectedType == Types.VARCHAR && expected instanceof CsvResultSet) && nameOf(actualType).startsWith("INTERVAL_")) { + expectedType = actualType; + } + // csv doesn't support NULL type so skip type checking if (actualType == Types.NULL && expected instanceof CsvResultSet) { expectedType = Types.NULL; } // when lenient is used, an int is equivalent to a short, etc... - assertEquals("Different column type for column [" + expectedName + "] (" + JDBCType.valueOf(expectedType) + " != " - + JDBCType.valueOf(actualType) + ")", expectedType, actualType); + assertEquals( + "Different column type for column [" + expectedName + "] (" + nameOf(expectedType) + " != " + nameOf(actualType) + ")", + expectedType, actualType); } } + + private static String nameOf(int sqlType) { + return SQL_TO_TYPE.get(sqlType).getName(); + } // The ResultSet is consumed and thus it should be closed public static void assertResultSetData(ResultSet expected, ResultSet actual, Logger logger) throws SQLException { @@ -201,6 +224,10 @@ else if (type == Types.DOUBLE) { } else if (type == Types.FLOAT) { assertEquals(msg, (float) expectedObject, (float) actualObject, 1f); } + // intervals + else if (type == Types.VARCHAR && actualObject instanceof TemporalAmount) { + assertEquals(msg, expectedObject, StringUtils.toString(actualObject)); + } // finally the actual comparison else { assertEquals(msg, expectedObject, actualObject); diff --git a/x-pack/plugin/sql/qa/src/main/java/org/elasticsearch/xpack/sql/qa/jdbc/JdbcTestUtils.java b/x-pack/plugin/sql/qa/src/main/java/org/elasticsearch/xpack/sql/qa/jdbc/JdbcTestUtils.java index 7b511ee63f943..709905965604a 100644 --- a/x-pack/plugin/sql/qa/src/main/java/org/elasticsearch/xpack/sql/qa/jdbc/JdbcTestUtils.java +++ b/x-pack/plugin/sql/qa/src/main/java/org/elasticsearch/xpack/sql/qa/jdbc/JdbcTestUtils.java @@ -9,11 +9,9 @@ import org.elasticsearch.xpack.sql.action.CliFormatter; import org.elasticsearch.xpack.sql.proto.ColumnInfo; -import java.sql.JDBCType; import java.sql.ResultSet; import java.sql.ResultSetMetaData; import java.sql.SQLException; -import java.sql.Timestamp; import java.util.ArrayList; import java.util.List; @@ -111,7 +109,7 @@ public static void logLikeCLI(ResultSet rs, Logger logger) throws SQLException { for (int i = 1; i <= columns; i++) { cols.add(new ColumnInfo(metaData.getTableName(i), metaData.getColumnName(i), metaData.getColumnTypeName(i), - JDBCType.valueOf(metaData.getColumnType(i)), metaData.getColumnDisplaySize(i))); + metaData.getColumnType(i), metaData.getColumnDisplaySize(i))); } @@ -120,13 +118,7 @@ public static void logLikeCLI(ResultSet rs, Logger logger) throws SQLException { while (rs.next()) { List entry = new ArrayList<>(columns); for (int i = 1; i <= columns; i++) { - Object value = rs.getObject(i); - // timestamp to string is similar but not ISO8601 - fix it - if (value instanceof Timestamp) { - Timestamp ts = (Timestamp) value; - value = ts.toInstant().toString(); - } - entry.add(value); + entry.add(rs.getObject(i)); } data.add(entry); } diff --git a/x-pack/plugin/sql/qa/src/main/java/org/elasticsearch/xpack/sql/qa/jdbc/ResultSetTestCase.java b/x-pack/plugin/sql/qa/src/main/java/org/elasticsearch/xpack/sql/qa/jdbc/ResultSetTestCase.java index bd7914278b360..ebaa09c501896 100644 --- a/x-pack/plugin/sql/qa/src/main/java/org/elasticsearch/xpack/sql/qa/jdbc/ResultSetTestCase.java +++ b/x-pack/plugin/sql/qa/src/main/java/org/elasticsearch/xpack/sql/qa/jdbc/ResultSetTestCase.java @@ -15,7 +15,7 @@ import org.elasticsearch.common.xcontent.json.JsonXContent; import org.elasticsearch.xpack.sql.jdbc.jdbc.JdbcConfiguration; import org.elasticsearch.xpack.sql.jdbc.jdbcx.JdbcDataSource; -import org.elasticsearch.xpack.sql.type.DataType; +import org.elasticsearch.xpack.sql.jdbc.type.DataType; import java.io.IOException; import java.io.InputStream; @@ -69,14 +69,14 @@ public class ResultSetTestCase extends JdbcIntegrationTestCase { + "test_long, test_short, test_double, test_float, test_keyword, test_date FROM test"; static final String SELECT_WILDCARD = "SELECT * FROM test"; static { - dateTimeTestingFields.put(new Tuple("test_boolean", true), DataType.BOOLEAN.jdbcType); - dateTimeTestingFields.put(new Tuple("test_byte", 1), DataType.BYTE.jdbcType); - dateTimeTestingFields.put(new Tuple("test_integer", 1), DataType.INTEGER.jdbcType); - dateTimeTestingFields.put(new Tuple("test_long", 1L), DataType.LONG.jdbcType); - dateTimeTestingFields.put(new Tuple("test_short", 1), DataType.SHORT.jdbcType); - dateTimeTestingFields.put(new Tuple("test_double", 1d), DataType.DOUBLE.jdbcType); - dateTimeTestingFields.put(new Tuple("test_float", 1f), DataType.FLOAT.jdbcType); - dateTimeTestingFields.put(new Tuple("test_keyword", "true"), DataType.KEYWORD.jdbcType); + dateTimeTestingFields.put(new Tuple("test_boolean", true), DataType.BOOLEAN); + dateTimeTestingFields.put(new Tuple("test_byte", 1), DataType.BYTE); + dateTimeTestingFields.put(new Tuple("test_integer", 1), DataType.INTEGER); + dateTimeTestingFields.put(new Tuple("test_long", 1L), DataType.LONG); + dateTimeTestingFields.put(new Tuple("test_short", 1), DataType.SHORT); + dateTimeTestingFields.put(new Tuple("test_double", 1d), DataType.DOUBLE); + dateTimeTestingFields.put(new Tuple("test_float", 1f), DataType.FLOAT); + dateTimeTestingFields.put(new Tuple("test_keyword", "true"), DataType.KEYWORD); } // Byte values testing @@ -193,17 +193,17 @@ public void testGettingInvalidByte() throws Exception { assertEquals(format(Locale.ROOT, "Numeric %s out of range", Double.toString(floatNotByte)), sqle.getMessage()); sqle = expectThrows(SQLException.class, () -> results.getByte("test_keyword")); - assertEquals(format(Locale.ROOT, "Unable to convert value [%.128s] of type [VARCHAR] to a Byte", randomString), + assertEquals(format(Locale.ROOT, "Unable to convert value [%.128s] of type [KEYWORD] to [Byte]", randomString), sqle.getMessage()); sqle = expectThrows(SQLException.class, () -> results.getObject("test_keyword", Byte.class)); - assertEquals(format(Locale.ROOT, "Unable to convert value [%.128s] of type [VARCHAR] to a Byte", randomString), + assertEquals(format(Locale.ROOT, "Unable to convert value [%.128s] of type [KEYWORD] to [Byte]", randomString), sqle.getMessage()); sqle = expectThrows(SQLException.class, () -> results.getByte("test_date")); - assertEquals(format(Locale.ROOT, "Unable to convert value [%.128s] of type [TIMESTAMP] to a Byte", randomDate), + assertEquals(format(Locale.ROOT, "Unable to convert value [%.128s] of type [DATE] to [Byte]", randomDate), sqle.getMessage()); sqle = expectThrows(SQLException.class, () -> results.getObject("test_date", Byte.class)); - assertEquals(format(Locale.ROOT, "Unable to convert value [%.128s] of type [TIMESTAMP] to a Byte", randomDate), + assertEquals(format(Locale.ROOT, "Unable to convert value [%.128s] of type [DATE] to [Byte]", randomDate), sqle.getMessage()); }); } @@ -316,17 +316,17 @@ public void testGettingInvalidShort() throws Exception { assertEquals(format(Locale.ROOT, "Numeric %s out of range", Double.toString(floatNotShort)), sqle.getMessage()); sqle = expectThrows(SQLException.class, () -> results.getShort("test_keyword")); - assertEquals(format(Locale.ROOT, "Unable to convert value [%.128s] of type [VARCHAR] to a Short", randomString), + assertEquals(format(Locale.ROOT, "Unable to convert value [%.128s] of type [KEYWORD] to [Short]", randomString), sqle.getMessage()); sqle = expectThrows(SQLException.class, () -> results.getObject("test_keyword", Short.class)); - assertEquals(format(Locale.ROOT, "Unable to convert value [%.128s] of type [VARCHAR] to a Short", randomString), + assertEquals(format(Locale.ROOT, "Unable to convert value [%.128s] of type [KEYWORD] to [Short]", randomString), sqle.getMessage()); sqle = expectThrows(SQLException.class, () -> results.getShort("test_date")); - assertEquals(format(Locale.ROOT, "Unable to convert value [%.128s] of type [TIMESTAMP] to a Short", randomDate), + assertEquals(format(Locale.ROOT, "Unable to convert value [%.128s] of type [DATE] to [Short]", randomDate), sqle.getMessage()); sqle = expectThrows(SQLException.class, () -> results.getObject("test_date", Short.class)); - assertEquals(format(Locale.ROOT, "Unable to convert value [%.128s] of type [TIMESTAMP] to a Short", randomDate), + assertEquals(format(Locale.ROOT, "Unable to convert value [%.128s] of type [DATE] to [Short]", randomDate), sqle.getMessage()); }); } @@ -431,17 +431,17 @@ public void testGettingInvalidInteger() throws Exception { assertEquals(format(Locale.ROOT, "Numeric %s out of range", Double.toString(floatNotInt)), sqle.getMessage()); sqle = expectThrows(SQLException.class, () -> results.getInt("test_keyword")); - assertEquals(format(Locale.ROOT, "Unable to convert value [%.128s] of type [VARCHAR] to an Integer", randomString), + assertEquals(format(Locale.ROOT, "Unable to convert value [%.128s] of type [KEYWORD] to [Integer]", randomString), sqle.getMessage()); sqle = expectThrows(SQLException.class, () -> results.getObject("test_keyword", Integer.class)); - assertEquals(format(Locale.ROOT, "Unable to convert value [%.128s] of type [VARCHAR] to an Integer", randomString), + assertEquals(format(Locale.ROOT, "Unable to convert value [%.128s] of type [KEYWORD] to [Integer]", randomString), sqle.getMessage()); sqle = expectThrows(SQLException.class, () -> results.getInt("test_date")); - assertEquals(format(Locale.ROOT, "Unable to convert value [%.128s] of type [TIMESTAMP] to an Integer", randomDate), + assertEquals(format(Locale.ROOT, "Unable to convert value [%.128s] of type [DATE] to [Integer]", randomDate), sqle.getMessage()); sqle = expectThrows(SQLException.class, () -> results.getObject("test_date", Integer.class)); - assertEquals(format(Locale.ROOT, "Unable to convert value [%.128s] of type [TIMESTAMP] to an Integer", randomDate), + assertEquals(format(Locale.ROOT, "Unable to convert value [%.128s] of type [DATE] to [Integer]", randomDate), sqle.getMessage()); }); } @@ -533,17 +533,17 @@ public void testGettingInvalidLong() throws Exception { assertEquals(format(Locale.ROOT, "Numeric %s out of range", Double.toString(floatNotLong)), sqle.getMessage()); sqle = expectThrows(SQLException.class, () -> results.getLong("test_keyword")); - assertEquals(format(Locale.ROOT, "Unable to convert value [%.128s] of type [VARCHAR] to a Long", randomString), + assertEquals(format(Locale.ROOT, "Unable to convert value [%.128s] of type [KEYWORD] to [Long]", randomString), sqle.getMessage()); sqle = expectThrows(SQLException.class, () -> results.getObject("test_keyword", Long.class)); - assertEquals(format(Locale.ROOT, "Unable to convert value [%.128s] of type [VARCHAR] to a Long", randomString), + assertEquals(format(Locale.ROOT, "Unable to convert value [%.128s] of type [KEYWORD] to [Long]", randomString), sqle.getMessage()); sqle = expectThrows(SQLException.class, () -> results.getLong("test_date")); - assertEquals(format(Locale.ROOT, "Unable to convert value [%.128s] of type [TIMESTAMP] to a Long", randomDate), + assertEquals(format(Locale.ROOT, "Unable to convert value [%.128s] of type [DATE] to [Long]", randomDate), sqle.getMessage()); sqle = expectThrows(SQLException.class, () -> results.getObject("test_date", Long.class)); - assertEquals(format(Locale.ROOT, "Unable to convert value [%.128s] of type [TIMESTAMP] to a Long", randomDate), + assertEquals(format(Locale.ROOT, "Unable to convert value [%.128s] of type [DATE] to [Long]", randomDate), sqle.getMessage()); }); } @@ -616,17 +616,17 @@ public void testGettingInvalidDouble() throws Exception { results.next(); SQLException sqle = expectThrows(SQLException.class, () -> results.getDouble("test_keyword")); - assertEquals(format(Locale.ROOT, "Unable to convert value [%.128s] of type [VARCHAR] to a Double", randomString), + assertEquals(format(Locale.ROOT, "Unable to convert value [%.128s] of type [KEYWORD] to [Double]", randomString), sqle.getMessage()); sqle = expectThrows(SQLException.class, () -> results.getObject("test_keyword", Double.class)); - assertEquals(format(Locale.ROOT, "Unable to convert value [%.128s] of type [VARCHAR] to a Double", randomString), + assertEquals(format(Locale.ROOT, "Unable to convert value [%.128s] of type [KEYWORD] to [Double]", randomString), sqle.getMessage()); sqle = expectThrows(SQLException.class, () -> results.getDouble("test_date")); - assertEquals(format(Locale.ROOT, "Unable to convert value [%.128s] of type [TIMESTAMP] to a Double", randomDate), + assertEquals(format(Locale.ROOT, "Unable to convert value [%.128s] of type [DATE] to [Double]", randomDate), sqle.getMessage()); sqle = expectThrows(SQLException.class, () -> results.getObject("test_date", Double.class)); - assertEquals(format(Locale.ROOT, "Unable to convert value [%.128s] of type [TIMESTAMP] to a Double", randomDate), + assertEquals(format(Locale.ROOT, "Unable to convert value [%.128s] of type [DATE] to [Double]", randomDate), sqle.getMessage()); }); } @@ -699,17 +699,17 @@ public void testGettingInvalidFloat() throws Exception { results.next(); SQLException sqle = expectThrows(SQLException.class, () -> results.getFloat("test_keyword")); - assertEquals(format(Locale.ROOT, "Unable to convert value [%.128s] of type [VARCHAR] to a Float", randomString), + assertEquals(format(Locale.ROOT, "Unable to convert value [%.128s] of type [KEYWORD] to [Float]", randomString), sqle.getMessage()); sqle = expectThrows(SQLException.class, () -> results.getObject("test_keyword", Float.class)); - assertEquals(format(Locale.ROOT, "Unable to convert value [%.128s] of type [VARCHAR] to a Float", randomString), + assertEquals(format(Locale.ROOT, "Unable to convert value [%.128s] of type [KEYWORD] to [Float]", randomString), sqle.getMessage()); sqle = expectThrows(SQLException.class, () -> results.getFloat("test_date")); - assertEquals(format(Locale.ROOT, "Unable to convert value [%.128s] of type [TIMESTAMP] to a Float", randomDate), + assertEquals(format(Locale.ROOT, "Unable to convert value [%.128s] of type [DATE] to [Float]", randomDate), sqle.getMessage()); sqle = expectThrows(SQLException.class, () -> results.getObject("test_date", Float.class)); - assertEquals(format(Locale.ROOT, "Unable to convert value [%.128s] of type [TIMESTAMP] to a Float", randomDate), + assertEquals(format(Locale.ROOT, "Unable to convert value [%.128s] of type [DATE] to [Float]", randomDate), sqle.getMessage()); }); } @@ -767,7 +767,7 @@ public void testGettingBooleanValues() throws Exception { assertEquals("Expected: but was: for field " + fld, true, results.getObject(fld, Boolean.class)); } SQLException sqle = expectThrows(SQLException.class, () -> results.getBoolean("test_date")); - assertEquals(format(Locale.ROOT, "Unable to convert value [%.128s] of type [TIMESTAMP] to a Boolean", randomDate1), + assertEquals(format(Locale.ROOT, "Unable to convert value [%.128s] of type [DATE] to [Boolean]", randomDate1), sqle.getMessage()); results.next(); @@ -777,11 +777,11 @@ public void testGettingBooleanValues() throws Exception { assertEquals("Expected: but was: for field " + fld, false, results.getObject(fld, Boolean.class)); } sqle = expectThrows(SQLException.class, () -> results.getBoolean("test_date")); - assertEquals(format(Locale.ROOT, "Unable to convert value [%.128s] of type [TIMESTAMP] to a Boolean", randomDate2), + assertEquals(format(Locale.ROOT, "Unable to convert value [%.128s] of type [DATE] to [Boolean]", randomDate2), sqle.getMessage()); sqle = expectThrows(SQLException.class, () -> results.getObject("test_date", Boolean.class)); - assertEquals(format(Locale.ROOT, "Unable to convert value [%.128s] of type [TIMESTAMP] to a Boolean", randomDate2), + assertEquals(format(Locale.ROOT, "Unable to convert value [%.128s] of type [DATE] to [Boolean]", randomDate2), sqle.getMessage()); results.next(); diff --git a/x-pack/plugin/sql/qa/src/main/java/org/elasticsearch/xpack/sql/qa/jdbc/SimpleExampleTestCase.java b/x-pack/plugin/sql/qa/src/main/java/org/elasticsearch/xpack/sql/qa/jdbc/SimpleExampleTestCase.java index 4d3b7698e5b5a..ddf5ef88a4318 100644 --- a/x-pack/plugin/sql/qa/src/main/java/org/elasticsearch/xpack/sql/qa/jdbc/SimpleExampleTestCase.java +++ b/x-pack/plugin/sql/qa/src/main/java/org/elasticsearch/xpack/sql/qa/jdbc/SimpleExampleTestCase.java @@ -32,7 +32,7 @@ public void testSimpleExample() throws Exception { SQLException e = expectThrows(SQLException.class, () -> results.getInt(1)); assertThat(e.getMessage(), containsString("Unable to convert " - + "value [Don Quixote] of type [VARCHAR] to an Integer")); + + "value [Don Quixote] of type [TEXT] to [Integer]")); assertFalse(results.next()); } // end::simple_example diff --git a/x-pack/plugin/sql/qa/src/main/resources/datetime-interval.csv-spec b/x-pack/plugin/sql/qa/src/main/resources/datetime-interval.csv-spec new file mode 100644 index 0000000000000..0a2dda3f46eda --- /dev/null +++ b/x-pack/plugin/sql/qa/src/main/resources/datetime-interval.csv-spec @@ -0,0 +1,133 @@ +// +// Unfortunately H2 doesn't support INTERVAL type, +// hence why all INTERVAL tests need to be done manually +// + +testExactIntervals +SELECT INTERVAL 1 YEAR AS y, INTERVAL 2 MONTH AS m, INTERVAL 3 DAY AS d, INTERVAL 4 HOUR AS h, INTERVAL 5 MINUTE AS mm, INTERVAL 6 SECOND AS s; + + y | m | d | h | mm | s +---------------+---------------+---------------+---------------+---------------+--------------- ++1-0 |+0-2 |+3 00:00:00.0 |+0 04:00:00.0 |+0 00:05:00.0 |+0 00:00:06.0 +; + +testExactIntervalPlural +SELECT INTERVAL 1 YEARS AS y, INTERVAL 2 MONTHS AS m, INTERVAL 3 DAYS AS d, INTERVAL 4 HOURS AS h, INTERVAL 5 MINUTES AS mm, INTERVAL 6 SECONDS AS s; + + y | m | d | h | mm | s +---------------+---------------+---------------+---------------+---------------+--------------- ++1-0 |+0-2 |+3 00:00:00.0 |+0 04:00:00.0 |+0 00:05:00.0 |+0 00:00:06.0 +; + +// take the examples from https://docs.microsoft.com/en-us/sql/odbc/reference/appendixes/interval-literals?view=sql-server-2017 +testYear +SELECT INTERVAL '326' YEAR; + +INTERVAL '326' YEAR +------------------- ++326-0 +; + +testMonth +SELECT INTERVAL '326' MONTH; + +INTERVAL '326' MONTH +-------------------- ++0-326 +; + +testDay +SELECT INTERVAL '3261' DAY; + +INTERVAL '3261' DAY +------------------- ++3261 00:00:00.0 +; + +testHour +SELECT INTERVAL '163' HOUR; + +INTERVAL '163' HOUR +------------------- ++6 19:00:00.0 +; + +testMinute +SELECT INTERVAL '163' MINUTE; + +INTERVAL '163' MINUTE +--------------------- ++0 02:43:00.0 +; + +testSecond +SELECT INTERVAL '223.16' SECOND; + +INTERVAL '223.16' SECOND +------------------------ ++0 00:03:43.16 +; + +testYearMonth +SELECT INTERVAL '163-11' YEAR TO MONTH; + +INTERVAL '163-11' YEAR TO MONTH +------------------------------- ++163-11 +; + +testDayHour +SELECT INTERVAL '163 12' DAY TO HOUR; + +INTERVAL '163 12' DAY TO HOUR +----------------------------- ++163 12:00:00.0 +; + +testDayMinute +SELECT INTERVAL '163 12:39' DAY TO MINUTE AS interval; + +interval +--------------- ++163 12:39:00.0 +; + +testDaySecond +SELECT INTERVAL '163 12:39:59.163' DAY TO SECOND AS interval; + +interval +--------------- ++163 12:39:59.163 +; + +testDaySecondNegative +SELECT INTERVAL -'163 23:39:56.23' DAY TO SECOND AS interval; + +interval +--------------- +-163 23:39:56.23 +; + +testHourMinute +SELECT INTERVAL '163:39' HOUR TO MINUTE AS interval; + +interval +--------------- ++6 19:39:00.0 +; + +testHourSecond +SELECT INTERVAL '163:39:59.163' HOUR TO SECOND AS interval; + +interval +--------------- ++6 19:39:59.163 +; + +testMinuteSecond +SELECT INTERVAL '163:59.163' MINUTE TO SECOND AS interval; + +interval +--------------- ++0 02:43:59.163 +; diff --git a/x-pack/plugin/sql/sql-action/src/main/java/org/elasticsearch/xpack/sql/action/AbstractSqlQueryRequest.java b/x-pack/plugin/sql/sql-action/src/main/java/org/elasticsearch/xpack/sql/action/AbstractSqlQueryRequest.java index 7881db84f0f17..28ca05baf3109 100644 --- a/x-pack/plugin/sql/sql-action/src/main/java/org/elasticsearch/xpack/sql/action/AbstractSqlQueryRequest.java +++ b/x-pack/plugin/sql/sql-action/src/main/java/org/elasticsearch/xpack/sql/action/AbstractSqlQueryRequest.java @@ -18,7 +18,6 @@ import org.elasticsearch.xpack.sql.proto.Mode; import org.elasticsearch.xpack.sql.proto.Protocol; import org.elasticsearch.xpack.sql.proto.SqlTypedParamValue; -import org.elasticsearch.xpack.sql.type.DataType; import java.io.IOException; import java.util.Collections; @@ -188,12 +187,12 @@ public AbstractSqlQueryRequest(StreamInput in) throws IOException { } public static void writeSqlTypedParamValue(StreamOutput out, SqlTypedParamValue value) throws IOException { - out.writeEnum(value.dataType); + out.writeString(value.type); out.writeGenericValue(value.value); } public static SqlTypedParamValue readSqlTypedParamValue(StreamInput in) throws IOException { - return new SqlTypedParamValue(in.readEnum(DataType.class), in.readGenericValue()); + return new SqlTypedParamValue(in.readString(), in.readGenericValue()); } @Override @@ -213,9 +212,15 @@ public void writeTo(StreamOutput out) throws IOException { @Override public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - if (!super.equals(o)) return false; + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + if (!super.equals(o)) { + return false; + } AbstractSqlQueryRequest that = (AbstractSqlQueryRequest) o; return fetchSize == that.fetchSize && Objects.equals(query, that.query) && diff --git a/x-pack/plugin/sql/sql-action/src/main/java/org/elasticsearch/xpack/sql/action/CliFormatter.java b/x-pack/plugin/sql/sql-action/src/main/java/org/elasticsearch/xpack/sql/action/CliFormatter.java index c773e75aa18be..ecda7708c38a0 100644 --- a/x-pack/plugin/sql/sql-action/src/main/java/org/elasticsearch/xpack/sql/action/CliFormatter.java +++ b/x-pack/plugin/sql/sql-action/src/main/java/org/elasticsearch/xpack/sql/action/CliFormatter.java @@ -9,6 +9,7 @@ import org.elasticsearch.common.io.stream.StreamOutput; import org.elasticsearch.common.io.stream.Writeable; import org.elasticsearch.xpack.sql.proto.ColumnInfo; +import org.elasticsearch.xpack.sql.proto.StringUtils; import java.io.IOException; import java.util.Arrays; @@ -117,9 +118,7 @@ private String formatWithoutHeader(StringBuilder sb, List> rows) { sb.append('|'); } - // TODO are we sure toString is correct here? What about dates that come back as longs. - // Tracked by https://github.com/elastic/x-pack-elasticsearch/issues/3081 - String string = Objects.toString(row.get(i)); + String string = StringUtils.toString(row.get(i)); if (string.length() <= width[i]) { // Pad sb.append(string); @@ -154,8 +153,12 @@ int estimateSize(int rows) { @Override public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } CliFormatter that = (CliFormatter) o; return Arrays.equals(width, that.width); } diff --git a/x-pack/plugin/sql/sql-action/src/main/java/org/elasticsearch/xpack/sql/action/SqlQueryRequest.java b/x-pack/plugin/sql/sql-action/src/main/java/org/elasticsearch/xpack/sql/action/SqlQueryRequest.java index a7166f56790c0..b0701f82c91ff 100644 --- a/x-pack/plugin/sql/sql-action/src/main/java/org/elasticsearch/xpack/sql/action/SqlQueryRequest.java +++ b/x-pack/plugin/sql/sql-action/src/main/java/org/elasticsearch/xpack/sql/action/SqlQueryRequest.java @@ -119,5 +119,4 @@ public static SqlQueryRequest fromXContent(XContentParser parser, Mode mode) { request.mode(mode); return request; } - -} +} \ No newline at end of file diff --git a/x-pack/plugin/sql/sql-action/src/main/java/org/elasticsearch/xpack/sql/action/SqlQueryResponse.java b/x-pack/plugin/sql/sql-action/src/main/java/org/elasticsearch/xpack/sql/action/SqlQueryResponse.java index da4037ac95c64..cb693b2f6d007 100644 --- a/x-pack/plugin/sql/sql-action/src/main/java/org/elasticsearch/xpack/sql/action/SqlQueryResponse.java +++ b/x-pack/plugin/sql/sql-action/src/main/java/org/elasticsearch/xpack/sql/action/SqlQueryResponse.java @@ -17,7 +17,6 @@ import org.joda.time.ReadableDateTime; import java.io.IOException; -import java.sql.JDBCType; import java.util.ArrayList; import java.util.List; import java.util.Objects; @@ -180,11 +179,10 @@ public static ColumnInfo readColumnInfo(StreamInput in) throws IOException { String table = in.readString(); String name = in.readString(); String esType = in.readString(); - JDBCType jdbcType; + Integer jdbcType; int displaySize; if (in.readBoolean()) { - // FIXME: this needs changing to allow custom types - jdbcType = JDBCType.valueOf(in.readVInt()); + jdbcType = in.readVInt(); displaySize = in.readVInt(); } else { jdbcType = null; @@ -199,7 +197,7 @@ public static void writeColumnInfo(StreamOutput out, ColumnInfo columnInfo) thro out.writeString(columnInfo.esType()); if (columnInfo.jdbcType() != null) { out.writeBoolean(true); - out.writeVInt(columnInfo.jdbcType().getVendorTypeNumber()); + out.writeVInt(columnInfo.jdbcType()); out.writeVInt(columnInfo.displaySize()); } else { out.writeBoolean(false); diff --git a/x-pack/plugin/sql/sql-action/src/test/java/org/elasticsearch/xpack/sql/action/SqlQueryRequestTests.java b/x-pack/plugin/sql/sql-action/src/test/java/org/elasticsearch/xpack/sql/action/SqlQueryRequestTests.java index d552f844d0875..45113fcc62e4c 100644 --- a/x-pack/plugin/sql/sql-action/src/test/java/org/elasticsearch/xpack/sql/action/SqlQueryRequestTests.java +++ b/x-pack/plugin/sql/sql-action/src/test/java/org/elasticsearch/xpack/sql/action/SqlQueryRequestTests.java @@ -16,7 +16,6 @@ import org.elasticsearch.test.ESTestCase; import org.elasticsearch.xpack.sql.proto.Mode; import org.elasticsearch.xpack.sql.proto.SqlTypedParamValue; -import org.elasticsearch.xpack.sql.type.DataType; import org.junit.Before; import java.util.ArrayList; @@ -65,11 +64,11 @@ public List randomParameters() { List arr = new ArrayList<>(len); for (int i = 0; i < len; i++) { @SuppressWarnings("unchecked") Supplier supplier = randomFrom( - () -> new SqlTypedParamValue(DataType.BOOLEAN, randomBoolean()), - () -> new SqlTypedParamValue(DataType.LONG, randomLong()), - () -> new SqlTypedParamValue(DataType.DOUBLE, randomDouble()), - () -> new SqlTypedParamValue(DataType.NULL, null), - () -> new SqlTypedParamValue(DataType.KEYWORD, randomAlphaOfLength(10)) + () -> new SqlTypedParamValue("boolean", randomBoolean()), + () -> new SqlTypedParamValue("long", randomLong()), + () -> new SqlTypedParamValue("double", randomDouble()), + () -> new SqlTypedParamValue("null", null), + () -> new SqlTypedParamValue("keyword", randomAlphaOfLength(10)) ); arr.add(supplier.get()); } diff --git a/x-pack/plugin/sql/sql-action/src/test/java/org/elasticsearch/xpack/sql/action/SqlQueryResponseTests.java b/x-pack/plugin/sql/sql-action/src/test/java/org/elasticsearch/xpack/sql/action/SqlQueryResponseTests.java index 0957b4c5202c1..7d9fa40a416ef 100644 --- a/x-pack/plugin/sql/sql-action/src/test/java/org/elasticsearch/xpack/sql/action/SqlQueryResponseTests.java +++ b/x-pack/plugin/sql/sql-action/src/test/java/org/elasticsearch/xpack/sql/action/SqlQueryResponseTests.java @@ -16,7 +16,6 @@ import org.elasticsearch.xpack.sql.proto.ColumnInfo; import java.io.IOException; -import java.sql.JDBCType; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -46,7 +45,7 @@ public static SqlQueryResponse createRandomInstance(String cursor) { columns = new ArrayList<>(columnCount); for (int i = 0; i < columnCount; i++) { columns.add(new ColumnInfo(randomAlphaOfLength(10), randomAlphaOfLength(10), randomAlphaOfLength(10), - randomFrom(JDBCType.values()), randomInt(25))); + randomInt(), randomInt(25))); } } @@ -95,7 +94,7 @@ public void testToXContent() throws IOException { assertEquals(columnInfo.name(), columnMap.get("name")); assertEquals(columnInfo.esType(), columnMap.get("type")); assertEquals(columnInfo.displaySize(), columnMap.get("display_size")); - assertEquals(columnInfo.jdbcType().getVendorTypeNumber(), columnMap.get("jdbc_type")); + assertEquals(columnInfo.jdbcType(), columnMap.get("jdbc_type")); } } else { assertNull(rootMap.get("columns")); diff --git a/x-pack/plugin/sql/sql-cli/src/test/java/org/elasticsearch/xpack/sql/cli/command/ServerQueryCliCommandTests.java b/x-pack/plugin/sql/sql-cli/src/test/java/org/elasticsearch/xpack/sql/cli/command/ServerQueryCliCommandTests.java index 86ebfa52fe49f..015769f641bf6 100644 --- a/x-pack/plugin/sql/sql-cli/src/test/java/org/elasticsearch/xpack/sql/cli/command/ServerQueryCliCommandTests.java +++ b/x-pack/plugin/sql/sql-cli/src/test/java/org/elasticsearch/xpack/sql/cli/command/ServerQueryCliCommandTests.java @@ -11,11 +11,12 @@ import org.elasticsearch.xpack.sql.proto.ColumnInfo; import org.elasticsearch.xpack.sql.proto.SqlQueryResponse; -import java.sql.JDBCType; import java.sql.SQLException; +import java.sql.Types; import java.util.Collections; import java.util.List; +import static java.util.Collections.singletonList; import static org.mockito.Matchers.any; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.mock; @@ -108,14 +109,14 @@ private SqlQueryResponse fakeResponse(String cursor, boolean includeColumns, Str List> rows; List columns; if (includeColumns) { - columns = Collections.singletonList(new ColumnInfo("", "field", "string", JDBCType.VARCHAR, 0)); + columns = singletonList(new ColumnInfo("", "field", "string", Types.VARCHAR, 0)); } else { columns = null; } if (val != null) { - rows = Collections.singletonList(Collections.singletonList(val)); + rows = singletonList(Collections.singletonList(val)); } else { - rows = Collections.singletonList(Collections.emptyList()); + rows = singletonList(Collections.emptyList()); } return new SqlQueryResponse(cursor, columns, rows); } diff --git a/x-pack/plugin/sql/sql-proto/src/main/java/org/elasticsearch/xpack/sql/proto/ColumnInfo.java b/x-pack/plugin/sql/sql-proto/src/main/java/org/elasticsearch/xpack/sql/proto/ColumnInfo.java index 28eb1b37fdcbc..64ff8b4b96332 100644 --- a/x-pack/plugin/sql/sql-proto/src/main/java/org/elasticsearch/xpack/sql/proto/ColumnInfo.java +++ b/x-pack/plugin/sql/sql-proto/src/main/java/org/elasticsearch/xpack/sql/proto/ColumnInfo.java @@ -13,15 +13,15 @@ import org.elasticsearch.common.xcontent.XContentParser; import java.io.IOException; -import java.sql.JDBCType; -import java.sql.SQLType; import java.util.Objects; import static org.elasticsearch.common.xcontent.ConstructingObjectParser.constructorArg; import static org.elasticsearch.common.xcontent.ConstructingObjectParser.optionalConstructorArg; /** - * Information about a column returned with first query response + * Information about a column returned with first query response. + * As this represents the response for all drivers, it is important for it to be explicit about + * its structure, in particular types (using es_type and jdbc_type instead of DataType). */ public class ColumnInfo implements ToXContentObject { @@ -31,7 +31,7 @@ public class ColumnInfo implements ToXContentObject { objects[0] == null ? "" : (String) objects[0], (String) objects[1], (String) objects[2], - objects[3] == null ? null : JDBCType.valueOf((int) objects[3]), + objects[3] == null ? null : (int) objects[3], objects[4] == null ? 0 : (int) objects[4])); private static final ParseField TABLE = new ParseField("table"); @@ -52,10 +52,10 @@ public class ColumnInfo implements ToXContentObject { private final String name; private final String esType; @Nullable - private final SQLType jdbcType; + private final Integer jdbcType; private final int displaySize; - public ColumnInfo(String table, String name, String esType, SQLType jdbcType, int displaySize) { + public ColumnInfo(String table, String name, String esType, Integer jdbcType, int displaySize) { this.table = table; this.name = name; this.esType = esType; @@ -80,11 +80,7 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws builder.field("name", name); builder.field("type", esType); if (jdbcType != null) { - // FIXME: make this pluggable by saving the SQLType.getVendorName - if (!(jdbcType instanceof JDBCType)) { - throw new IOException("Unknown jdbc type " + jdbcType); - } - builder.field("jdbc_type", jdbcType.getVendorTypeNumber()); + builder.field("jdbc_type", jdbcType); builder.field("display_size", displaySize); } return builder.endObject(); @@ -119,7 +115,7 @@ public String esType() { /** * The type of the column as it would be returned by a JDBC driver. */ - public SQLType jdbcType() { + public Integer jdbcType() { return jdbcType; } @@ -143,7 +139,7 @@ public boolean equals(Object o) { Objects.equals(table, that.table) && Objects.equals(name, that.name) && Objects.equals(esType, that.esType) && - jdbcType == that.jdbcType; + Objects.equals(jdbcType, that.jdbcType); } @Override @@ -156,4 +152,4 @@ public int hashCode() { public String toString() { return ProtoUtils.toString(this); } -} +} \ No newline at end of file diff --git a/x-pack/plugin/sql/sql-proto/src/main/java/org/elasticsearch/xpack/sql/proto/ProtoUtils.java b/x-pack/plugin/sql/sql-proto/src/main/java/org/elasticsearch/xpack/sql/proto/ProtoUtils.java index 60f4405decda6..9cafd58fec4e9 100644 --- a/x-pack/plugin/sql/sql-proto/src/main/java/org/elasticsearch/xpack/sql/proto/ProtoUtils.java +++ b/x-pack/plugin/sql/sql-proto/src/main/java/org/elasticsearch/xpack/sql/proto/ProtoUtils.java @@ -19,7 +19,6 @@ public final class ProtoUtils { private ProtoUtils() { - } /** diff --git a/x-pack/plugin/sql/sql-proto/src/main/java/org/elasticsearch/xpack/sql/proto/SqlTypedParamValue.java b/x-pack/plugin/sql/sql-proto/src/main/java/org/elasticsearch/xpack/sql/proto/SqlTypedParamValue.java index b5b86237b018d..caa230d06350e 100644 --- a/x-pack/plugin/sql/sql-proto/src/main/java/org/elasticsearch/xpack/sql/proto/SqlTypedParamValue.java +++ b/x-pack/plugin/sql/sql-proto/src/main/java/org/elasticsearch/xpack/sql/proto/SqlTypedParamValue.java @@ -11,7 +11,6 @@ import org.elasticsearch.common.xcontent.ToXContentObject; import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentParser; -import org.elasticsearch.xpack.sql.type.DataType; import java.io.IOException; import java.util.Objects; @@ -25,8 +24,7 @@ public class SqlTypedParamValue implements ToXContentObject { private static final ConstructingObjectParser PARSER = new ConstructingObjectParser<>("params", true, objects -> - new SqlTypedParamValue( - DataType.fromEsType((String) objects[1]), objects[0] + new SqlTypedParamValue((String) objects[1], objects[0] )); private static final ParseField VALUE = new ParseField("value"); @@ -38,17 +36,17 @@ public class SqlTypedParamValue implements ToXContentObject { } public final Object value; - public final DataType dataType; + public final String type; - public SqlTypedParamValue(DataType dataType, Object value) { + public SqlTypedParamValue(String type, Object value) { this.value = value; - this.dataType = dataType; + this.type = type; } @Override public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { builder.startObject(); - builder.field("type", dataType.esType); + builder.field("type", type); builder.field("value", value); builder.endObject(); return builder; @@ -67,17 +65,16 @@ public boolean equals(Object o) { return false; } SqlTypedParamValue that = (SqlTypedParamValue) o; - return Objects.equals(value, that.value) && - dataType == that.dataType; + return Objects.equals(value, that.value) && Objects.equals(type, that.type); } @Override public int hashCode() { - return Objects.hash(value, dataType); + return Objects.hash(value, type); } @Override public String toString() { - return String.valueOf(value) + "[" + dataType + "]"; + return String.valueOf(value) + "[" + type + "]"; } -} +} \ No newline at end of file diff --git a/x-pack/plugin/sql/sql-proto/src/main/java/org/elasticsearch/xpack/sql/proto/StringUtils.java b/x-pack/plugin/sql/sql-proto/src/main/java/org/elasticsearch/xpack/sql/proto/StringUtils.java new file mode 100644 index 0000000000000..344f614f9c266 --- /dev/null +++ b/x-pack/plugin/sql/sql-proto/src/main/java/org/elasticsearch/xpack/sql/proto/StringUtils.java @@ -0,0 +1,76 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +package org.elasticsearch.xpack.sql.proto; + +import java.sql.Timestamp; +import java.time.Duration; +import java.time.Period; +import java.util.Objects; +import java.util.concurrent.TimeUnit; + +public class StringUtils { + + private static final int SECONDS_PER_MINUTE = 60; + private static final int SECONDS_PER_HOUR = SECONDS_PER_MINUTE * 60; + private static final int SECONDS_PER_DAY = SECONDS_PER_HOUR * 24; + + private StringUtils() {} + + public static String toString(Object value) { + if (value == null) { + return "null"; + } + + if (value instanceof Timestamp) { + Timestamp ts = (Timestamp) value; + return ts.toInstant().toString(); + } + + // handle intervals + // YEAR/MONTH/YEAR TO MONTH -> YEAR TO MONTH + if (value instanceof Period) { + // +yyy-mm - 7 chars + StringBuilder sb = new StringBuilder(7); + Period p = (Period) value; + sb.append(p.isNegative() ? "-" : "+"); + sb.append(p.getYears()); + sb.append("-"); + sb.append(p.getMonths()); + return sb.toString(); + } + + // DAY/HOUR/MINUTE/SECOND (and variations) -> DAY_TO_SECOND + if (value instanceof Duration) { + // +ddd hh:mm:ss.mmmmmmmmm - 23 chars + StringBuilder sb = new StringBuilder(23); + Duration d = (Duration) value; + sb.append(d.isNegative() ? "-" : "+"); + + long durationInSec = d.getSeconds(); + + sb.append(durationInSec / SECONDS_PER_DAY); + sb.append(" "); + durationInSec = durationInSec % SECONDS_PER_DAY; + sb.append(indent(durationInSec / SECONDS_PER_HOUR)); + sb.append(":"); + durationInSec = durationInSec % SECONDS_PER_HOUR; + sb.append(indent(durationInSec / SECONDS_PER_MINUTE)); + sb.append(":"); + durationInSec = durationInSec % SECONDS_PER_MINUTE; + sb.append(indent(durationInSec)); + sb.append("."); + sb.append(TimeUnit.NANOSECONDS.toMillis(d.getNano())); + return sb.toString(); + } + + return Objects.toString(value); + } + + private static String indent(long timeUnit) { + return timeUnit < 10 ? "0" + timeUnit : Long.toString(timeUnit); + } +} \ No newline at end of file diff --git a/x-pack/plugin/sql/sql-proto/src/main/java/org/elasticsearch/xpack/sql/proto/type/ExtendedJDBCType.java b/x-pack/plugin/sql/sql-proto/src/main/java/org/elasticsearch/xpack/sql/proto/type/ExtendedJDBCType.java new file mode 100644 index 0000000000000..daa39fcfa2fac --- /dev/null +++ b/x-pack/plugin/sql/sql-proto/src/main/java/org/elasticsearch/xpack/sql/proto/type/ExtendedJDBCType.java @@ -0,0 +1,52 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +package org.elasticsearch.xpack.sql.proto.type; + +import java.sql.JDBCType; +import java.sql.SQLType; +import java.sql.Types; + +/** + * Provides ODBC-based codes for the missing SQL data types from {@link Types}/{@link JDBCType}. + */ +enum ExtendedJDBCType implements SQLType { + + INTERVAL_YEAR(101), + INTERVAL_MONTH(102), + INTERVAL_DAY(103), + INTERVAL_HOUR(104), + INTERVAL_MINUTE(105), + INTERVAL_SECOND(106), + INTERVAL_YEAR_TO_MONTH(107), + INTERVAL_DAY_TO_HOUR(108), + INTERVAL_DAY_TO_MINUTE(109), + INTERVAL_DAY_TO_SECOND(110), + INTERVAL_HOUR_TO_MINUTE(111), + INTERVAL_HOUR_TO_SECOND(112), + INTERVAL_MINUTE_TO_SECOND(113); + + private final Integer type; + + ExtendedJDBCType(Integer type) { + this.type = type; + } + + @Override + public String getName() { + return name(); + } + + @Override + public String getVendor() { + return "org.elasticsearch"; + } + + @Override + public Integer getVendorTypeNumber() { + return type; + } +} diff --git a/x-pack/plugin/sql/sql-proto/src/main/java/org/elasticsearch/xpack/sql/type/DataType.java b/x-pack/plugin/sql/sql-proto/src/main/java/org/elasticsearch/xpack/sql/type/DataType.java deleted file mode 100644 index e07351a877e33..0000000000000 --- a/x-pack/plugin/sql/sql-proto/src/main/java/org/elasticsearch/xpack/sql/type/DataType.java +++ /dev/null @@ -1,249 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ -package org.elasticsearch.xpack.sql.type; - -import java.sql.JDBCType; -import java.sql.SQLType; -import java.sql.Timestamp; -import java.util.Arrays; -import java.util.HashMap; -import java.util.Locale; -import java.util.Map; - -import static java.util.stream.Collectors.toMap; - -/** - * Elasticsearch data types that supported by SQL interface - */ -public enum DataType { - // @formatter:off - // jdbc type, Java Class size, defPrecision, dispSize, int, rat, docvals - NULL( JDBCType.NULL, null, 0, 0, 0), - UNSUPPORTED( JDBCType.OTHER, null, 0, 0, 0), - BOOLEAN( JDBCType.BOOLEAN, Boolean.class, 1, 1, 1), - BYTE( JDBCType.TINYINT, Byte.class, Byte.BYTES, 3, 5, true, false, true), - SHORT( JDBCType.SMALLINT, Short.class, Short.BYTES, 5, 6, true, false, true), - INTEGER( JDBCType.INTEGER, Integer.class, Integer.BYTES, 10, 11, true, false, true), - LONG( JDBCType.BIGINT, Long.class, Long.BYTES, 19, 20, true, false, true), - // 53 bits defaultPrecision ~ 15(15.95) decimal digits (53log10(2)), - DOUBLE( JDBCType.DOUBLE, Double.class, Double.BYTES, 15, 25, false, true, true), - // 24 bits defaultPrecision - 24*log10(2) =~ 7 (7.22) - FLOAT( JDBCType.REAL, Float.class, Float.BYTES, 7, 15, false, true, true), - HALF_FLOAT( JDBCType.FLOAT, Double.class, Double.BYTES, 16, 25, false, true, true), - // precision is based on long - SCALED_FLOAT(JDBCType.FLOAT, Double.class, Double.BYTES, 19, 25, false, true, true), - KEYWORD( JDBCType.VARCHAR, String.class, Integer.MAX_VALUE, 256, 0), - TEXT( JDBCType.VARCHAR, String.class, Integer.MAX_VALUE, Integer.MAX_VALUE, 0, false, false, false), - OBJECT( JDBCType.STRUCT, null, -1, 0, 0, false, false, false), - NESTED( JDBCType.STRUCT, null, -1, 0, 0, false, false, false), - BINARY( JDBCType.VARBINARY, byte[].class, -1, Integer.MAX_VALUE, 0), - // since ODBC and JDBC interpret precision for Date as display size, - // the precision is 23 (number of chars in ISO8601 with millis) + Z (the UTC timezone) - // see https://github.com/elastic/elasticsearch/issues/30386#issuecomment-386807288 - DATE( JDBCType.TIMESTAMP, Timestamp.class, Long.BYTES, 24, 24), - // - // specialized types - // - // IP can be v4 or v6. The latter has 2^128 addresses or 340,282,366,920,938,463,463,374,607,431,768,211,456 - // aka 39 chars - IP( JDBCType.VARCHAR, String.class, 39, 39, 0,false, false, true); - // @formatter:on - - public static final String ODBC_DATATYPE_PREFIX = "SQL_"; - - private static final Map jdbcToEs; - private static final Map odbcToEs; - - static { - jdbcToEs = Arrays.stream(DataType.values()) - .filter(type -> type != TEXT && type != NESTED - && type != SCALED_FLOAT && type != IP) // Remove duplicates - .collect(toMap(dataType -> dataType.jdbcType, dataType -> dataType)); - - odbcToEs = new HashMap<>(36); - - // Numeric - odbcToEs.put("SQL_BIT", BOOLEAN); - odbcToEs.put("SQL_TINYINT", BYTE); - odbcToEs.put("SQL_SMALLINT", SHORT); - odbcToEs.put("SQL_INTEGER", INTEGER); - odbcToEs.put("SQL_BIGINT", LONG); - odbcToEs.put("SQL_FLOAT", FLOAT); - odbcToEs.put("SQL_REAL", FLOAT); - odbcToEs.put("SQL_DOUBLE", DOUBLE); - odbcToEs.put("SQL_DECIMAL", DOUBLE); - odbcToEs.put("SQL_NUMERIC", DOUBLE); - - // String - odbcToEs.put("SQL_GUID", KEYWORD); - odbcToEs.put("SQL_CHAR", KEYWORD); - odbcToEs.put("SQL_WCHAR", KEYWORD); - odbcToEs.put("SQL_VARCHAR", TEXT); - odbcToEs.put("SQL_WVARCHAR", TEXT); - odbcToEs.put("SQL_LONGVARCHAR", TEXT); - odbcToEs.put("SQL_WLONGVARCHAR", TEXT); - - // Binary - odbcToEs.put("SQL_BINARY", BINARY); - odbcToEs.put("SQL_VARBINARY", BINARY); - odbcToEs.put("SQL_LONGVARBINARY", BINARY); - - // Date - odbcToEs.put("SQL_DATE", DATE); - odbcToEs.put("SQL_TIME", DATE); - odbcToEs.put("SQL_TIMESTAMP", DATE); - - // Intervals - Currently Not Supported - odbcToEs.put("SQL_INTERVAL_HOUR_TO_MINUTE", UNSUPPORTED); - odbcToEs.put("SQL_INTERVAL_HOUR_TO_SECOND", UNSUPPORTED); - odbcToEs.put("SQL_INTERVAL_MINUTE_TO_SECOND", UNSUPPORTED); - odbcToEs.put("SQL_INTERVAL_MONTH", UNSUPPORTED); - odbcToEs.put("SQL_INTERVAL_YEAR", UNSUPPORTED); - odbcToEs.put("SQL_INTERVAL_YEAR_TO_MONTH", UNSUPPORTED); - odbcToEs.put("SQL_INTERVAL_DAY", UNSUPPORTED); - odbcToEs.put("SQL_INTERVAL_HOUR", UNSUPPORTED); - odbcToEs.put("SQL_INTERVAL_MINUTE", UNSUPPORTED); - odbcToEs.put("SQL_INTERVAL_SECOND", UNSUPPORTED); - odbcToEs.put("SQL_INTERVAL_DAY_TO_HOUR", UNSUPPORTED); - odbcToEs.put("SQL_INTERVAL_DAY_TO_MINUTE", UNSUPPORTED); - odbcToEs.put("SQL_INTERVAL_DAY_TO_SECOND", UNSUPPORTED); - } - - /** - * Elasticsearch type name - */ - public final String esType; - - /** - * Compatible JDBC type - */ - public final SQLType jdbcType; - - /** - * Size of the type in bytes - *

- * -1 if the size can vary - */ - public final int size; - - /** - * Precision - *

- * Specified column size. For numeric data, this is the maximum precision. For character - * data, this is the length in characters. For datetime datatypes, this is the length in characters of the - * String representation (assuming the maximum allowed defaultPrecision of the fractional milliseconds component). - */ - public final int defaultPrecision; - - - /** - * Display Size - *

- * Normal maximum width in characters. - */ - public final int displaySize; - - /** - * True if the type represents an integer number - */ - public final boolean isInteger; - - /** - * True if the type represents a rational number - */ - public final boolean isRational; - - /** - * True if the type supports doc values by default - */ - public final boolean defaultDocValues; - - private final Class javaClass; - - DataType(SQLType jdbcType, Class javaClass, int size, int defaultPrecision, int displaySize, boolean isInteger, boolean isRational, - boolean defaultDocValues) { - this.esType = name().toLowerCase(Locale.ROOT); - this.javaClass = javaClass; - this.jdbcType = jdbcType; - this.size = size; - this.defaultPrecision = defaultPrecision; - this.displaySize = displaySize; - this.isInteger = isInteger; - this.isRational = isRational; - this.defaultDocValues = defaultDocValues; - } - - DataType(SQLType jdbcType, Class javaClass, int size, int defaultPrecision, int displaySize) { - this(jdbcType, javaClass, size, defaultPrecision, displaySize, false, false, true); - } - - public String sqlName() { - return jdbcType.getName(); - } - - public Class javaClass() { - return javaClass; - } - - public boolean isNumeric() { - return isInteger || isRational; - } - - /** - * Returns true if value is signed, false otherwise (including if the type is not numeric) - */ - public boolean isSigned() { - // For now all numeric values that es supports are signed - return isNumeric(); - } - - public boolean isString() { - return this == KEYWORD || this == TEXT; - } - - public boolean isPrimitive() { - return this != OBJECT && this != NESTED; - } - - public static DataType fromJdbcType(SQLType jdbcType) { - if (jdbcToEs.containsKey(jdbcType) == false) { - throw new IllegalArgumentException("Unsupported JDBC type [" + jdbcType + "]"); - } - return jdbcToEs.get(jdbcType); - } - - public static Class fromJdbcTypeToJava(SQLType jdbcType) { - if (jdbcToEs.containsKey(jdbcType) == false) { - throw new IllegalArgumentException("Unsupported JDBC type [" + jdbcType + "]"); - } - return jdbcToEs.get(jdbcType).javaClass(); - } - - public static DataType fromODBCType(String odbcType) { - return odbcToEs.get(odbcType); - } - /** - * Creates returns DataType enum coresponding to the specified es type - *

- * For any dataType DataType.fromEsType(dataType.esType) == dataType - */ - public static DataType fromEsType(String esType) { - try { - return DataType.valueOf(esType.toUpperCase(Locale.ROOT)); - } catch (IllegalArgumentException ex) { - return DataType.UNSUPPORTED; - } - } - - public boolean isCompatibleWith(DataType other) { - if (this == other) { - return true; - } else return - (this == NULL || other == NULL) || - (isString() && other.isString()) || - (isNumeric() && other.isNumeric()); - } -} diff --git a/x-pack/plugin/sql/src/main/antlr/SqlBase.g4 b/x-pack/plugin/sql/src/main/antlr/SqlBase.g4 index 1b3cbc04307d2..5566f7164bb4a 100644 --- a/x-pack/plugin/sql/src/main/antlr/SqlBase.g4 +++ b/x-pack/plugin/sql/src/main/antlr/SqlBase.g4 @@ -226,8 +226,8 @@ primaryExpression ; castExpression - : castTemplate - | FUNCTION_ESC castTemplate ESC_END + : castTemplate + | FUNCTION_ESC castTemplate ESC_END | convertTemplate | FUNCTION_ESC convertTemplate ESC_END ; @@ -235,7 +235,7 @@ castExpression castTemplate : CAST '(' expression AS dataType ')' ; - + convertTemplate : CONVERT '(' expression ',' dataType ')' ; @@ -265,6 +265,7 @@ functionName constant : NULL #nullLiteral + | interval #intervalLiteral | number #numericLiteral | booleanValue #booleanLiteral | STRING+ #stringLiteral @@ -283,6 +284,19 @@ booleanValue : TRUE | FALSE ; +interval + : INTERVAL sign=(PLUS | MINUS)? (valueNumeric=number | valuePattern=string) leading=intervalField (TO trailing=intervalField)? + ; + +intervalValue + : number + | string + ; + +intervalField + : YEAR | YEARS | MONTH | MONTHS | DAY | DAYS | HOUR | HOURS | MINUTE | MINUTES | SECOND | SECONDS + ; + dataType : identifier #primitiveDataType ; @@ -313,8 +327,8 @@ unquoteIdentifier ; number - : DECIMAL_VALUE #decimalLiteral - | INTEGER_VALUE #integerLiteral + : DECIMAL_VALUE #decimalLiteral + | INTEGER_VALUE #integerLiteral ; string @@ -326,18 +340,22 @@ string nonReserved : ANALYZE | ANALYZED | CATALOGS | COLUMNS - | DEBUG + | DAY | DEBUG | EXECUTABLE | EXPLAIN - | FORMAT | FUNCTIONS - | GRAPHVIZ - | MAPPED + | FIRST | FORMAT | FUNCTIONS + | GRAPHVIZ + | HOUR + | INTERVAL + | LAST | LIMIT + | MAPPED | MINUTE | MONTH | OPTIMIZED | PARSED | PHYSICAL | PLAN | QUERY | RLIKE - | SCHEMAS | SHOW | SYS + | SCHEMAS | SECOND | SHOW | SYS | TABLES | TEXT | TYPE | TYPES | VERIFY + | YEAR ; ALL: 'ALL'; @@ -354,6 +372,8 @@ CATALOG: 'CATALOG'; CATALOGS: 'CATALOGS'; COLUMNS: 'COLUMNS'; CONVERT: 'CONVERT'; +DAY: 'DAY'; +DAYS: 'DAYS'; DEBUG: 'DEBUG'; DESC: 'DESC'; DESCRIBE: 'DESCRIBE'; @@ -372,8 +392,11 @@ FUNCTIONS: 'FUNCTIONS'; GRAPHVIZ: 'GRAPHVIZ'; GROUP: 'GROUP'; HAVING: 'HAVING'; +HOUR: 'HOUR'; +HOURS: 'HOURS'; IN: 'IN'; INNER: 'INNER'; +INTERVAL: 'INTERVAL'; IS: 'IS'; JOIN: 'JOIN'; LAST: 'LAST'; @@ -382,6 +405,10 @@ LIKE: 'LIKE'; LIMIT: 'LIMIT'; MAPPED: 'MAPPED'; MATCH: 'MATCH'; +MINUTE: 'MINUTE'; +MINUTES: 'MINUTES'; +MONTH: 'MONTH'; +MONTHS: 'MONTHS'; NATURAL: 'NATURAL'; NOT: 'NOT'; NULL: 'NULL'; @@ -398,6 +425,8 @@ RIGHT: 'RIGHT'; RLIKE: 'RLIKE'; QUERY: 'QUERY'; SCHEMAS: 'SCHEMAS'; +SECOND: 'SECOND'; +SECONDS: 'SECONDS'; SELECT: 'SELECT'; SHOW: 'SHOW'; SYS: 'SYS'; @@ -405,12 +434,15 @@ TABLE: 'TABLE'; TABLES: 'TABLES'; TEXT: 'TEXT'; TRUE: 'TRUE'; +TO: 'TO'; TYPE: 'TYPE'; TYPES: 'TYPES'; USING: 'USING'; VERIFY: 'VERIFY'; WHERE: 'WHERE'; WITH: 'WITH'; +YEAR: 'YEAR'; +YEARS: 'YEARS'; // Escaped Sequence ESCAPE_ESC: '{ESCAPE'; diff --git a/x-pack/plugin/sql/src/main/antlr/SqlBase.tokens b/x-pack/plugin/sql/src/main/antlr/SqlBase.tokens index b422b510aa9e6..f8232bf886e58 100644 --- a/x-pack/plugin/sql/src/main/antlr/SqlBase.tokens +++ b/x-pack/plugin/sql/src/main/antlr/SqlBase.tokens @@ -16,98 +16,112 @@ CATALOG=15 CATALOGS=16 COLUMNS=17 CONVERT=18 -DEBUG=19 -DESC=20 -DESCRIBE=21 -DISTINCT=22 -ESCAPE=23 -EXECUTABLE=24 -EXISTS=25 -EXPLAIN=26 -EXTRACT=27 -FALSE=28 -FIRST=29 -FORMAT=30 -FROM=31 -FULL=32 -FUNCTIONS=33 -GRAPHVIZ=34 -GROUP=35 -HAVING=36 -IN=37 -INNER=38 -IS=39 -JOIN=40 -LAST=41 -LEFT=42 -LIKE=43 -LIMIT=44 -MAPPED=45 -MATCH=46 -NATURAL=47 -NOT=48 -NULL=49 -NULLS=50 -ON=51 -OPTIMIZED=52 -OR=53 -ORDER=54 -OUTER=55 -PARSED=56 -PHYSICAL=57 -PLAN=58 -RIGHT=59 -RLIKE=60 -QUERY=61 -SCHEMAS=62 -SELECT=63 -SHOW=64 -SYS=65 -TABLE=66 -TABLES=67 -TEXT=68 -TRUE=69 -TYPE=70 -TYPES=71 -USING=72 -VERIFY=73 -WHERE=74 -WITH=75 -ESCAPE_ESC=76 -FUNCTION_ESC=77 -LIMIT_ESC=78 -DATE_ESC=79 -TIME_ESC=80 -TIMESTAMP_ESC=81 -GUID_ESC=82 -ESC_END=83 -EQ=84 -NEQ=85 -LT=86 -LTE=87 -GT=88 -GTE=89 -PLUS=90 -MINUS=91 -ASTERISK=92 -SLASH=93 -PERCENT=94 -CONCAT=95 -DOT=96 -PARAM=97 -STRING=98 -INTEGER_VALUE=99 -DECIMAL_VALUE=100 -IDENTIFIER=101 -DIGIT_IDENTIFIER=102 -TABLE_IDENTIFIER=103 -QUOTED_IDENTIFIER=104 -BACKQUOTED_IDENTIFIER=105 -SIMPLE_COMMENT=106 -BRACKETED_COMMENT=107 -WS=108 -UNRECOGNIZED=109 -DELIMITER=110 +DAY=19 +DAYS=20 +DEBUG=21 +DESC=22 +DESCRIBE=23 +DISTINCT=24 +ESCAPE=25 +EXECUTABLE=26 +EXISTS=27 +EXPLAIN=28 +EXTRACT=29 +FALSE=30 +FIRST=31 +FORMAT=32 +FROM=33 +FULL=34 +FUNCTIONS=35 +GRAPHVIZ=36 +GROUP=37 +HAVING=38 +HOUR=39 +HOURS=40 +IN=41 +INNER=42 +INTERVAL=43 +IS=44 +JOIN=45 +LAST=46 +LEFT=47 +LIKE=48 +LIMIT=49 +MAPPED=50 +MATCH=51 +MINUTE=52 +MINUTES=53 +MONTH=54 +MONTHS=55 +NATURAL=56 +NOT=57 +NULL=58 +NULLS=59 +ON=60 +OPTIMIZED=61 +OR=62 +ORDER=63 +OUTER=64 +PARSED=65 +PHYSICAL=66 +PLAN=67 +RIGHT=68 +RLIKE=69 +QUERY=70 +SCHEMAS=71 +SECOND=72 +SECONDS=73 +SELECT=74 +SHOW=75 +SYS=76 +TABLE=77 +TABLES=78 +TEXT=79 +TRUE=80 +TO=81 +TYPE=82 +TYPES=83 +USING=84 +VERIFY=85 +WHERE=86 +WITH=87 +YEAR=88 +YEARS=89 +ESCAPE_ESC=90 +FUNCTION_ESC=91 +LIMIT_ESC=92 +DATE_ESC=93 +TIME_ESC=94 +TIMESTAMP_ESC=95 +GUID_ESC=96 +ESC_END=97 +EQ=98 +NEQ=99 +LT=100 +LTE=101 +GT=102 +GTE=103 +PLUS=104 +MINUS=105 +ASTERISK=106 +SLASH=107 +PERCENT=108 +CONCAT=109 +DOT=110 +PARAM=111 +STRING=112 +INTEGER_VALUE=113 +DECIMAL_VALUE=114 +IDENTIFIER=115 +DIGIT_IDENTIFIER=116 +TABLE_IDENTIFIER=117 +QUOTED_IDENTIFIER=118 +BACKQUOTED_IDENTIFIER=119 +SIMPLE_COMMENT=120 +BRACKETED_COMMENT=121 +WS=122 +UNRECOGNIZED=123 +DELIMITER=124 '('=1 ')'=2 ','=3 @@ -126,81 +140,95 @@ DELIMITER=110 'CATALOGS'=16 'COLUMNS'=17 'CONVERT'=18 -'DEBUG'=19 -'DESC'=20 -'DESCRIBE'=21 -'DISTINCT'=22 -'ESCAPE'=23 -'EXECUTABLE'=24 -'EXISTS'=25 -'EXPLAIN'=26 -'EXTRACT'=27 -'FALSE'=28 -'FIRST'=29 -'FORMAT'=30 -'FROM'=31 -'FULL'=32 -'FUNCTIONS'=33 -'GRAPHVIZ'=34 -'GROUP'=35 -'HAVING'=36 -'IN'=37 -'INNER'=38 -'IS'=39 -'JOIN'=40 -'LAST'=41 -'LEFT'=42 -'LIKE'=43 -'LIMIT'=44 -'MAPPED'=45 -'MATCH'=46 -'NATURAL'=47 -'NOT'=48 -'NULL'=49 -'NULLS'=50 -'ON'=51 -'OPTIMIZED'=52 -'OR'=53 -'ORDER'=54 -'OUTER'=55 -'PARSED'=56 -'PHYSICAL'=57 -'PLAN'=58 -'RIGHT'=59 -'RLIKE'=60 -'QUERY'=61 -'SCHEMAS'=62 -'SELECT'=63 -'SHOW'=64 -'SYS'=65 -'TABLE'=66 -'TABLES'=67 -'TEXT'=68 -'TRUE'=69 -'TYPE'=70 -'TYPES'=71 -'USING'=72 -'VERIFY'=73 -'WHERE'=74 -'WITH'=75 -'{ESCAPE'=76 -'{FN'=77 -'{LIMIT'=78 -'{D'=79 -'{T'=80 -'{TS'=81 -'{GUID'=82 -'}'=83 -'='=84 -'<'=86 -'<='=87 -'>'=88 -'>='=89 -'+'=90 -'-'=91 -'*'=92 -'/'=93 -'%'=94 -'||'=95 -'.'=96 -'?'=97 +'DAY'=19 +'DAYS'=20 +'DEBUG'=21 +'DESC'=22 +'DESCRIBE'=23 +'DISTINCT'=24 +'ESCAPE'=25 +'EXECUTABLE'=26 +'EXISTS'=27 +'EXPLAIN'=28 +'EXTRACT'=29 +'FALSE'=30 +'FIRST'=31 +'FORMAT'=32 +'FROM'=33 +'FULL'=34 +'FUNCTIONS'=35 +'GRAPHVIZ'=36 +'GROUP'=37 +'HAVING'=38 +'HOUR'=39 +'HOURS'=40 +'IN'=41 +'INNER'=42 +'INTERVAL'=43 +'IS'=44 +'JOIN'=45 +'LAST'=46 +'LEFT'=47 +'LIKE'=48 +'LIMIT'=49 +'MAPPED'=50 +'MATCH'=51 +'MINUTE'=52 +'MINUTES'=53 +'MONTH'=54 +'MONTHS'=55 +'NATURAL'=56 +'NOT'=57 +'NULL'=58 +'NULLS'=59 +'ON'=60 +'OPTIMIZED'=61 +'OR'=62 +'ORDER'=63 +'OUTER'=64 +'PARSED'=65 +'PHYSICAL'=66 +'PLAN'=67 +'RIGHT'=68 +'RLIKE'=69 +'QUERY'=70 +'SCHEMAS'=71 +'SECOND'=72 +'SECONDS'=73 +'SELECT'=74 +'SHOW'=75 +'SYS'=76 +'TABLE'=77 +'TABLES'=78 +'TEXT'=79 +'TRUE'=80 +'TO'=81 +'TYPE'=82 +'TYPES'=83 +'USING'=84 +'VERIFY'=85 +'WHERE'=86 +'WITH'=87 +'YEAR'=88 +'YEARS'=89 +'{ESCAPE'=90 +'{FN'=91 +'{LIMIT'=92 +'{D'=93 +'{T'=94 +'{TS'=95 +'{GUID'=96 +'}'=97 +'='=98 +'<'=100 +'<='=101 +'>'=102 +'>='=103 +'+'=104 +'-'=105 +'*'=106 +'/'=107 +'%'=108 +'||'=109 +'.'=110 +'?'=111 diff --git a/x-pack/plugin/sql/src/main/antlr/SqlBaseLexer.tokens b/x-pack/plugin/sql/src/main/antlr/SqlBaseLexer.tokens index 96ccf27d1f905..c05044ba0b1cc 100644 --- a/x-pack/plugin/sql/src/main/antlr/SqlBaseLexer.tokens +++ b/x-pack/plugin/sql/src/main/antlr/SqlBaseLexer.tokens @@ -16,97 +16,111 @@ CATALOG=15 CATALOGS=16 COLUMNS=17 CONVERT=18 -DEBUG=19 -DESC=20 -DESCRIBE=21 -DISTINCT=22 -ESCAPE=23 -EXECUTABLE=24 -EXISTS=25 -EXPLAIN=26 -EXTRACT=27 -FALSE=28 -FIRST=29 -FORMAT=30 -FROM=31 -FULL=32 -FUNCTIONS=33 -GRAPHVIZ=34 -GROUP=35 -HAVING=36 -IN=37 -INNER=38 -IS=39 -JOIN=40 -LAST=41 -LEFT=42 -LIKE=43 -LIMIT=44 -MAPPED=45 -MATCH=46 -NATURAL=47 -NOT=48 -NULL=49 -NULLS=50 -ON=51 -OPTIMIZED=52 -OR=53 -ORDER=54 -OUTER=55 -PARSED=56 -PHYSICAL=57 -PLAN=58 -RIGHT=59 -RLIKE=60 -QUERY=61 -SCHEMAS=62 -SELECT=63 -SHOW=64 -SYS=65 -TABLE=66 -TABLES=67 -TEXT=68 -TRUE=69 -TYPE=70 -TYPES=71 -USING=72 -VERIFY=73 -WHERE=74 -WITH=75 -ESCAPE_ESC=76 -FUNCTION_ESC=77 -LIMIT_ESC=78 -DATE_ESC=79 -TIME_ESC=80 -TIMESTAMP_ESC=81 -GUID_ESC=82 -ESC_END=83 -EQ=84 -NEQ=85 -LT=86 -LTE=87 -GT=88 -GTE=89 -PLUS=90 -MINUS=91 -ASTERISK=92 -SLASH=93 -PERCENT=94 -CONCAT=95 -DOT=96 -PARAM=97 -STRING=98 -INTEGER_VALUE=99 -DECIMAL_VALUE=100 -IDENTIFIER=101 -DIGIT_IDENTIFIER=102 -TABLE_IDENTIFIER=103 -QUOTED_IDENTIFIER=104 -BACKQUOTED_IDENTIFIER=105 -SIMPLE_COMMENT=106 -BRACKETED_COMMENT=107 -WS=108 -UNRECOGNIZED=109 +DAY=19 +DAYS=20 +DEBUG=21 +DESC=22 +DESCRIBE=23 +DISTINCT=24 +ESCAPE=25 +EXECUTABLE=26 +EXISTS=27 +EXPLAIN=28 +EXTRACT=29 +FALSE=30 +FIRST=31 +FORMAT=32 +FROM=33 +FULL=34 +FUNCTIONS=35 +GRAPHVIZ=36 +GROUP=37 +HAVING=38 +HOUR=39 +HOURS=40 +IN=41 +INNER=42 +INTERVAL=43 +IS=44 +JOIN=45 +LAST=46 +LEFT=47 +LIKE=48 +LIMIT=49 +MAPPED=50 +MATCH=51 +MINUTE=52 +MINUTES=53 +MONTH=54 +MONTHS=55 +NATURAL=56 +NOT=57 +NULL=58 +NULLS=59 +ON=60 +OPTIMIZED=61 +OR=62 +ORDER=63 +OUTER=64 +PARSED=65 +PHYSICAL=66 +PLAN=67 +RIGHT=68 +RLIKE=69 +QUERY=70 +SCHEMAS=71 +SECOND=72 +SECONDS=73 +SELECT=74 +SHOW=75 +SYS=76 +TABLE=77 +TABLES=78 +TEXT=79 +TRUE=80 +TO=81 +TYPE=82 +TYPES=83 +USING=84 +VERIFY=85 +WHERE=86 +WITH=87 +YEAR=88 +YEARS=89 +ESCAPE_ESC=90 +FUNCTION_ESC=91 +LIMIT_ESC=92 +DATE_ESC=93 +TIME_ESC=94 +TIMESTAMP_ESC=95 +GUID_ESC=96 +ESC_END=97 +EQ=98 +NEQ=99 +LT=100 +LTE=101 +GT=102 +GTE=103 +PLUS=104 +MINUS=105 +ASTERISK=106 +SLASH=107 +PERCENT=108 +CONCAT=109 +DOT=110 +PARAM=111 +STRING=112 +INTEGER_VALUE=113 +DECIMAL_VALUE=114 +IDENTIFIER=115 +DIGIT_IDENTIFIER=116 +TABLE_IDENTIFIER=117 +QUOTED_IDENTIFIER=118 +BACKQUOTED_IDENTIFIER=119 +SIMPLE_COMMENT=120 +BRACKETED_COMMENT=121 +WS=122 +UNRECOGNIZED=123 '('=1 ')'=2 ','=3 @@ -125,81 +139,95 @@ UNRECOGNIZED=109 'CATALOGS'=16 'COLUMNS'=17 'CONVERT'=18 -'DEBUG'=19 -'DESC'=20 -'DESCRIBE'=21 -'DISTINCT'=22 -'ESCAPE'=23 -'EXECUTABLE'=24 -'EXISTS'=25 -'EXPLAIN'=26 -'EXTRACT'=27 -'FALSE'=28 -'FIRST'=29 -'FORMAT'=30 -'FROM'=31 -'FULL'=32 -'FUNCTIONS'=33 -'GRAPHVIZ'=34 -'GROUP'=35 -'HAVING'=36 -'IN'=37 -'INNER'=38 -'IS'=39 -'JOIN'=40 -'LAST'=41 -'LEFT'=42 -'LIKE'=43 -'LIMIT'=44 -'MAPPED'=45 -'MATCH'=46 -'NATURAL'=47 -'NOT'=48 -'NULL'=49 -'NULLS'=50 -'ON'=51 -'OPTIMIZED'=52 -'OR'=53 -'ORDER'=54 -'OUTER'=55 -'PARSED'=56 -'PHYSICAL'=57 -'PLAN'=58 -'RIGHT'=59 -'RLIKE'=60 -'QUERY'=61 -'SCHEMAS'=62 -'SELECT'=63 -'SHOW'=64 -'SYS'=65 -'TABLE'=66 -'TABLES'=67 -'TEXT'=68 -'TRUE'=69 -'TYPE'=70 -'TYPES'=71 -'USING'=72 -'VERIFY'=73 -'WHERE'=74 -'WITH'=75 -'{ESCAPE'=76 -'{FN'=77 -'{LIMIT'=78 -'{D'=79 -'{T'=80 -'{TS'=81 -'{GUID'=82 -'}'=83 -'='=84 -'<'=86 -'<='=87 -'>'=88 -'>='=89 -'+'=90 -'-'=91 -'*'=92 -'/'=93 -'%'=94 -'||'=95 -'.'=96 -'?'=97 +'DAY'=19 +'DAYS'=20 +'DEBUG'=21 +'DESC'=22 +'DESCRIBE'=23 +'DISTINCT'=24 +'ESCAPE'=25 +'EXECUTABLE'=26 +'EXISTS'=27 +'EXPLAIN'=28 +'EXTRACT'=29 +'FALSE'=30 +'FIRST'=31 +'FORMAT'=32 +'FROM'=33 +'FULL'=34 +'FUNCTIONS'=35 +'GRAPHVIZ'=36 +'GROUP'=37 +'HAVING'=38 +'HOUR'=39 +'HOURS'=40 +'IN'=41 +'INNER'=42 +'INTERVAL'=43 +'IS'=44 +'JOIN'=45 +'LAST'=46 +'LEFT'=47 +'LIKE'=48 +'LIMIT'=49 +'MAPPED'=50 +'MATCH'=51 +'MINUTE'=52 +'MINUTES'=53 +'MONTH'=54 +'MONTHS'=55 +'NATURAL'=56 +'NOT'=57 +'NULL'=58 +'NULLS'=59 +'ON'=60 +'OPTIMIZED'=61 +'OR'=62 +'ORDER'=63 +'OUTER'=64 +'PARSED'=65 +'PHYSICAL'=66 +'PLAN'=67 +'RIGHT'=68 +'RLIKE'=69 +'QUERY'=70 +'SCHEMAS'=71 +'SECOND'=72 +'SECONDS'=73 +'SELECT'=74 +'SHOW'=75 +'SYS'=76 +'TABLE'=77 +'TABLES'=78 +'TEXT'=79 +'TRUE'=80 +'TO'=81 +'TYPE'=82 +'TYPES'=83 +'USING'=84 +'VERIFY'=85 +'WHERE'=86 +'WITH'=87 +'YEAR'=88 +'YEARS'=89 +'{ESCAPE'=90 +'{FN'=91 +'{LIMIT'=92 +'{D'=93 +'{T'=94 +'{TS'=95 +'{GUID'=96 +'}'=97 +'='=98 +'<'=100 +'<='=101 +'>'=102 +'>='=103 +'+'=104 +'-'=105 +'*'=106 +'/'=107 +'%'=108 +'||'=109 +'.'=110 +'?'=111 diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/analysis/analyzer/Analyzer.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/analysis/analyzer/Analyzer.java index b376e38e40bc0..6ff87d4ee4677 100644 --- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/analysis/analyzer/Analyzer.java +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/analysis/analyzer/Analyzer.java @@ -558,7 +558,7 @@ protected LogicalPlan rule(LogicalPlan plan) { private Integer findOrdinal(Expression expression) { if (expression instanceof Literal) { Literal l = (Literal) expression; - if (l.dataType().isInteger) { + if (l.dataType().isInteger()) { Object v = l.value(); if (v instanceof Number) { return Integer.valueOf(((Number) v).intValue()); diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/analysis/index/IndexResolver.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/analysis/index/IndexResolver.java index 574106f07cae4..b22dea680639e 100644 --- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/analysis/index/IndexResolver.java +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/analysis/index/IndexResolver.java @@ -344,7 +344,7 @@ private static EsField createField(String fieldName, FieldCapabilities caps, Map EsField field = null; Map props = hasChildren ? new TreeMap<>() : emptyMap(); - DataType esType = DataType.fromEsType(caps.getType()); + DataType esType = DataType.fromTypeName(caps.getType()); switch (esType) { case TEXT: field = new TextEsField(fieldName, props, false); diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/execution/search/extractor/FieldHitExtractor.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/execution/search/extractor/FieldHitExtractor.java index 66e177530547f..e52cbe979f4ed 100644 --- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/execution/search/extractor/FieldHitExtractor.java +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/execution/search/extractor/FieldHitExtractor.java @@ -5,7 +5,6 @@ */ package org.elasticsearch.xpack.sql.execution.search.extractor; -import org.elasticsearch.Version; import org.elasticsearch.common.Strings; import org.elasticsearch.common.document.DocumentField; import org.elasticsearch.common.io.stream.StreamInput; @@ -70,16 +69,8 @@ public FieldHitExtractor(String name, DataType dataType, boolean useDocValue, St FieldHitExtractor(StreamInput in) throws IOException { fieldName = in.readString(); - if (in.getVersion().onOrAfter(Version.V_6_4_0)) { - String esType = in.readOptionalString(); - if (esType != null) { - dataType = DataType.fromEsType(esType); - } else { - dataType = null; - } - } else { - dataType = null; - } + String esType = in.readOptionalString(); + dataType = esType != null ? DataType.fromTypeName(esType) : null; useDocValue = in.readBoolean(); hitName = in.readOptionalString(); path = sourcePath(fieldName, useDocValue, hitName); @@ -93,9 +84,7 @@ public String getWriteableName() { @Override public void writeTo(StreamOutput out) throws IOException { out.writeString(fieldName); - if (out.getVersion().onOrAfter(Version.V_6_4_0)) { - out.writeOptionalString(dataType == null ? null : dataType.esType); - } + out.writeOptionalString(dataType == null ? null : dataType.esType); out.writeBoolean(useDocValue); out.writeOptionalString(hitName); } diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/Expressions.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/Expressions.java index 465726962bc6a..aa6a2b0e89a7e 100644 --- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/Expressions.java +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/Expressions.java @@ -155,7 +155,7 @@ public static TypeResolution typeMustBeBoolean(Expression e, String operationNam } public static TypeResolution typeMustBeInteger(Expression e, String operationName, ParamOrdinal paramOrd) { - return typeMustBe(e, dt -> dt.isInteger, operationName, paramOrd, "integer"); + return typeMustBe(e, DataType::isInteger, operationName, paramOrd, "integer"); } public static TypeResolution typeMustBeNumeric(Expression e, String operationName, ParamOrdinal paramOrd) { diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/Literal.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/Literal.java index 7148a08a3facd..09a8bd08917c9 100644 --- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/Literal.java +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/Literal.java @@ -42,7 +42,7 @@ public Literal(Location location, String name, Object value, DataType dataType) } @Override - protected NodeInfo info() { + protected NodeInfo info() { return NodeInfo.create(this, Literal::new, value, dataType); } diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/literal/Interval.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/literal/Interval.java new file mode 100644 index 0000000000000..da505fe72cdd4 --- /dev/null +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/literal/Interval.java @@ -0,0 +1,74 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +package org.elasticsearch.xpack.sql.expression.literal; + +import org.elasticsearch.common.xcontent.ToXContentObject; +import org.elasticsearch.common.xcontent.XContentBuilder; +import org.elasticsearch.xpack.sql.type.DataType; + +import java.io.IOException; +import java.time.temporal.TemporalAmount; +import java.util.Objects; + +/** + * Interval value. + * + * As SQL defines two main types, YearMonth and DayToHour/Minute/Second, the interval has to be split accordingly + * mainly to differentiate between a period (which is relative) for the former and duration (which is exact) + * for the latter. + * Unfortunately because the SQL interval type is not preserved accurately by the JDK TemporalAmount class + * in both cases, the data type needs to be carried around as it cannot be inferred. + */ +public abstract class Interval implements ToXContentObject { + + private final I interval; + private final DataType intervalType; + + public Interval(I interval, DataType intervalType) { + this.interval = interval; + this.intervalType = intervalType; + } + + public I interval() { + return interval; + } + + public DataType dataType() { + return intervalType; + } + + @Override + public int hashCode() { + return Objects.hash(interval, intervalType); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + + if (obj == null || getClass() != obj.getClass()) { + return false; + } + + Interval other = (Interval) obj; + return Objects.equals(other.interval, interval) + && Objects.equals(other.intervalType, intervalType); + } + + @Override + public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { + builder.value(interval); + return builder; + } + + @Override + public String toString() { + return intervalType.name() + "[" + interval + "]"; + } +} \ No newline at end of file diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/literal/IntervalDayTime.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/literal/IntervalDayTime.java new file mode 100644 index 0000000000000..dd177fff090c8 --- /dev/null +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/literal/IntervalDayTime.java @@ -0,0 +1,21 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +package org.elasticsearch.xpack.sql.expression.literal; + +import org.elasticsearch.xpack.sql.type.DataType; + +import java.time.Duration; + +/** + * Day/Hour/Minutes/Seconds (exact) interval. + */ +public class IntervalDayTime extends Interval { + + public IntervalDayTime(Duration interval, DataType intervalType) { + super(interval, intervalType); + } +} diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/literal/IntervalUtils.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/literal/IntervalUtils.java new file mode 100644 index 0000000000000..c8604d7714615 --- /dev/null +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/literal/IntervalUtils.java @@ -0,0 +1,372 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +package org.elasticsearch.xpack.sql.expression.literal; + +import org.elasticsearch.xpack.sql.SqlIllegalArgumentException; +import org.elasticsearch.xpack.sql.parser.ParsingException; +import org.elasticsearch.xpack.sql.tree.Location; +import org.elasticsearch.xpack.sql.type.DataType; +import org.elasticsearch.xpack.sql.util.StringUtils; + +import java.time.Duration; +import java.time.Period; +import java.time.temporal.TemporalAmount; +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import static org.elasticsearch.xpack.sql.type.DataType.INTERVAL_DAY_TO_HOUR; +import static org.elasticsearch.xpack.sql.type.DataType.INTERVAL_DAY_TO_MINUTE; +import static org.elasticsearch.xpack.sql.type.DataType.INTERVAL_DAY_TO_SECOND; +import static org.elasticsearch.xpack.sql.type.DataType.INTERVAL_HOUR_TO_MINUTE; +import static org.elasticsearch.xpack.sql.type.DataType.INTERVAL_HOUR_TO_SECOND; +import static org.elasticsearch.xpack.sql.type.DataType.INTERVAL_MINUTE_TO_SECOND; +import static org.elasticsearch.xpack.sql.type.DataType.INTERVAL_YEAR_TO_MONTH; + +public final class IntervalUtils { + + /** + * Time unit definition - used to remember the initial declaration + * for exposing it in ODBC. + */ + public enum TimeUnit { + YEAR, MONTH, DAY, HOUR, MINUTE, SECOND, MILLISECOND; + } + + private IntervalUtils() {} + + public static TemporalAmount of(Location source, long duration, TimeUnit unit) { + // Cannot use Period.of since it accepts int so use plus which accepts long + // Further more Period and Duration have inconsistent addition methods but plus is there + try { + switch (unit) { + case YEAR: + return Period.ZERO.plusYears(duration); + case MONTH: + return Period.ZERO.plusMonths(duration); + case DAY: + return Duration.ZERO.plusDays(duration); + case HOUR: + return Duration.ZERO.plusHours(duration); + case MINUTE: + return Duration.ZERO.plusMinutes(duration); + case SECOND: + return Duration.ZERO.plusSeconds(duration); + case MILLISECOND: + return Duration.ZERO.plusMillis(duration); + default: + throw new ParsingException(source, "Cannot parse duration [{}]", unit); + } + } catch (ArithmeticException ae) { + throw new ParsingException(source, "Value [{}] cannot be used as it is too large to convert into " + unit.name() + "s"); + } + } + + public static DataType intervalType(Location source, TimeUnit leading, TimeUnit trailing) { + if (trailing == null) { + switch (leading) { + case YEAR: + return DataType.INTERVAL_YEAR; + case MONTH: + return DataType.INTERVAL_MONTH; + case DAY: + return DataType.INTERVAL_DAY; + case HOUR: + return DataType.INTERVAL_HOUR; + case MINUTE: + return DataType.INTERVAL_MINUTE; + case SECOND: + return DataType.INTERVAL_SECOND; + default: + throw new ParsingException(source, "Cannot determine datatype for [{}]", leading); + } + } else { + if (leading == TimeUnit.YEAR && trailing == TimeUnit.MONTH) { + return INTERVAL_YEAR_TO_MONTH; + } + if (leading == TimeUnit.DAY && trailing == TimeUnit.HOUR) { + return INTERVAL_DAY_TO_HOUR; + } + if (leading == TimeUnit.DAY && trailing == TimeUnit.MINUTE) { + return INTERVAL_DAY_TO_MINUTE; + } + if (leading == TimeUnit.DAY && trailing == TimeUnit.SECOND) { + return INTERVAL_DAY_TO_SECOND; + } + if (leading == TimeUnit.HOUR && trailing == TimeUnit.MINUTE) { + return INTERVAL_HOUR_TO_MINUTE; + } + if (leading == TimeUnit.HOUR && trailing == TimeUnit.SECOND) { + return INTERVAL_HOUR_TO_SECOND; + } + if (leading == TimeUnit.MINUTE && trailing == TimeUnit.SECOND) { + return INTERVAL_MINUTE_TO_SECOND; + } + throw new ParsingException(source, "Cannot determine datatype for combination [{}] [{}]", leading, trailing); + } + } + + // + // String parsers + // + // For consistency and validation, each pattern has its own parser + + private static class ParserBuilder { + + private final List units; + private final List tokens; + private final String name; + + ParserBuilder(DataType dataType) { + units = new ArrayList<>(10); + tokens = new ArrayList<>(6); + name = dataType.name().replace('_', ' '); + } + + ParserBuilder unit(TimeUnit unit) { + unit(unit, 0); + return this; + } + + ParserBuilder unit(TimeUnit unit, int maxValue) { + units.add(unit); + tokens.add(new Token((char) 0, maxValue)); + return this; + } + + ParserBuilder separator(char ch) { + tokens.add(new Token(ch, 0)); + return this; + } + + Parser build() { + return new Parser(units, tokens, name); + } + } + + private static class Token { + private final char ch; + private final int maxValue; + + Token(char ch, int maxValue) { + this.ch = ch; + this.maxValue = maxValue; + } + + @Override + public String toString() { + return ch > 0 ? String.valueOf(ch) : "[numeric" + (maxValue > 0 ? " < " + maxValue + " " : "") + "]"; + } + } + + private static class Parser { + private static final char PLUS = '+', MINUS = '-'; + + private final List units; + private final List tokens; + private final String name; + + Parser(List units, List tokens, String name) { + this.units = units; + this.tokens = tokens; + this.name = name; + } + + TemporalAmount parse(Location source, String string) { + int unitIndex = 0; + int startToken = 0; + int endToken = 0; + + long[] values = new long[units.size()]; + + boolean negate = false; + + // first take check if there's a sign + char maybeSign = string.charAt(0); + if (PLUS == maybeSign) { + startToken = 1; + } else if (MINUS == maybeSign) { + startToken = 1; + negate = true; + } + + // take each token and use it consume a part of the string + // validate each token and that the whole string is consumed + for (Token token : tokens) { + endToken = startToken; + + // char token + if (token.ch != 0) { + char found = string.charAt(startToken); + if (found != token.ch) { + throw new ParsingException(source, invalidIntervalMessage(string) + ": expected [{}] (at [{}]) but found [{}]", + token.ch, startToken, found); + } + startToken++; + } + // number char + else { + // go through the group the digits + for (; endToken < string.length() && Character.isDigit(string.charAt(endToken)); endToken++) { + } + + if (endToken == startToken) { + throw new ParsingException(source, + invalidIntervalMessage(string) + ": expected digit (at [{}]) but found [{}]", + endToken, string.charAt(endToken)); + } + + String number = string.substring(startToken, endToken); + try { + long v = StringUtils.parseLong(number); + if (token.maxValue > 0 && v > token.maxValue) { + throw new ParsingException(source, + invalidIntervalMessage(string) + + ": [{}] unit has illegal value [{}], expected a positive number up to [{}]", + units.get(unitIndex).name(), v, token.maxValue); + } + if (v < 0) { + throw new ParsingException(source, + invalidIntervalMessage(string) + + ": negative value [{}] not allowed (negate the entire interval instead)", + v); + } + values[unitIndex++] = v; + } catch (SqlIllegalArgumentException siae) { + throw new ParsingException(source, invalidIntervalMessage(string), siae.getMessage()); + } + startToken = endToken; + } + } + + if (endToken <= string.length() - 1) { + throw new ParsingException(source, invalidIntervalMessage(string) + ": unexpected trailing characters found [{}]", + string.substring(endToken)); + } + + TemporalAmount interval = units.get(0) == TimeUnit.YEAR || units.get(0) == TimeUnit.MONTH ? Period.ZERO : Duration.ZERO; + + for (int i = 0; i < values.length; i++) { + TemporalAmount ta = of(source, values[i], units.get(i)); + interval = ta instanceof Period ? ((Period) ta).plus(interval) : ((Duration) ta).plus((Duration) interval); + } + + if (negate) { + interval = negate(interval); + } + + return interval; + } + + private String invalidIntervalMessage(String interval) { + return "Invalid [" + name + "] value [" + interval + "]"; + } + + @Override + public String toString() { + return name; + } + } + + public static TemporalAmount negate(TemporalAmount interval) { + // negated is not present on TemporalAmount though present in both Period and Duration so handle each class individually + return interval instanceof Period ? ((Period) interval).negated() : ((Duration) interval).negated(); + } + + static final Map PARSERS = new LinkedHashMap<>(); + + // + // Used the syntax described at the links below + // + // https://docs.microsoft.com/en-us/sql/odbc/reference/appendixes/interval-literal-syntax?view=sql-server-2017 + // https://docs.microsoft.com/en-us/sql/odbc/reference/appendixes/interval-literals?view=sql-server-2017 + // https://docs.oracle.com/cd/B19306_01/server.102/b14200/sql_elements003.htm#i38598 + + static { + int MAX_MONTH = 11; + int MAX_HOUR = 23; + int MAX_MINUTE = 59; + int MAX_SECOND = 59; + int MAX_MILLI = 999999999; + + char DOT = '.'; + char SPACE = ' '; + char MINUS = '-'; + char COLON = ':'; + + PARSERS.put(DataType.INTERVAL_YEAR, new ParserBuilder(DataType.INTERVAL_YEAR).unit(TimeUnit.YEAR).build()); + PARSERS.put(DataType.INTERVAL_MONTH, new ParserBuilder(DataType.INTERVAL_MONTH).unit(TimeUnit.MONTH).build()); + PARSERS.put(DataType.INTERVAL_DAY, new ParserBuilder(DataType.INTERVAL_DAY).unit(TimeUnit.DAY).build()); + PARSERS.put(DataType.INTERVAL_HOUR, new ParserBuilder(DataType.INTERVAL_HOUR).unit(TimeUnit.HOUR).build()); + PARSERS.put(DataType.INTERVAL_MINUTE, new ParserBuilder(DataType.INTERVAL_MINUTE).unit(TimeUnit.MINUTE).build()); + PARSERS.put(DataType.INTERVAL_SECOND, new ParserBuilder(DataType.INTERVAL_SECOND) + .unit(TimeUnit.SECOND) + .separator(DOT) + .unit(TimeUnit.MILLISECOND, MAX_MILLI) + .build()); + + // patterns + PARSERS.put(DataType.INTERVAL_YEAR_TO_MONTH, new ParserBuilder(DataType.INTERVAL_YEAR_TO_MONTH) + .unit(TimeUnit.YEAR) + .separator(MINUS) + .unit(TimeUnit.MONTH, MAX_MONTH) + .build()); + + PARSERS.put(DataType.INTERVAL_DAY_TO_HOUR, new ParserBuilder(DataType.INTERVAL_DAY_TO_HOUR) + .unit(TimeUnit.DAY) + .separator(SPACE) + .unit(TimeUnit.HOUR, MAX_HOUR) + .build()); + + PARSERS.put(DataType.INTERVAL_DAY_TO_MINUTE, new ParserBuilder(DataType.INTERVAL_DAY_TO_MINUTE) + .unit(TimeUnit.DAY) + .separator(SPACE) + .unit(TimeUnit.HOUR, MAX_HOUR) + .separator(COLON) + .unit(TimeUnit.MINUTE, MAX_MINUTE) + .build()); + + PARSERS.put(DataType.INTERVAL_DAY_TO_SECOND, new ParserBuilder(DataType.INTERVAL_DAY_TO_SECOND) + .unit(TimeUnit.DAY) + .separator(SPACE) + .unit(TimeUnit.HOUR, MAX_HOUR) + .separator(COLON) + .unit(TimeUnit.MINUTE, MAX_MINUTE) + .separator(COLON) + .unit(TimeUnit.SECOND, MAX_SECOND) + .separator(DOT).unit(TimeUnit.MILLISECOND, MAX_MILLI) + .build()); + + PARSERS.put(DataType.INTERVAL_HOUR_TO_MINUTE, new ParserBuilder(DataType.INTERVAL_HOUR_TO_MINUTE) + .unit(TimeUnit.HOUR) + .separator(COLON) + .unit(TimeUnit.MINUTE, MAX_MINUTE) + .build()); + + PARSERS.put(DataType.INTERVAL_HOUR_TO_SECOND, new ParserBuilder(DataType.INTERVAL_HOUR_TO_SECOND) + .unit(TimeUnit.HOUR) + .separator(COLON) + .unit(TimeUnit.MINUTE, MAX_MINUTE) + .separator(COLON) + .unit(TimeUnit.SECOND, MAX_SECOND) + .separator(DOT).unit(TimeUnit.MILLISECOND, MAX_MILLI) + .build()); + + PARSERS.put(DataType.INTERVAL_MINUTE_TO_SECOND, new ParserBuilder(DataType.INTERVAL_MINUTE_TO_SECOND) + .unit(TimeUnit.MINUTE) + .separator(COLON) + .unit(TimeUnit.SECOND, MAX_SECOND) + .separator(DOT) + .unit(TimeUnit.MILLISECOND, MAX_MILLI) + .build()); + } + + public static TemporalAmount parseInterval(Location source, String value, DataType intervalType) { + return PARSERS.get(intervalType).parse(source, value); + } +} \ No newline at end of file diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/literal/IntervalYearMonth.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/literal/IntervalYearMonth.java new file mode 100644 index 0000000000000..2eb784d253f1d --- /dev/null +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/literal/IntervalYearMonth.java @@ -0,0 +1,21 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +package org.elasticsearch.xpack.sql.expression.literal; + +import org.elasticsearch.xpack.sql.type.DataType; + +import java.time.Period; + +/** + * Year/Month (relative) interval. + */ +public class IntervalYearMonth extends Interval { + + public IntervalYearMonth(Period interval, DataType intervalType) { + super(interval, intervalType); + } +} diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/parser/ExpressionBuilder.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/parser/ExpressionBuilder.java index fc3d023c9b492..8be5284955fe7 100644 --- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/parser/ExpressionBuilder.java +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/parser/ExpressionBuilder.java @@ -24,7 +24,11 @@ import org.elasticsearch.xpack.sql.expression.function.Function; import org.elasticsearch.xpack.sql.expression.function.UnresolvedFunction; import org.elasticsearch.xpack.sql.expression.function.scalar.Cast; -import org.elasticsearch.xpack.sql.expression.predicate.nulls.IsNull; +import org.elasticsearch.xpack.sql.expression.literal.Interval; +import org.elasticsearch.xpack.sql.expression.literal.IntervalDayTime; +import org.elasticsearch.xpack.sql.expression.literal.IntervalUtils; +import org.elasticsearch.xpack.sql.expression.literal.IntervalUtils.TimeUnit; +import org.elasticsearch.xpack.sql.expression.literal.IntervalYearMonth; import org.elasticsearch.xpack.sql.expression.predicate.Range; import org.elasticsearch.xpack.sql.expression.predicate.fulltext.MatchQueryPredicate; import org.elasticsearch.xpack.sql.expression.predicate.fulltext.MultiMatchQueryPredicate; @@ -33,6 +37,7 @@ import org.elasticsearch.xpack.sql.expression.predicate.logical.Not; import org.elasticsearch.xpack.sql.expression.predicate.logical.Or; import org.elasticsearch.xpack.sql.expression.predicate.nulls.IsNotNull; +import org.elasticsearch.xpack.sql.expression.predicate.nulls.IsNull; import org.elasticsearch.xpack.sql.expression.predicate.operator.arithmetic.Add; import org.elasticsearch.xpack.sql.expression.predicate.operator.arithmetic.Div; import org.elasticsearch.xpack.sql.expression.predicate.operator.arithmetic.Mod; @@ -66,6 +71,9 @@ import org.elasticsearch.xpack.sql.parser.SqlBaseParser.FunctionTemplateContext; import org.elasticsearch.xpack.sql.parser.SqlBaseParser.GuidEscapedLiteralContext; import org.elasticsearch.xpack.sql.parser.SqlBaseParser.IntegerLiteralContext; +import org.elasticsearch.xpack.sql.parser.SqlBaseParser.IntervalContext; +import org.elasticsearch.xpack.sql.parser.SqlBaseParser.IntervalFieldContext; +import org.elasticsearch.xpack.sql.parser.SqlBaseParser.IntervalLiteralContext; import org.elasticsearch.xpack.sql.parser.SqlBaseParser.LikePatternContext; import org.elasticsearch.xpack.sql.parser.SqlBaseParser.LogicalBinaryContext; import org.elasticsearch.xpack.sql.parser.SqlBaseParser.LogicalNotContext; @@ -73,6 +81,7 @@ import org.elasticsearch.xpack.sql.parser.SqlBaseParser.MatchQueryOptionsContext; import org.elasticsearch.xpack.sql.parser.SqlBaseParser.MultiMatchQueryContext; import org.elasticsearch.xpack.sql.parser.SqlBaseParser.NullLiteralContext; +import org.elasticsearch.xpack.sql.parser.SqlBaseParser.NumberContext; import org.elasticsearch.xpack.sql.parser.SqlBaseParser.OrderByContext; import org.elasticsearch.xpack.sql.parser.SqlBaseParser.ParamLiteralContext; import org.elasticsearch.xpack.sql.parser.SqlBaseParser.ParenthesizedExpressionContext; @@ -94,12 +103,16 @@ import org.elasticsearch.xpack.sql.tree.Location; import org.elasticsearch.xpack.sql.type.DataType; import org.elasticsearch.xpack.sql.type.DataTypes; +import org.elasticsearch.xpack.sql.util.StringUtils; import org.joda.time.DateTime; import org.joda.time.format.DateTimeFormatter; import org.joda.time.format.DateTimeFormatterBuilder; import org.joda.time.format.ISODateTimeFormat; -import java.math.BigInteger; +import java.time.Duration; +import java.time.Period; +import java.time.temporal.TemporalAmount; +import java.util.EnumSet; import java.util.List; import java.util.Locale; import java.util.Map; @@ -412,11 +425,11 @@ public Cast visitCastExpression(CastExpressionContext ctx) { ConvertTemplateContext convertTc = ctx.convertTemplate(); String convertDataType = convertTc.dataType().getText().toUpperCase(Locale.ROOT); DataType dataType; - if (convertDataType.startsWith(DataType.ODBC_DATATYPE_PREFIX)) { - dataType = DataType.fromODBCType(convertDataType); + if (convertDataType.startsWith("SQL_")) { + dataType = DataType.fromOdbcType(convertDataType); if (dataType == null) { throw new ParsingException(source(convertTc.dataType()), "Invalid data type [{}] provided", convertDataType); - } + } } else { try { dataType = DataType.valueOf(convertDataType); @@ -493,6 +506,110 @@ public Expression visitNullLiteral(NullLiteralContext ctx) { return new Literal(source(ctx), null, DataType.NULL); } + @Override + public Literal visitIntervalLiteral(IntervalLiteralContext ctx) { + + IntervalContext interval = ctx.interval(); + + TimeUnit leading = visitIntervalField(interval.leading); + TimeUnit trailing = visitIntervalField(interval.trailing); + + // only YEAR TO MONTH or DAY TO HOUR/MINUTE/SECOND are valid declaration + if (trailing != null) { + if (leading == TimeUnit.YEAR && trailing != TimeUnit.MONTH) { + throw new ParsingException(source(interval.trailing), + "Invalid interval declaration; YEAR trailing unit required to be MONTH, received {}", trailing); + } else { + if (trailing.ordinal() <= leading.ordinal()) { + EnumSet range = EnumSet.range(leading, TimeUnit.SECOND); + range.remove(leading); + throw new ParsingException(source(interval.trailing), + "Invalid interval declaration; trailing unit [{}] needs to be smaller than leading unit[{}], " + + "expected one of {}", trailing, leading, range); + } + } + } + + DataType intervalType = IntervalUtils.intervalType(source(interval), leading, trailing); + + boolean negative = interval.sign != null && interval.sign.getType() == SqlBaseParser.MINUS; + + TemporalAmount value = null; + String valueAsText = null; + + if (interval.valueNumeric != null) { + if (trailing != null) { + throw new ParsingException(source(interval.trailing), + "Invalid interval declaration; trailing unit [{}] specified but the value is with numeric (single unit), " + + "use the string notation instead", trailing); + } + value = of(interval.valueNumeric, negative, leading); + valueAsText = interval.valueNumeric.getText(); + } else { + value = visitIntervalValue(interval.valuePattern, negative, intervalType); + valueAsText = interval.valuePattern.getText(); + } + + String name = "INTERVAL " + valueAsText + " " + leading.name() + (trailing != null ? " TO " + trailing.name() : ""); + + Interval timeInterval = value instanceof Period ? new IntervalYearMonth((Period) value, + intervalType) : new IntervalDayTime((Duration) value, intervalType); + + return new Literal(source(ctx), name, timeInterval, intervalType); + } + + private TemporalAmount of(NumberContext valueNumeric, boolean negative, TimeUnit unit) { + // expect numbers for now + Literal value = (Literal) visit(valueNumeric); + Number numeric = (Number) value.fold(); + + if (Math.rint(numeric.doubleValue()) != numeric.longValue()) { + throw new ParsingException(source(valueNumeric), "Fractional values are not supported for intervals"); + } + + return IntervalUtils.of(source(valueNumeric), numeric.longValue(), unit); + } + + private TemporalAmount visitIntervalValue(StringContext valuePattern, boolean negative, DataType intervalType) { + String valueString = string(valuePattern); + Location loc = source(valuePattern); + TemporalAmount interval = IntervalUtils.parseInterval(loc, valueString, intervalType); + if (negative) { + interval = IntervalUtils.negate(interval); + } + return interval; + } + + @Override + public IntervalUtils.TimeUnit visitIntervalField(IntervalFieldContext ctx) { + if (ctx == null) { + return null; + } + + switch (ctx.getChild(TerminalNode.class, 0).getSymbol().getType()) { + case SqlBaseParser.YEAR: + case SqlBaseParser.YEARS: + return IntervalUtils.TimeUnit.YEAR; + case SqlBaseParser.MONTH: + case SqlBaseParser.MONTHS: + return IntervalUtils.TimeUnit.MONTH; + case SqlBaseParser.DAY: + case SqlBaseParser.DAYS: + return IntervalUtils.TimeUnit.DAY; + case SqlBaseParser.HOUR: + case SqlBaseParser.HOURS: + return IntervalUtils.TimeUnit.HOUR; + case SqlBaseParser.MINUTE: + case SqlBaseParser.MINUTES: + return IntervalUtils.TimeUnit.MINUTE; + case SqlBaseParser.SECOND: + case SqlBaseParser.SECONDS: + return IntervalUtils.TimeUnit.SECOND; + } + + throw new IllegalArgumentException("Unsupported interval field: " + ctx.getText()); + } + @Override public Expression visitBooleanLiteral(BooleanLiteralContext ctx) { boolean value; @@ -500,7 +617,7 @@ public Expression visitBooleanLiteral(BooleanLiteralContext ctx) { value = Booleans.parseBoolean(ctx.getText().toLowerCase(Locale.ROOT), false); } catch(IllegalArgumentException iae) { throw new ParsingException(source(ctx), iae.getMessage()); - } + } return new Literal(source(ctx), Boolean.valueOf(value), DataType.BOOLEAN); } @@ -515,40 +632,23 @@ public Expression visitStringLiteral(StringLiteralContext ctx) { @Override public Literal visitDecimalLiteral(DecimalLiteralContext ctx) { - String ctxText = (hasMinusFromParent(ctx) ? "-" : "") + ctx.getText(); - double value; + String string = (hasMinusFromParent(ctx) ? "-" : "") + ctx.getText(); try { - value = Double.parseDouble(ctxText); - } catch (NumberFormatException nfe) { - throw new ParsingException(source(ctx), "Cannot parse number [{}]", ctxText); - } - if (Double.isInfinite(value)) { - throw new ParsingException(source(ctx), "Number [{}] is too large", ctxText); + return new Literal(source(ctx), Double.valueOf(StringUtils.parseDouble(string)), DataType.DOUBLE); + } catch (SqlIllegalArgumentException siae) { + throw new ParsingException(source(ctx), siae.getMessage()); } - if (Double.isNaN(value)) { - throw new ParsingException(source(ctx), "[{}] cannot be parsed as a number (NaN)", ctxText); - } - return new Literal(source(ctx), Double.valueOf(value), DataType.DOUBLE); } @Override public Literal visitIntegerLiteral(IntegerLiteralContext ctx) { - String ctxText = (hasMinusFromParent(ctx) ? "-" : "") + ctx.getText(); + String string = (hasMinusFromParent(ctx) ? "-" : "") + ctx.getText(); + long value; try { - value = Long.parseLong(ctxText); - } catch (NumberFormatException nfe) { - try { - BigInteger bi = new BigInteger(ctxText); - try { - bi.longValueExact(); - } catch (ArithmeticException ae) { - throw new ParsingException(source(ctx), "Number [{}] is too large", ctxText); - } - } catch (NumberFormatException ex) { - // parsing fails, go through - } - throw new ParsingException(source(ctx), "Cannot parse number [{}]", ctxText); + value = Long.valueOf(StringUtils.parseLong(string)); + } catch (SqlIllegalArgumentException siae) { + throw new ParsingException(source(ctx), siae.getMessage()); } DataType type = DataType.LONG; @@ -562,27 +662,28 @@ public Literal visitIntegerLiteral(IntegerLiteralContext ctx) { @Override public Literal visitParamLiteral(ParamLiteralContext ctx) { SqlTypedParamValue param = param(ctx.PARAM()); + DataType dataType = DataType.fromTypeName(param.type); Location loc = source(ctx); if (param.value == null) { // no conversion is required for null values - return new Literal(loc, null, param.dataType); + return new Literal(loc, null, dataType); } final DataType sourceType; try { sourceType = DataTypes.fromJava(param.value); } catch (SqlIllegalArgumentException ex) { throw new ParsingException(ex, loc, "Unexpected actual parameter type [{}] for type [{}]", param.value.getClass().getName(), - param.dataType); + param.type); } - if (sourceType == param.dataType) { + if (sourceType == dataType) { // no conversion is required if the value is already have correct type - return new Literal(loc, param.value, param.dataType); + return new Literal(loc, param.value, dataType); } // otherwise we need to make sure that xcontent-serialized value is converted to the correct type try { - return new Literal(loc, conversionFor(sourceType, param.dataType).convert(param.value), param.dataType); + return new Literal(loc, conversionFor(sourceType, dataType).convert(param.value), dataType); } catch (SqlIllegalArgumentException ex) { - throw new ParsingException(ex, loc, "Unexpected actual parameter type [{}] for type [{}]", sourceType, param.dataType); + throw new ParsingException(ex, loc, "Unexpected actual parameter type [{}] for type [{}]", sourceType, param.type); } } @@ -718,16 +819,21 @@ public Literal visitGuidEscapedLiteral(GuidEscapedLiteralContext ctx) { private boolean hasMinusFromParent(SqlBaseParser.NumberContext ctx) { ParserRuleContext parentCtx = ctx.getParent(); - if (parentCtx != null && parentCtx instanceof SqlBaseParser.NumericLiteralContext) { - parentCtx = parentCtx.getParent(); - if (parentCtx != null && parentCtx instanceof SqlBaseParser.ConstantDefaultContext) { + if (parentCtx != null) { + if (parentCtx instanceof SqlBaseParser.NumericLiteralContext) { parentCtx = parentCtx.getParent(); - if (parentCtx != null && parentCtx instanceof SqlBaseParser.ValueExpressionDefaultContext) { + if (parentCtx != null && parentCtx instanceof SqlBaseParser.ConstantDefaultContext) { parentCtx = parentCtx.getParent(); - if (parentCtx != null && parentCtx instanceof SqlBaseParser.ArithmeticUnaryContext) { - return ((ArithmeticUnaryContext) parentCtx).MINUS() != null; + if (parentCtx != null && parentCtx instanceof SqlBaseParser.ValueExpressionDefaultContext) { + parentCtx = parentCtx.getParent(); + if (parentCtx != null && parentCtx instanceof SqlBaseParser.ArithmeticUnaryContext) { + return ((ArithmeticUnaryContext) parentCtx).MINUS() != null; + } } } + } else if (parentCtx instanceof SqlBaseParser.IntervalContext) { + IntervalContext ic = (IntervalContext) parentCtx; + return ic.sign != null && ic.sign.getType() == SqlBaseParser.MINUS; } } return false; diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/parser/SqlBaseBaseListener.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/parser/SqlBaseBaseListener.java index 380428b8ac922..0177549f1f2e5 100644 --- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/parser/SqlBaseBaseListener.java +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/parser/SqlBaseBaseListener.java @@ -851,6 +851,18 @@ class SqlBaseBaseListener implements SqlBaseListener { *

The default implementation does nothing.

*/ @Override public void exitNullLiteral(SqlBaseParser.NullLiteralContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void enterIntervalLiteral(SqlBaseParser.IntervalLiteralContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void exitIntervalLiteral(SqlBaseParser.IntervalLiteralContext ctx) { } /** * {@inheritDoc} * @@ -971,6 +983,42 @@ class SqlBaseBaseListener implements SqlBaseListener { *

The default implementation does nothing.

*/ @Override public void exitBooleanValue(SqlBaseParser.BooleanValueContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void enterInterval(SqlBaseParser.IntervalContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void exitInterval(SqlBaseParser.IntervalContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void enterIntervalValue(SqlBaseParser.IntervalValueContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void exitIntervalValue(SqlBaseParser.IntervalValueContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void enterIntervalField(SqlBaseParser.IntervalFieldContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void exitIntervalField(SqlBaseParser.IntervalFieldContext ctx) { } /** * {@inheritDoc} * diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/parser/SqlBaseBaseVisitor.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/parser/SqlBaseBaseVisitor.java index 1305d5788b7cb..554fbbf1afa46 100644 --- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/parser/SqlBaseBaseVisitor.java +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/parser/SqlBaseBaseVisitor.java @@ -501,6 +501,13 @@ class SqlBaseBaseVisitor extends AbstractParseTreeVisitor implements SqlBa * {@link #visitChildren} on {@code ctx}.

*/ @Override public T visitNullLiteral(SqlBaseParser.NullLiteralContext ctx) { return visitChildren(ctx); } + /** + * {@inheritDoc} + * + *

The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.

+ */ + @Override public T visitIntervalLiteral(SqlBaseParser.IntervalLiteralContext ctx) { return visitChildren(ctx); } /** * {@inheritDoc} * @@ -571,6 +578,27 @@ class SqlBaseBaseVisitor extends AbstractParseTreeVisitor implements SqlBa * {@link #visitChildren} on {@code ctx}.

*/ @Override public T visitBooleanValue(SqlBaseParser.BooleanValueContext ctx) { return visitChildren(ctx); } + /** + * {@inheritDoc} + * + *

The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.

+ */ + @Override public T visitInterval(SqlBaseParser.IntervalContext ctx) { return visitChildren(ctx); } + /** + * {@inheritDoc} + * + *

The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.

+ */ + @Override public T visitIntervalValue(SqlBaseParser.IntervalValueContext ctx) { return visitChildren(ctx); } + /** + * {@inheritDoc} + * + *

The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.

+ */ + @Override public T visitIntervalField(SqlBaseParser.IntervalFieldContext ctx) { return visitChildren(ctx); } /** * {@inheritDoc} * diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/parser/SqlBaseLexer.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/parser/SqlBaseLexer.java index 07406b4297aad..d35808d1b9374 100644 --- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/parser/SqlBaseLexer.java +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/parser/SqlBaseLexer.java @@ -19,20 +19,23 @@ class SqlBaseLexer extends Lexer { public static final int T__0=1, T__1=2, T__2=3, T__3=4, ALL=5, ANALYZE=6, ANALYZED=7, AND=8, ANY=9, AS=10, ASC=11, BETWEEN=12, BY=13, CAST=14, CATALOG=15, CATALOGS=16, COLUMNS=17, - CONVERT=18, DEBUG=19, DESC=20, DESCRIBE=21, DISTINCT=22, ESCAPE=23, EXECUTABLE=24, - EXISTS=25, EXPLAIN=26, EXTRACT=27, FALSE=28, FIRST=29, FORMAT=30, FROM=31, - FULL=32, FUNCTIONS=33, GRAPHVIZ=34, GROUP=35, HAVING=36, IN=37, INNER=38, - IS=39, JOIN=40, LAST=41, LEFT=42, LIKE=43, LIMIT=44, MAPPED=45, MATCH=46, - NATURAL=47, NOT=48, NULL=49, NULLS=50, ON=51, OPTIMIZED=52, OR=53, ORDER=54, - OUTER=55, PARSED=56, PHYSICAL=57, PLAN=58, RIGHT=59, RLIKE=60, QUERY=61, - SCHEMAS=62, SELECT=63, SHOW=64, SYS=65, TABLE=66, TABLES=67, TEXT=68, - TRUE=69, TYPE=70, TYPES=71, USING=72, VERIFY=73, WHERE=74, WITH=75, ESCAPE_ESC=76, - FUNCTION_ESC=77, LIMIT_ESC=78, DATE_ESC=79, TIME_ESC=80, TIMESTAMP_ESC=81, - GUID_ESC=82, ESC_END=83, EQ=84, NEQ=85, LT=86, LTE=87, GT=88, GTE=89, - PLUS=90, MINUS=91, ASTERISK=92, SLASH=93, PERCENT=94, CONCAT=95, DOT=96, - PARAM=97, STRING=98, INTEGER_VALUE=99, DECIMAL_VALUE=100, IDENTIFIER=101, - DIGIT_IDENTIFIER=102, TABLE_IDENTIFIER=103, QUOTED_IDENTIFIER=104, BACKQUOTED_IDENTIFIER=105, - SIMPLE_COMMENT=106, BRACKETED_COMMENT=107, WS=108, UNRECOGNIZED=109; + CONVERT=18, DAY=19, DAYS=20, DEBUG=21, DESC=22, DESCRIBE=23, DISTINCT=24, + ESCAPE=25, EXECUTABLE=26, EXISTS=27, EXPLAIN=28, EXTRACT=29, FALSE=30, + FIRST=31, FORMAT=32, FROM=33, FULL=34, FUNCTIONS=35, GRAPHVIZ=36, GROUP=37, + HAVING=38, HOUR=39, HOURS=40, IN=41, INNER=42, INTERVAL=43, IS=44, JOIN=45, + LAST=46, LEFT=47, LIKE=48, LIMIT=49, MAPPED=50, MATCH=51, MINUTE=52, MINUTES=53, + MONTH=54, MONTHS=55, NATURAL=56, NOT=57, NULL=58, NULLS=59, ON=60, OPTIMIZED=61, + OR=62, ORDER=63, OUTER=64, PARSED=65, PHYSICAL=66, PLAN=67, RIGHT=68, + RLIKE=69, QUERY=70, SCHEMAS=71, SECOND=72, SECONDS=73, SELECT=74, SHOW=75, + SYS=76, TABLE=77, TABLES=78, TEXT=79, TRUE=80, TO=81, TYPE=82, TYPES=83, + USING=84, VERIFY=85, WHERE=86, WITH=87, YEAR=88, YEARS=89, ESCAPE_ESC=90, + FUNCTION_ESC=91, LIMIT_ESC=92, DATE_ESC=93, TIME_ESC=94, TIMESTAMP_ESC=95, + GUID_ESC=96, ESC_END=97, EQ=98, NEQ=99, LT=100, LTE=101, GT=102, GTE=103, + PLUS=104, MINUS=105, ASTERISK=106, SLASH=107, PERCENT=108, CONCAT=109, + DOT=110, PARAM=111, STRING=112, INTEGER_VALUE=113, DECIMAL_VALUE=114, + IDENTIFIER=115, DIGIT_IDENTIFIER=116, TABLE_IDENTIFIER=117, QUOTED_IDENTIFIER=118, + BACKQUOTED_IDENTIFIER=119, SIMPLE_COMMENT=120, BRACKETED_COMMENT=121, + WS=122, UNRECOGNIZED=123; public static String[] modeNames = { "DEFAULT_MODE" }; @@ -40,54 +43,59 @@ class SqlBaseLexer extends Lexer { public static final String[] ruleNames = { "T__0", "T__1", "T__2", "T__3", "ALL", "ANALYZE", "ANALYZED", "AND", "ANY", "AS", "ASC", "BETWEEN", "BY", "CAST", "CATALOG", "CATALOGS", "COLUMNS", - "CONVERT", "DEBUG", "DESC", "DESCRIBE", "DISTINCT", "ESCAPE", "EXECUTABLE", - "EXISTS", "EXPLAIN", "EXTRACT", "FALSE", "FIRST", "FORMAT", "FROM", "FULL", - "FUNCTIONS", "GRAPHVIZ", "GROUP", "HAVING", "IN", "INNER", "IS", "JOIN", - "LAST", "LEFT", "LIKE", "LIMIT", "MAPPED", "MATCH", "NATURAL", "NOT", - "NULL", "NULLS", "ON", "OPTIMIZED", "OR", "ORDER", "OUTER", "PARSED", - "PHYSICAL", "PLAN", "RIGHT", "RLIKE", "QUERY", "SCHEMAS", "SELECT", "SHOW", - "SYS", "TABLE", "TABLES", "TEXT", "TRUE", "TYPE", "TYPES", "USING", "VERIFY", - "WHERE", "WITH", "ESCAPE_ESC", "FUNCTION_ESC", "LIMIT_ESC", "DATE_ESC", - "TIME_ESC", "TIMESTAMP_ESC", "GUID_ESC", "ESC_END", "EQ", "NEQ", "LT", - "LTE", "GT", "GTE", "PLUS", "MINUS", "ASTERISK", "SLASH", "PERCENT", "CONCAT", - "DOT", "PARAM", "STRING", "INTEGER_VALUE", "DECIMAL_VALUE", "IDENTIFIER", - "DIGIT_IDENTIFIER", "TABLE_IDENTIFIER", "QUOTED_IDENTIFIER", "BACKQUOTED_IDENTIFIER", - "EXPONENT", "DIGIT", "LETTER", "SIMPLE_COMMENT", "BRACKETED_COMMENT", - "WS", "UNRECOGNIZED" + "CONVERT", "DAY", "DAYS", "DEBUG", "DESC", "DESCRIBE", "DISTINCT", "ESCAPE", + "EXECUTABLE", "EXISTS", "EXPLAIN", "EXTRACT", "FALSE", "FIRST", "FORMAT", + "FROM", "FULL", "FUNCTIONS", "GRAPHVIZ", "GROUP", "HAVING", "HOUR", "HOURS", + "IN", "INNER", "INTERVAL", "IS", "JOIN", "LAST", "LEFT", "LIKE", "LIMIT", + "MAPPED", "MATCH", "MINUTE", "MINUTES", "MONTH", "MONTHS", "NATURAL", + "NOT", "NULL", "NULLS", "ON", "OPTIMIZED", "OR", "ORDER", "OUTER", "PARSED", + "PHYSICAL", "PLAN", "RIGHT", "RLIKE", "QUERY", "SCHEMAS", "SECOND", "SECONDS", + "SELECT", "SHOW", "SYS", "TABLE", "TABLES", "TEXT", "TRUE", "TO", "TYPE", + "TYPES", "USING", "VERIFY", "WHERE", "WITH", "YEAR", "YEARS", "ESCAPE_ESC", + "FUNCTION_ESC", "LIMIT_ESC", "DATE_ESC", "TIME_ESC", "TIMESTAMP_ESC", + "GUID_ESC", "ESC_END", "EQ", "NEQ", "LT", "LTE", "GT", "GTE", "PLUS", + "MINUS", "ASTERISK", "SLASH", "PERCENT", "CONCAT", "DOT", "PARAM", "STRING", + "INTEGER_VALUE", "DECIMAL_VALUE", "IDENTIFIER", "DIGIT_IDENTIFIER", "TABLE_IDENTIFIER", + "QUOTED_IDENTIFIER", "BACKQUOTED_IDENTIFIER", "EXPONENT", "DIGIT", "LETTER", + "SIMPLE_COMMENT", "BRACKETED_COMMENT", "WS", "UNRECOGNIZED" }; private static final String[] _LITERAL_NAMES = { null, "'('", "')'", "','", "':'", "'ALL'", "'ANALYZE'", "'ANALYZED'", "'AND'", "'ANY'", "'AS'", "'ASC'", "'BETWEEN'", "'BY'", "'CAST'", "'CATALOG'", - "'CATALOGS'", "'COLUMNS'", "'CONVERT'", "'DEBUG'", "'DESC'", "'DESCRIBE'", - "'DISTINCT'", "'ESCAPE'", "'EXECUTABLE'", "'EXISTS'", "'EXPLAIN'", "'EXTRACT'", - "'FALSE'", "'FIRST'", "'FORMAT'", "'FROM'", "'FULL'", "'FUNCTIONS'", "'GRAPHVIZ'", - "'GROUP'", "'HAVING'", "'IN'", "'INNER'", "'IS'", "'JOIN'", "'LAST'", - "'LEFT'", "'LIKE'", "'LIMIT'", "'MAPPED'", "'MATCH'", "'NATURAL'", "'NOT'", - "'NULL'", "'NULLS'", "'ON'", "'OPTIMIZED'", "'OR'", "'ORDER'", "'OUTER'", - "'PARSED'", "'PHYSICAL'", "'PLAN'", "'RIGHT'", "'RLIKE'", "'QUERY'", "'SCHEMAS'", + "'CATALOGS'", "'COLUMNS'", "'CONVERT'", "'DAY'", "'DAYS'", "'DEBUG'", + "'DESC'", "'DESCRIBE'", "'DISTINCT'", "'ESCAPE'", "'EXECUTABLE'", "'EXISTS'", + "'EXPLAIN'", "'EXTRACT'", "'FALSE'", "'FIRST'", "'FORMAT'", "'FROM'", + "'FULL'", "'FUNCTIONS'", "'GRAPHVIZ'", "'GROUP'", "'HAVING'", "'HOUR'", + "'HOURS'", "'IN'", "'INNER'", "'INTERVAL'", "'IS'", "'JOIN'", "'LAST'", + "'LEFT'", "'LIKE'", "'LIMIT'", "'MAPPED'", "'MATCH'", "'MINUTE'", "'MINUTES'", + "'MONTH'", "'MONTHS'", "'NATURAL'", "'NOT'", "'NULL'", "'NULLS'", "'ON'", + "'OPTIMIZED'", "'OR'", "'ORDER'", "'OUTER'", "'PARSED'", "'PHYSICAL'", + "'PLAN'", "'RIGHT'", "'RLIKE'", "'QUERY'", "'SCHEMAS'", "'SECOND'", "'SECONDS'", "'SELECT'", "'SHOW'", "'SYS'", "'TABLE'", "'TABLES'", "'TEXT'", "'TRUE'", - "'TYPE'", "'TYPES'", "'USING'", "'VERIFY'", "'WHERE'", "'WITH'", "'{ESCAPE'", - "'{FN'", "'{LIMIT'", "'{D'", "'{T'", "'{TS'", "'{GUID'", "'}'", "'='", - null, "'<'", "'<='", "'>'", "'>='", "'+'", "'-'", "'*'", "'/'", "'%'", - "'||'", "'.'", "'?'" + "'TO'", "'TYPE'", "'TYPES'", "'USING'", "'VERIFY'", "'WHERE'", "'WITH'", + "'YEAR'", "'YEARS'", "'{ESCAPE'", "'{FN'", "'{LIMIT'", "'{D'", "'{T'", + "'{TS'", "'{GUID'", "'}'", "'='", null, "'<'", "'<='", "'>'", "'>='", + "'+'", "'-'", "'*'", "'/'", "'%'", "'||'", "'.'", "'?'" }; private static final String[] _SYMBOLIC_NAMES = { null, null, null, null, null, "ALL", "ANALYZE", "ANALYZED", "AND", "ANY", "AS", "ASC", "BETWEEN", "BY", "CAST", "CATALOG", "CATALOGS", "COLUMNS", - "CONVERT", "DEBUG", "DESC", "DESCRIBE", "DISTINCT", "ESCAPE", "EXECUTABLE", - "EXISTS", "EXPLAIN", "EXTRACT", "FALSE", "FIRST", "FORMAT", "FROM", "FULL", - "FUNCTIONS", "GRAPHVIZ", "GROUP", "HAVING", "IN", "INNER", "IS", "JOIN", - "LAST", "LEFT", "LIKE", "LIMIT", "MAPPED", "MATCH", "NATURAL", "NOT", - "NULL", "NULLS", "ON", "OPTIMIZED", "OR", "ORDER", "OUTER", "PARSED", - "PHYSICAL", "PLAN", "RIGHT", "RLIKE", "QUERY", "SCHEMAS", "SELECT", "SHOW", - "SYS", "TABLE", "TABLES", "TEXT", "TRUE", "TYPE", "TYPES", "USING", "VERIFY", - "WHERE", "WITH", "ESCAPE_ESC", "FUNCTION_ESC", "LIMIT_ESC", "DATE_ESC", - "TIME_ESC", "TIMESTAMP_ESC", "GUID_ESC", "ESC_END", "EQ", "NEQ", "LT", - "LTE", "GT", "GTE", "PLUS", "MINUS", "ASTERISK", "SLASH", "PERCENT", "CONCAT", - "DOT", "PARAM", "STRING", "INTEGER_VALUE", "DECIMAL_VALUE", "IDENTIFIER", - "DIGIT_IDENTIFIER", "TABLE_IDENTIFIER", "QUOTED_IDENTIFIER", "BACKQUOTED_IDENTIFIER", - "SIMPLE_COMMENT", "BRACKETED_COMMENT", "WS", "UNRECOGNIZED" + "CONVERT", "DAY", "DAYS", "DEBUG", "DESC", "DESCRIBE", "DISTINCT", "ESCAPE", + "EXECUTABLE", "EXISTS", "EXPLAIN", "EXTRACT", "FALSE", "FIRST", "FORMAT", + "FROM", "FULL", "FUNCTIONS", "GRAPHVIZ", "GROUP", "HAVING", "HOUR", "HOURS", + "IN", "INNER", "INTERVAL", "IS", "JOIN", "LAST", "LEFT", "LIKE", "LIMIT", + "MAPPED", "MATCH", "MINUTE", "MINUTES", "MONTH", "MONTHS", "NATURAL", + "NOT", "NULL", "NULLS", "ON", "OPTIMIZED", "OR", "ORDER", "OUTER", "PARSED", + "PHYSICAL", "PLAN", "RIGHT", "RLIKE", "QUERY", "SCHEMAS", "SECOND", "SECONDS", + "SELECT", "SHOW", "SYS", "TABLE", "TABLES", "TEXT", "TRUE", "TO", "TYPE", + "TYPES", "USING", "VERIFY", "WHERE", "WITH", "YEAR", "YEARS", "ESCAPE_ESC", + "FUNCTION_ESC", "LIMIT_ESC", "DATE_ESC", "TIME_ESC", "TIMESTAMP_ESC", + "GUID_ESC", "ESC_END", "EQ", "NEQ", "LT", "LTE", "GT", "GTE", "PLUS", + "MINUS", "ASTERISK", "SLASH", "PERCENT", "CONCAT", "DOT", "PARAM", "STRING", + "INTEGER_VALUE", "DECIMAL_VALUE", "IDENTIFIER", "DIGIT_IDENTIFIER", "TABLE_IDENTIFIER", + "QUOTED_IDENTIFIER", "BACKQUOTED_IDENTIFIER", "SIMPLE_COMMENT", "BRACKETED_COMMENT", + "WS", "UNRECOGNIZED" }; public static final Vocabulary VOCABULARY = new VocabularyImpl(_LITERAL_NAMES, _SYMBOLIC_NAMES); @@ -144,7 +152,7 @@ public SqlBaseLexer(CharStream input) { public ATN getATN() { return _ATN; } public static final String _serializedATN = - "\3\u0430\ud6d1\u8206\uad2d\u4417\uaef1\u8d80\uaadd\2o\u0390\b\1\4\2\t"+ + "\3\u0430\ud6d1\u8206\uad2d\u4417\uaef1\u8d80\uaadd\2}\u0402\b\1\4\2\t"+ "\2\4\3\t\3\4\4\t\4\4\5\t\5\4\6\t\6\4\7\t\7\4\b\t\b\4\t\t\t\4\n\t\n\4\13"+ "\t\13\4\f\t\f\4\r\t\r\4\16\t\16\4\17\t\17\4\20\t\20\4\21\t\21\4\22\t\22"+ "\4\23\t\23\4\24\t\24\4\25\t\25\4\26\t\26\4\27\t\27\4\30\t\30\4\31\t\31"+ @@ -156,302 +164,339 @@ public SqlBaseLexer(CharStream input) { "\tI\4J\tJ\4K\tK\4L\tL\4M\tM\4N\tN\4O\tO\4P\tP\4Q\tQ\4R\tR\4S\tS\4T\tT"+ "\4U\tU\4V\tV\4W\tW\4X\tX\4Y\tY\4Z\tZ\4[\t[\4\\\t\\\4]\t]\4^\t^\4_\t_\4"+ "`\t`\4a\ta\4b\tb\4c\tc\4d\td\4e\te\4f\tf\4g\tg\4h\th\4i\ti\4j\tj\4k\t"+ - "k\4l\tl\4m\tm\4n\tn\4o\to\4p\tp\4q\tq\3\2\3\2\3\3\3\3\3\4\3\4\3\5\3\5"+ - "\3\6\3\6\3\6\3\6\3\7\3\7\3\7\3\7\3\7\3\7\3\7\3\7\3\b\3\b\3\b\3\b\3\b\3"+ - "\b\3\b\3\b\3\b\3\t\3\t\3\t\3\t\3\n\3\n\3\n\3\n\3\13\3\13\3\13\3\f\3\f"+ - "\3\f\3\f\3\r\3\r\3\r\3\r\3\r\3\r\3\r\3\r\3\16\3\16\3\16\3\17\3\17\3\17"+ - "\3\17\3\17\3\20\3\20\3\20\3\20\3\20\3\20\3\20\3\20\3\21\3\21\3\21\3\21"+ - "\3\21\3\21\3\21\3\21\3\21\3\22\3\22\3\22\3\22\3\22\3\22\3\22\3\22\3\23"+ - "\3\23\3\23\3\23\3\23\3\23\3\23\3\23\3\24\3\24\3\24\3\24\3\24\3\24\3\25"+ - "\3\25\3\25\3\25\3\25\3\26\3\26\3\26\3\26\3\26\3\26\3\26\3\26\3\26\3\27"+ - "\3\27\3\27\3\27\3\27\3\27\3\27\3\27\3\27\3\30\3\30\3\30\3\30\3\30\3\30"+ - "\3\30\3\31\3\31\3\31\3\31\3\31\3\31\3\31\3\31\3\31\3\31\3\31\3\32\3\32"+ - "\3\32\3\32\3\32\3\32\3\32\3\33\3\33\3\33\3\33\3\33\3\33\3\33\3\33\3\34"+ - "\3\34\3\34\3\34\3\34\3\34\3\34\3\34\3\35\3\35\3\35\3\35\3\35\3\35\3\36"+ - "\3\36\3\36\3\36\3\36\3\36\3\37\3\37\3\37\3\37\3\37\3\37\3\37\3 \3 \3 "+ - "\3 \3 \3!\3!\3!\3!\3!\3\"\3\"\3\"\3\"\3\"\3\"\3\"\3\"\3\"\3\"\3#\3#\3"+ - "#\3#\3#\3#\3#\3#\3#\3$\3$\3$\3$\3$\3$\3%\3%\3%\3%\3%\3%\3%\3&\3&\3&\3"+ - "\'\3\'\3\'\3\'\3\'\3\'\3(\3(\3(\3)\3)\3)\3)\3)\3*\3*\3*\3*\3*\3+\3+\3"+ - "+\3+\3+\3,\3,\3,\3,\3,\3-\3-\3-\3-\3-\3-\3.\3.\3.\3.\3.\3.\3.\3/\3/\3"+ - "/\3/\3/\3/\3\60\3\60\3\60\3\60\3\60\3\60\3\60\3\60\3\61\3\61\3\61\3\61"+ - "\3\62\3\62\3\62\3\62\3\62\3\63\3\63\3\63\3\63\3\63\3\63\3\64\3\64\3\64"+ - "\3\65\3\65\3\65\3\65\3\65\3\65\3\65\3\65\3\65\3\65\3\66\3\66\3\66\3\67"+ - "\3\67\3\67\3\67\3\67\3\67\38\38\38\38\38\38\39\39\39\39\39\39\39\3:\3"+ - ":\3:\3:\3:\3:\3:\3:\3:\3;\3;\3;\3;\3;\3<\3<\3<\3<\3<\3<\3=\3=\3=\3=\3"+ - "=\3=\3>\3>\3>\3>\3>\3>\3?\3?\3?\3?\3?\3?\3?\3?\3@\3@\3@\3@\3@\3@\3@\3"+ - "A\3A\3A\3A\3A\3B\3B\3B\3B\3C\3C\3C\3C\3C\3C\3D\3D\3D\3D\3D\3D\3D\3E\3"+ - "E\3E\3E\3E\3F\3F\3F\3F\3F\3G\3G\3G\3G\3G\3H\3H\3H\3H\3H\3H\3I\3I\3I\3"+ - "I\3I\3I\3J\3J\3J\3J\3J\3J\3J\3K\3K\3K\3K\3K\3K\3L\3L\3L\3L\3L\3M\3M\3"+ - "M\3M\3M\3M\3M\3M\3N\3N\3N\3N\3O\3O\3O\3O\3O\3O\3O\3P\3P\3P\3Q\3Q\3Q\3"+ - "R\3R\3R\3R\3S\3S\3S\3S\3S\3S\3T\3T\3U\3U\3V\3V\3V\3V\3V\3V\3V\5V\u02d1"+ - "\nV\3W\3W\3X\3X\3X\3Y\3Y\3Z\3Z\3Z\3[\3[\3\\\3\\\3]\3]\3^\3^\3_\3_\3`\3"+ - "`\3`\3a\3a\3b\3b\3c\3c\3c\3c\7c\u02f2\nc\fc\16c\u02f5\13c\3c\3c\3d\6d"+ - "\u02fa\nd\rd\16d\u02fb\3e\6e\u02ff\ne\re\16e\u0300\3e\3e\7e\u0305\ne\f"+ - "e\16e\u0308\13e\3e\3e\6e\u030c\ne\re\16e\u030d\3e\6e\u0311\ne\re\16e\u0312"+ - "\3e\3e\7e\u0317\ne\fe\16e\u031a\13e\5e\u031c\ne\3e\3e\3e\3e\6e\u0322\n"+ - "e\re\16e\u0323\3e\3e\5e\u0328\ne\3f\3f\5f\u032c\nf\3f\3f\3f\7f\u0331\n"+ - "f\ff\16f\u0334\13f\3g\3g\3g\3g\6g\u033a\ng\rg\16g\u033b\3h\3h\3h\6h\u0341"+ - "\nh\rh\16h\u0342\3i\3i\3i\3i\7i\u0349\ni\fi\16i\u034c\13i\3i\3i\3j\3j"+ - "\3j\3j\7j\u0354\nj\fj\16j\u0357\13j\3j\3j\3k\3k\5k\u035d\nk\3k\6k\u0360"+ - "\nk\rk\16k\u0361\3l\3l\3m\3m\3n\3n\3n\3n\7n\u036c\nn\fn\16n\u036f\13n"+ - "\3n\5n\u0372\nn\3n\5n\u0375\nn\3n\3n\3o\3o\3o\3o\3o\7o\u037e\no\fo\16"+ - "o\u0381\13o\3o\3o\3o\3o\3o\3p\6p\u0389\np\rp\16p\u038a\3p\3p\3q\3q\3\u037f"+ - "\2r\3\3\5\4\7\5\t\6\13\7\r\b\17\t\21\n\23\13\25\f\27\r\31\16\33\17\35"+ - "\20\37\21!\22#\23%\24\'\25)\26+\27-\30/\31\61\32\63\33\65\34\67\359\36"+ - ";\37= ?!A\"C#E$G%I&K\'M(O)Q*S+U,W-Y.[/]\60_\61a\62c\63e\64g\65i\66k\67"+ - "m8o9q:s;u{?}@\177A\u0081B\u0083C\u0085D\u0087E\u0089F\u008bG\u008d"+ - "H\u008fI\u0091J\u0093K\u0095L\u0097M\u0099N\u009bO\u009dP\u009fQ\u00a1"+ - "R\u00a3S\u00a5T\u00a7U\u00a9V\u00abW\u00adX\u00afY\u00b1Z\u00b3[\u00b5"+ - "\\\u00b7]\u00b9^\u00bb_\u00bd`\u00bfa\u00c1b\u00c3c\u00c5d\u00c7e\u00c9"+ - "f\u00cbg\u00cdh\u00cfi\u00d1j\u00d3k\u00d5\2\u00d7\2\u00d9\2\u00dbl\u00dd"+ - "m\u00dfn\u00e1o\3\2\f\3\2))\4\2BBaa\5\2<\3\2\2\2\u0199\u019a\7H\2\2\u019a\u019b\7T\2"+ - "\2\u019b\u019c\7Q\2\2\u019c\u019d\7O\2\2\u019d@\3\2\2\2\u019e\u019f\7"+ - "H\2\2\u019f\u01a0\7W\2\2\u01a0\u01a1\7N\2\2\u01a1\u01a2\7N\2\2\u01a2B"+ - "\3\2\2\2\u01a3\u01a4\7H\2\2\u01a4\u01a5\7W\2\2\u01a5\u01a6\7P\2\2\u01a6"+ - "\u01a7\7E\2\2\u01a7\u01a8\7V\2\2\u01a8\u01a9\7K\2\2\u01a9\u01aa\7Q\2\2"+ - "\u01aa\u01ab\7P\2\2\u01ab\u01ac\7U\2\2\u01acD\3\2\2\2\u01ad\u01ae\7I\2"+ - "\2\u01ae\u01af\7T\2\2\u01af\u01b0\7C\2\2\u01b0\u01b1\7R\2\2\u01b1\u01b2"+ - "\7J\2\2\u01b2\u01b3\7X\2\2\u01b3\u01b4\7K\2\2\u01b4\u01b5\7\\\2\2\u01b5"+ - "F\3\2\2\2\u01b6\u01b7\7I\2\2\u01b7\u01b8\7T\2\2\u01b8\u01b9\7Q\2\2\u01b9"+ - "\u01ba\7W\2\2\u01ba\u01bb\7R\2\2\u01bbH\3\2\2\2\u01bc\u01bd\7J\2\2\u01bd"+ - "\u01be\7C\2\2\u01be\u01bf\7X\2\2\u01bf\u01c0\7K\2\2\u01c0\u01c1\7P\2\2"+ - "\u01c1\u01c2\7I\2\2\u01c2J\3\2\2\2\u01c3\u01c4\7K\2\2\u01c4\u01c5\7P\2"+ - "\2\u01c5L\3\2\2\2\u01c6\u01c7\7K\2\2\u01c7\u01c8\7P\2\2\u01c8\u01c9\7"+ - "P\2\2\u01c9\u01ca\7G\2\2\u01ca\u01cb\7T\2\2\u01cbN\3\2\2\2\u01cc\u01cd"+ - "\7K\2\2\u01cd\u01ce\7U\2\2\u01ceP\3\2\2\2\u01cf\u01d0\7L\2\2\u01d0\u01d1"+ - "\7Q\2\2\u01d1\u01d2\7K\2\2\u01d2\u01d3\7P\2\2\u01d3R\3\2\2\2\u01d4\u01d5"+ - "\7N\2\2\u01d5\u01d6\7C\2\2\u01d6\u01d7\7U\2\2\u01d7\u01d8\7V\2\2\u01d8"+ - "T\3\2\2\2\u01d9\u01da\7N\2\2\u01da\u01db\7G\2\2\u01db\u01dc\7H\2\2\u01dc"+ - "\u01dd\7V\2\2\u01ddV\3\2\2\2\u01de\u01df\7N\2\2\u01df\u01e0\7K\2\2\u01e0"+ - "\u01e1\7M\2\2\u01e1\u01e2\7G\2\2\u01e2X\3\2\2\2\u01e3\u01e4\7N\2\2\u01e4"+ - "\u01e5\7K\2\2\u01e5\u01e6\7O\2\2\u01e6\u01e7\7K\2\2\u01e7\u01e8\7V\2\2"+ - "\u01e8Z\3\2\2\2\u01e9\u01ea\7O\2\2\u01ea\u01eb\7C\2\2\u01eb\u01ec\7R\2"+ - "\2\u01ec\u01ed\7R\2\2\u01ed\u01ee\7G\2\2\u01ee\u01ef\7F\2\2\u01ef\\\3"+ - "\2\2\2\u01f0\u01f1\7O\2\2\u01f1\u01f2\7C\2\2\u01f2\u01f3\7V\2\2\u01f3"+ - "\u01f4\7E\2\2\u01f4\u01f5\7J\2\2\u01f5^\3\2\2\2\u01f6\u01f7\7P\2\2\u01f7"+ - "\u01f8\7C\2\2\u01f8\u01f9\7V\2\2\u01f9\u01fa\7W\2\2\u01fa\u01fb\7T\2\2"+ - "\u01fb\u01fc\7C\2\2\u01fc\u01fd\7N\2\2\u01fd`\3\2\2\2\u01fe\u01ff\7P\2"+ - "\2\u01ff\u0200\7Q\2\2\u0200\u0201\7V\2\2\u0201b\3\2\2\2\u0202\u0203\7"+ - "P\2\2\u0203\u0204\7W\2\2\u0204\u0205\7N\2\2\u0205\u0206\7N\2\2\u0206d"+ - "\3\2\2\2\u0207\u0208\7P\2\2\u0208\u0209\7W\2\2\u0209\u020a\7N\2\2\u020a"+ - "\u020b\7N\2\2\u020b\u020c\7U\2\2\u020cf\3\2\2\2\u020d\u020e\7Q\2\2\u020e"+ - "\u020f\7P\2\2\u020fh\3\2\2\2\u0210\u0211\7Q\2\2\u0211\u0212\7R\2\2\u0212"+ - "\u0213\7V\2\2\u0213\u0214\7K\2\2\u0214\u0215\7O\2\2\u0215\u0216\7K\2\2"+ - "\u0216\u0217\7\\\2\2\u0217\u0218\7G\2\2\u0218\u0219\7F\2\2\u0219j\3\2"+ - "\2\2\u021a\u021b\7Q\2\2\u021b\u021c\7T\2\2\u021cl\3\2\2\2\u021d\u021e"+ - "\7Q\2\2\u021e\u021f\7T\2\2\u021f\u0220\7F\2\2\u0220\u0221\7G\2\2\u0221"+ - "\u0222\7T\2\2\u0222n\3\2\2\2\u0223\u0224\7Q\2\2\u0224\u0225\7W\2\2\u0225"+ - "\u0226\7V\2\2\u0226\u0227\7G\2\2\u0227\u0228\7T\2\2\u0228p\3\2\2\2\u0229"+ - "\u022a\7R\2\2\u022a\u022b\7C\2\2\u022b\u022c\7T\2\2\u022c\u022d\7U\2\2"+ - "\u022d\u022e\7G\2\2\u022e\u022f\7F\2\2\u022fr\3\2\2\2\u0230\u0231\7R\2"+ - "\2\u0231\u0232\7J\2\2\u0232\u0233\7[\2\2\u0233\u0234\7U\2\2\u0234\u0235"+ - "\7K\2\2\u0235\u0236\7E\2\2\u0236\u0237\7C\2\2\u0237\u0238\7N\2\2\u0238"+ - "t\3\2\2\2\u0239\u023a\7R\2\2\u023a\u023b\7N\2\2\u023b\u023c\7C\2\2\u023c"+ - "\u023d\7P\2\2\u023dv\3\2\2\2\u023e\u023f\7T\2\2\u023f\u0240\7K\2\2\u0240"+ - "\u0241\7I\2\2\u0241\u0242\7J\2\2\u0242\u0243\7V\2\2\u0243x\3\2\2\2\u0244"+ - "\u0245\7T\2\2\u0245\u0246\7N\2\2\u0246\u0247\7K\2\2\u0247\u0248\7M\2\2"+ - "\u0248\u0249\7G\2\2\u0249z\3\2\2\2\u024a\u024b\7S\2\2\u024b\u024c\7W\2"+ - "\2\u024c\u024d\7G\2\2\u024d\u024e\7T\2\2\u024e\u024f\7[\2\2\u024f|\3\2"+ - "\2\2\u0250\u0251\7U\2\2\u0251\u0252\7E\2\2\u0252\u0253\7J\2\2\u0253\u0254"+ - "\7G\2\2\u0254\u0255\7O\2\2\u0255\u0256\7C\2\2\u0256\u0257\7U\2\2\u0257"+ - "~\3\2\2\2\u0258\u0259\7U\2\2\u0259\u025a\7G\2\2\u025a\u025b\7N\2\2\u025b"+ - "\u025c\7G\2\2\u025c\u025d\7E\2\2\u025d\u025e\7V\2\2\u025e\u0080\3\2\2"+ - "\2\u025f\u0260\7U\2\2\u0260\u0261\7J\2\2\u0261\u0262\7Q\2\2\u0262\u0263"+ - "\7Y\2\2\u0263\u0082\3\2\2\2\u0264\u0265\7U\2\2\u0265\u0266\7[\2\2\u0266"+ - "\u0267\7U\2\2\u0267\u0084\3\2\2\2\u0268\u0269\7V\2\2\u0269\u026a\7C\2"+ - "\2\u026a\u026b\7D\2\2\u026b\u026c\7N\2\2\u026c\u026d\7G\2\2\u026d\u0086"+ - "\3\2\2\2\u026e\u026f\7V\2\2\u026f\u0270\7C\2\2\u0270\u0271\7D\2\2\u0271"+ - "\u0272\7N\2\2\u0272\u0273\7G\2\2\u0273\u0274\7U\2\2\u0274\u0088\3\2\2"+ - "\2\u0275\u0276\7V\2\2\u0276\u0277\7G\2\2\u0277\u0278\7Z\2\2\u0278\u0279"+ - "\7V\2\2\u0279\u008a\3\2\2\2\u027a\u027b\7V\2\2\u027b\u027c\7T\2\2\u027c"+ - "\u027d\7W\2\2\u027d\u027e\7G\2\2\u027e\u008c\3\2\2\2\u027f\u0280\7V\2"+ - "\2\u0280\u0281\7[\2\2\u0281\u0282\7R\2\2\u0282\u0283\7G\2\2\u0283\u008e"+ - "\3\2\2\2\u0284\u0285\7V\2\2\u0285\u0286\7[\2\2\u0286\u0287\7R\2\2\u0287"+ - "\u0288\7G\2\2\u0288\u0289\7U\2\2\u0289\u0090\3\2\2\2\u028a\u028b\7W\2"+ - "\2\u028b\u028c\7U\2\2\u028c\u028d\7K\2\2\u028d\u028e\7P\2\2\u028e\u028f"+ - "\7I\2\2\u028f\u0092\3\2\2\2\u0290\u0291\7X\2\2\u0291\u0292\7G\2\2\u0292"+ - "\u0293\7T\2\2\u0293\u0294\7K\2\2\u0294\u0295\7H\2\2\u0295\u0296\7[\2\2"+ - "\u0296\u0094\3\2\2\2\u0297\u0298\7Y\2\2\u0298\u0299\7J\2\2\u0299\u029a"+ - "\7G\2\2\u029a\u029b\7T\2\2\u029b\u029c\7G\2\2\u029c\u0096\3\2\2\2\u029d"+ - "\u029e\7Y\2\2\u029e\u029f\7K\2\2\u029f\u02a0\7V\2\2\u02a0\u02a1\7J\2\2"+ - "\u02a1\u0098\3\2\2\2\u02a2\u02a3\7}\2\2\u02a3\u02a4\7G\2\2\u02a4\u02a5"+ - "\7U\2\2\u02a5\u02a6\7E\2\2\u02a6\u02a7\7C\2\2\u02a7\u02a8\7R\2\2\u02a8"+ - "\u02a9\7G\2\2\u02a9\u009a\3\2\2\2\u02aa\u02ab\7}\2\2\u02ab\u02ac\7H\2"+ - "\2\u02ac\u02ad\7P\2\2\u02ad\u009c\3\2\2\2\u02ae\u02af\7}\2\2\u02af\u02b0"+ - "\7N\2\2\u02b0\u02b1\7K\2\2\u02b1\u02b2\7O\2\2\u02b2\u02b3\7K\2\2\u02b3"+ - "\u02b4\7V\2\2\u02b4\u009e\3\2\2\2\u02b5\u02b6\7}\2\2\u02b6\u02b7\7F\2"+ - "\2\u02b7\u00a0\3\2\2\2\u02b8\u02b9\7}\2\2\u02b9\u02ba\7V\2\2\u02ba\u00a2"+ - "\3\2\2\2\u02bb\u02bc\7}\2\2\u02bc\u02bd\7V\2\2\u02bd\u02be\7U\2\2\u02be"+ - "\u00a4\3\2\2\2\u02bf\u02c0\7}\2\2\u02c0\u02c1\7I\2\2\u02c1\u02c2\7W\2"+ - "\2\u02c2\u02c3\7K\2\2\u02c3\u02c4\7F\2\2\u02c4\u00a6\3\2\2\2\u02c5\u02c6"+ - "\7\177\2\2\u02c6\u00a8\3\2\2\2\u02c7\u02c8\7?\2\2\u02c8\u00aa\3\2\2\2"+ - "\u02c9\u02ca\7>\2\2\u02ca\u02d1\7@\2\2\u02cb\u02cc\7#\2\2\u02cc\u02d1"+ - "\7?\2\2\u02cd\u02ce\7>\2\2\u02ce\u02cf\7?\2\2\u02cf\u02d1\7@\2\2\u02d0"+ - "\u02c9\3\2\2\2\u02d0\u02cb\3\2\2\2\u02d0\u02cd\3\2\2\2\u02d1\u00ac\3\2"+ - "\2\2\u02d2\u02d3\7>\2\2\u02d3\u00ae\3\2\2\2\u02d4\u02d5\7>\2\2\u02d5\u02d6"+ - "\7?\2\2\u02d6\u00b0\3\2\2\2\u02d7\u02d8\7@\2\2\u02d8\u00b2\3\2\2\2\u02d9"+ - "\u02da\7@\2\2\u02da\u02db\7?\2\2\u02db\u00b4\3\2\2\2\u02dc\u02dd\7-\2"+ - "\2\u02dd\u00b6\3\2\2\2\u02de\u02df\7/\2\2\u02df\u00b8\3\2\2\2\u02e0\u02e1"+ - "\7,\2\2\u02e1\u00ba\3\2\2\2\u02e2\u02e3\7\61\2\2\u02e3\u00bc\3\2\2\2\u02e4"+ - "\u02e5\7\'\2\2\u02e5\u00be\3\2\2\2\u02e6\u02e7\7~\2\2\u02e7\u02e8\7~\2"+ - "\2\u02e8\u00c0\3\2\2\2\u02e9\u02ea\7\60\2\2\u02ea\u00c2\3\2\2\2\u02eb"+ - "\u02ec\7A\2\2\u02ec\u00c4\3\2\2\2\u02ed\u02f3\7)\2\2\u02ee\u02f2\n\2\2"+ - "\2\u02ef\u02f0\7)\2\2\u02f0\u02f2\7)\2\2\u02f1\u02ee\3\2\2\2\u02f1\u02ef"+ - "\3\2\2\2\u02f2\u02f5\3\2\2\2\u02f3\u02f1\3\2\2\2\u02f3\u02f4\3\2\2\2\u02f4"+ - "\u02f6\3\2\2\2\u02f5\u02f3\3\2\2\2\u02f6\u02f7\7)\2\2\u02f7\u00c6\3\2"+ - "\2\2\u02f8\u02fa\5\u00d7l\2\u02f9\u02f8\3\2\2\2\u02fa\u02fb\3\2\2\2\u02fb"+ - "\u02f9\3\2\2\2\u02fb\u02fc\3\2\2\2\u02fc\u00c8\3\2\2\2\u02fd\u02ff\5\u00d7"+ - "l\2\u02fe\u02fd\3\2\2\2\u02ff\u0300\3\2\2\2\u0300\u02fe\3\2\2\2\u0300"+ - "\u0301\3\2\2\2\u0301\u0302\3\2\2\2\u0302\u0306\5\u00c1a\2\u0303\u0305"+ - "\5\u00d7l\2\u0304\u0303\3\2\2\2\u0305\u0308\3\2\2\2\u0306\u0304\3\2\2"+ - "\2\u0306\u0307\3\2\2\2\u0307\u0328\3\2\2\2\u0308\u0306\3\2\2\2\u0309\u030b"+ - "\5\u00c1a\2\u030a\u030c\5\u00d7l\2\u030b\u030a\3\2\2\2\u030c\u030d\3\2"+ - "\2\2\u030d\u030b\3\2\2\2\u030d\u030e\3\2\2\2\u030e\u0328\3\2\2\2\u030f"+ - "\u0311\5\u00d7l\2\u0310\u030f\3\2\2\2\u0311\u0312\3\2\2\2\u0312\u0310"+ - "\3\2\2\2\u0312\u0313\3\2\2\2\u0313\u031b\3\2\2\2\u0314\u0318\5\u00c1a"+ - "\2\u0315\u0317\5\u00d7l\2\u0316\u0315\3\2\2\2\u0317\u031a\3\2\2\2\u0318"+ - "\u0316\3\2\2\2\u0318\u0319\3\2\2\2\u0319\u031c\3\2\2\2\u031a\u0318\3\2"+ - "\2\2\u031b\u0314\3\2\2\2\u031b\u031c\3\2\2\2\u031c\u031d\3\2\2\2\u031d"+ - "\u031e\5\u00d5k\2\u031e\u0328\3\2\2\2\u031f\u0321\5\u00c1a\2\u0320\u0322"+ - "\5\u00d7l\2\u0321\u0320\3\2\2\2\u0322\u0323\3\2\2\2\u0323\u0321\3\2\2"+ - "\2\u0323\u0324\3\2\2\2\u0324\u0325\3\2\2\2\u0325\u0326\5\u00d5k\2\u0326"+ - "\u0328\3\2\2\2\u0327\u02fe\3\2\2\2\u0327\u0309\3\2\2\2\u0327\u0310\3\2"+ - "\2\2\u0327\u031f\3\2\2\2\u0328\u00ca\3\2\2\2\u0329\u032c\5\u00d9m\2\u032a"+ - "\u032c\7a\2\2\u032b\u0329\3\2\2\2\u032b\u032a\3\2\2\2\u032c\u0332\3\2"+ - "\2\2\u032d\u0331\5\u00d9m\2\u032e\u0331\5\u00d7l\2\u032f\u0331\t\3\2\2"+ - "\u0330\u032d\3\2\2\2\u0330\u032e\3\2\2\2\u0330\u032f\3\2\2\2\u0331\u0334"+ - "\3\2\2\2\u0332\u0330\3\2\2\2\u0332\u0333\3\2\2\2\u0333\u00cc\3\2\2\2\u0334"+ - "\u0332\3\2\2\2\u0335\u0339\5\u00d7l\2\u0336\u033a\5\u00d9m\2\u0337\u033a"+ - "\5\u00d7l\2\u0338\u033a\t\4\2\2\u0339\u0336\3\2\2\2\u0339\u0337\3\2\2"+ - "\2\u0339\u0338\3\2\2\2\u033a\u033b\3\2\2\2\u033b\u0339\3\2\2\2\u033b\u033c"+ - "\3\2\2\2\u033c\u00ce\3\2\2\2\u033d\u0341\5\u00d9m\2\u033e\u0341\5\u00d7"+ - "l\2\u033f\u0341\7a\2\2\u0340\u033d\3\2\2\2\u0340\u033e\3\2\2\2\u0340\u033f"+ - "\3\2\2\2\u0341\u0342\3\2\2\2\u0342\u0340\3\2\2\2\u0342\u0343\3\2\2\2\u0343"+ - "\u00d0\3\2\2\2\u0344\u034a\7$\2\2\u0345\u0349\n\5\2\2\u0346\u0347\7$\2"+ - "\2\u0347\u0349\7$\2\2\u0348\u0345\3\2\2\2\u0348\u0346\3\2\2\2\u0349\u034c"+ - "\3\2\2\2\u034a\u0348\3\2\2\2\u034a\u034b\3\2\2\2\u034b\u034d\3\2\2\2\u034c"+ - "\u034a\3\2\2\2\u034d\u034e\7$\2\2\u034e\u00d2\3\2\2\2\u034f\u0355\7b\2"+ - "\2\u0350\u0354\n\6\2\2\u0351\u0352\7b\2\2\u0352\u0354\7b\2\2\u0353\u0350"+ - "\3\2\2\2\u0353\u0351\3\2\2\2\u0354\u0357\3\2\2\2\u0355\u0353\3\2\2\2\u0355"+ - "\u0356\3\2\2\2\u0356\u0358\3\2\2\2\u0357\u0355\3\2\2\2\u0358\u0359\7b"+ - "\2\2\u0359\u00d4\3\2\2\2\u035a\u035c\7G\2\2\u035b\u035d\t\7\2\2\u035c"+ - "\u035b\3\2\2\2\u035c\u035d\3\2\2\2\u035d\u035f\3\2\2\2\u035e\u0360\5\u00d7"+ - "l\2\u035f\u035e\3\2\2\2\u0360\u0361\3\2\2\2\u0361\u035f\3\2\2\2\u0361"+ - "\u0362\3\2\2\2\u0362\u00d6\3\2\2\2\u0363\u0364\t\b\2\2\u0364\u00d8\3\2"+ - "\2\2\u0365\u0366\t\t\2\2\u0366\u00da\3\2\2\2\u0367\u0368\7/\2\2\u0368"+ - "\u0369\7/\2\2\u0369\u036d\3\2\2\2\u036a\u036c\n\n\2\2\u036b\u036a\3\2"+ - "\2\2\u036c\u036f\3\2\2\2\u036d\u036b\3\2\2\2\u036d\u036e\3\2\2\2\u036e"+ - "\u0371\3\2\2\2\u036f\u036d\3\2\2\2\u0370\u0372\7\17\2\2\u0371\u0370\3"+ - "\2\2\2\u0371\u0372\3\2\2\2\u0372\u0374\3\2\2\2\u0373\u0375\7\f\2\2\u0374"+ - "\u0373\3\2\2\2\u0374\u0375\3\2\2\2\u0375\u0376\3\2\2\2\u0376\u0377\bn"+ - "\2\2\u0377\u00dc\3\2\2\2\u0378\u0379\7\61\2\2\u0379\u037a\7,\2\2\u037a"+ - "\u037f\3\2\2\2\u037b\u037e\5\u00ddo\2\u037c\u037e\13\2\2\2\u037d\u037b"+ - "\3\2\2\2\u037d\u037c\3\2\2\2\u037e\u0381\3\2\2\2\u037f\u0380\3\2\2\2\u037f"+ - "\u037d\3\2\2\2\u0380\u0382\3\2\2\2\u0381\u037f\3\2\2\2\u0382\u0383\7,"+ - "\2\2\u0383\u0384\7\61\2\2\u0384\u0385\3\2\2\2\u0385\u0386\bo\2\2\u0386"+ - "\u00de\3\2\2\2\u0387\u0389\t\13\2\2\u0388\u0387\3\2\2\2\u0389\u038a\3"+ - "\2\2\2\u038a\u0388\3\2\2\2\u038a\u038b\3\2\2\2\u038b\u038c\3\2\2\2\u038c"+ - "\u038d\bp\2\2\u038d\u00e0\3\2\2\2\u038e\u038f\13\2\2\2\u038f\u00e2\3\2"+ - "\2\2\"\2\u02d0\u02f1\u02f3\u02fb\u0300\u0306\u030d\u0312\u0318\u031b\u0323"+ - "\u0327\u032b\u0330\u0332\u0339\u033b\u0340\u0342\u0348\u034a\u0353\u0355"+ - "\u035c\u0361\u036d\u0371\u0374\u037d\u037f\u038a\3\2\3\2"; + "k\4l\tl\4m\tm\4n\tn\4o\to\4p\tp\4q\tq\4r\tr\4s\ts\4t\tt\4u\tu\4v\tv\4"+ + "w\tw\4x\tx\4y\ty\4z\tz\4{\t{\4|\t|\4}\t}\4~\t~\4\177\t\177\3\2\3\2\3\3"+ + "\3\3\3\4\3\4\3\5\3\5\3\6\3\6\3\6\3\6\3\7\3\7\3\7\3\7\3\7\3\7\3\7\3\7\3"+ + "\b\3\b\3\b\3\b\3\b\3\b\3\b\3\b\3\b\3\t\3\t\3\t\3\t\3\n\3\n\3\n\3\n\3\13"+ + "\3\13\3\13\3\f\3\f\3\f\3\f\3\r\3\r\3\r\3\r\3\r\3\r\3\r\3\r\3\16\3\16\3"+ + "\16\3\17\3\17\3\17\3\17\3\17\3\20\3\20\3\20\3\20\3\20\3\20\3\20\3\20\3"+ + "\21\3\21\3\21\3\21\3\21\3\21\3\21\3\21\3\21\3\22\3\22\3\22\3\22\3\22\3"+ + "\22\3\22\3\22\3\23\3\23\3\23\3\23\3\23\3\23\3\23\3\23\3\24\3\24\3\24\3"+ + "\24\3\25\3\25\3\25\3\25\3\25\3\26\3\26\3\26\3\26\3\26\3\26\3\27\3\27\3"+ + "\27\3\27\3\27\3\30\3\30\3\30\3\30\3\30\3\30\3\30\3\30\3\30\3\31\3\31\3"+ + "\31\3\31\3\31\3\31\3\31\3\31\3\31\3\32\3\32\3\32\3\32\3\32\3\32\3\32\3"+ + "\33\3\33\3\33\3\33\3\33\3\33\3\33\3\33\3\33\3\33\3\33\3\34\3\34\3\34\3"+ + "\34\3\34\3\34\3\34\3\35\3\35\3\35\3\35\3\35\3\35\3\35\3\35\3\36\3\36\3"+ + "\36\3\36\3\36\3\36\3\36\3\36\3\37\3\37\3\37\3\37\3\37\3\37\3 \3 \3 \3"+ + " \3 \3 \3!\3!\3!\3!\3!\3!\3!\3\"\3\"\3\"\3\"\3\"\3#\3#\3#\3#\3#\3$\3$"+ + "\3$\3$\3$\3$\3$\3$\3$\3$\3%\3%\3%\3%\3%\3%\3%\3%\3%\3&\3&\3&\3&\3&\3&"+ + "\3\'\3\'\3\'\3\'\3\'\3\'\3\'\3(\3(\3(\3(\3(\3)\3)\3)\3)\3)\3)\3*\3*\3"+ + "*\3+\3+\3+\3+\3+\3+\3,\3,\3,\3,\3,\3,\3,\3,\3,\3-\3-\3-\3.\3.\3.\3.\3"+ + ".\3/\3/\3/\3/\3/\3\60\3\60\3\60\3\60\3\60\3\61\3\61\3\61\3\61\3\61\3\62"+ + "\3\62\3\62\3\62\3\62\3\62\3\63\3\63\3\63\3\63\3\63\3\63\3\63\3\64\3\64"+ + "\3\64\3\64\3\64\3\64\3\65\3\65\3\65\3\65\3\65\3\65\3\65\3\66\3\66\3\66"+ + "\3\66\3\66\3\66\3\66\3\66\3\67\3\67\3\67\3\67\3\67\3\67\38\38\38\38\3"+ + "8\38\38\39\39\39\39\39\39\39\39\3:\3:\3:\3:\3;\3;\3;\3;\3;\3<\3<\3<\3"+ + "<\3<\3<\3=\3=\3=\3>\3>\3>\3>\3>\3>\3>\3>\3>\3>\3?\3?\3?\3@\3@\3@\3@\3"+ + "@\3@\3A\3A\3A\3A\3A\3A\3B\3B\3B\3B\3B\3B\3B\3C\3C\3C\3C\3C\3C\3C\3C\3"+ + "C\3D\3D\3D\3D\3D\3E\3E\3E\3E\3E\3E\3F\3F\3F\3F\3F\3F\3G\3G\3G\3G\3G\3"+ + "G\3H\3H\3H\3H\3H\3H\3H\3H\3I\3I\3I\3I\3I\3I\3I\3J\3J\3J\3J\3J\3J\3J\3"+ + "J\3K\3K\3K\3K\3K\3K\3K\3L\3L\3L\3L\3L\3M\3M\3M\3M\3N\3N\3N\3N\3N\3N\3"+ + "O\3O\3O\3O\3O\3O\3O\3P\3P\3P\3P\3P\3Q\3Q\3Q\3Q\3Q\3R\3R\3R\3S\3S\3S\3"+ + "S\3S\3T\3T\3T\3T\3T\3T\3U\3U\3U\3U\3U\3U\3V\3V\3V\3V\3V\3V\3V\3W\3W\3"+ + "W\3W\3W\3W\3X\3X\3X\3X\3X\3Y\3Y\3Y\3Y\3Y\3Z\3Z\3Z\3Z\3Z\3Z\3[\3[\3[\3"+ + "[\3[\3[\3[\3[\3\\\3\\\3\\\3\\\3]\3]\3]\3]\3]\3]\3]\3^\3^\3^\3_\3_\3_\3"+ + "`\3`\3`\3`\3a\3a\3a\3a\3a\3a\3b\3b\3c\3c\3d\3d\3d\3d\3d\3d\3d\5d\u0343"+ + "\nd\3e\3e\3f\3f\3f\3g\3g\3h\3h\3h\3i\3i\3j\3j\3k\3k\3l\3l\3m\3m\3n\3n"+ + "\3n\3o\3o\3p\3p\3q\3q\3q\3q\7q\u0364\nq\fq\16q\u0367\13q\3q\3q\3r\6r\u036c"+ + "\nr\rr\16r\u036d\3s\6s\u0371\ns\rs\16s\u0372\3s\3s\7s\u0377\ns\fs\16s"+ + "\u037a\13s\3s\3s\6s\u037e\ns\rs\16s\u037f\3s\6s\u0383\ns\rs\16s\u0384"+ + "\3s\3s\7s\u0389\ns\fs\16s\u038c\13s\5s\u038e\ns\3s\3s\3s\3s\6s\u0394\n"+ + "s\rs\16s\u0395\3s\3s\5s\u039a\ns\3t\3t\5t\u039e\nt\3t\3t\3t\7t\u03a3\n"+ + "t\ft\16t\u03a6\13t\3u\3u\3u\3u\6u\u03ac\nu\ru\16u\u03ad\3v\3v\3v\6v\u03b3"+ + "\nv\rv\16v\u03b4\3w\3w\3w\3w\7w\u03bb\nw\fw\16w\u03be\13w\3w\3w\3x\3x"+ + "\3x\3x\7x\u03c6\nx\fx\16x\u03c9\13x\3x\3x\3y\3y\5y\u03cf\ny\3y\6y\u03d2"+ + "\ny\ry\16y\u03d3\3z\3z\3{\3{\3|\3|\3|\3|\7|\u03de\n|\f|\16|\u03e1\13|"+ + "\3|\5|\u03e4\n|\3|\5|\u03e7\n|\3|\3|\3}\3}\3}\3}\3}\7}\u03f0\n}\f}\16"+ + "}\u03f3\13}\3}\3}\3}\3}\3}\3~\6~\u03fb\n~\r~\16~\u03fc\3~\3~\3\177\3\177"+ + "\3\u03f1\2\u0080\3\3\5\4\7\5\t\6\13\7\r\b\17\t\21\n\23\13\25\f\27\r\31"+ + "\16\33\17\35\20\37\21!\22#\23%\24\'\25)\26+\27-\30/\31\61\32\63\33\65"+ + "\34\67\359\36;\37= ?!A\"C#E$G%I&K\'M(O)Q*S+U,W-Y.[/]\60_\61a\62c\63e\64"+ + "g\65i\66k\67m8o9q:s;u{?}@\177A\u0081B\u0083C\u0085D\u0087E\u0089"+ + "F\u008bG\u008dH\u008fI\u0091J\u0093K\u0095L\u0097M\u0099N\u009bO\u009d"+ + "P\u009fQ\u00a1R\u00a3S\u00a5T\u00a7U\u00a9V\u00abW\u00adX\u00afY\u00b1"+ + "Z\u00b3[\u00b5\\\u00b7]\u00b9^\u00bb_\u00bd`\u00bfa\u00c1b\u00c3c\u00c5"+ + "d\u00c7e\u00c9f\u00cbg\u00cdh\u00cfi\u00d1j\u00d3k\u00d5l\u00d7m\u00d9"+ + "n\u00dbo\u00ddp\u00dfq\u00e1r\u00e3s\u00e5t\u00e7u\u00e9v\u00ebw\u00ed"+ + "x\u00efy\u00f1\2\u00f3\2\u00f5\2\u00f7z\u00f9{\u00fb|\u00fd}\3\2\f\3\2"+ + "))\4\2BBaa\5\2<\3\2\2\2\u01b1\u01b2\7H\2\2\u01b2\u01b3\7K\2\2\u01b3"+ + "\u01b4\7T\2\2\u01b4\u01b5\7U\2\2\u01b5\u01b6\7V\2\2\u01b6@\3\2\2\2\u01b7"+ + "\u01b8\7H\2\2\u01b8\u01b9\7Q\2\2\u01b9\u01ba\7T\2\2\u01ba\u01bb\7O\2\2"+ + "\u01bb\u01bc\7C\2\2\u01bc\u01bd\7V\2\2\u01bdB\3\2\2\2\u01be\u01bf\7H\2"+ + "\2\u01bf\u01c0\7T\2\2\u01c0\u01c1\7Q\2\2\u01c1\u01c2\7O\2\2\u01c2D\3\2"+ + "\2\2\u01c3\u01c4\7H\2\2\u01c4\u01c5\7W\2\2\u01c5\u01c6\7N\2\2\u01c6\u01c7"+ + "\7N\2\2\u01c7F\3\2\2\2\u01c8\u01c9\7H\2\2\u01c9\u01ca\7W\2\2\u01ca\u01cb"+ + "\7P\2\2\u01cb\u01cc\7E\2\2\u01cc\u01cd\7V\2\2\u01cd\u01ce\7K\2\2\u01ce"+ + "\u01cf\7Q\2\2\u01cf\u01d0\7P\2\2\u01d0\u01d1\7U\2\2\u01d1H\3\2\2\2\u01d2"+ + "\u01d3\7I\2\2\u01d3\u01d4\7T\2\2\u01d4\u01d5\7C\2\2\u01d5\u01d6\7R\2\2"+ + "\u01d6\u01d7\7J\2\2\u01d7\u01d8\7X\2\2\u01d8\u01d9\7K\2\2\u01d9\u01da"+ + "\7\\\2\2\u01daJ\3\2\2\2\u01db\u01dc\7I\2\2\u01dc\u01dd\7T\2\2\u01dd\u01de"+ + "\7Q\2\2\u01de\u01df\7W\2\2\u01df\u01e0\7R\2\2\u01e0L\3\2\2\2\u01e1\u01e2"+ + "\7J\2\2\u01e2\u01e3\7C\2\2\u01e3\u01e4\7X\2\2\u01e4\u01e5\7K\2\2\u01e5"+ + "\u01e6\7P\2\2\u01e6\u01e7\7I\2\2\u01e7N\3\2\2\2\u01e8\u01e9\7J\2\2\u01e9"+ + "\u01ea\7Q\2\2\u01ea\u01eb\7W\2\2\u01eb\u01ec\7T\2\2\u01ecP\3\2\2\2\u01ed"+ + "\u01ee\7J\2\2\u01ee\u01ef\7Q\2\2\u01ef\u01f0\7W\2\2\u01f0\u01f1\7T\2\2"+ + "\u01f1\u01f2\7U\2\2\u01f2R\3\2\2\2\u01f3\u01f4\7K\2\2\u01f4\u01f5\7P\2"+ + "\2\u01f5T\3\2\2\2\u01f6\u01f7\7K\2\2\u01f7\u01f8\7P\2\2\u01f8\u01f9\7"+ + "P\2\2\u01f9\u01fa\7G\2\2\u01fa\u01fb\7T\2\2\u01fbV\3\2\2\2\u01fc\u01fd"+ + "\7K\2\2\u01fd\u01fe\7P\2\2\u01fe\u01ff\7V\2\2\u01ff\u0200\7G\2\2\u0200"+ + "\u0201\7T\2\2\u0201\u0202\7X\2\2\u0202\u0203\7C\2\2\u0203\u0204\7N\2\2"+ + "\u0204X\3\2\2\2\u0205\u0206\7K\2\2\u0206\u0207\7U\2\2\u0207Z\3\2\2\2\u0208"+ + "\u0209\7L\2\2\u0209\u020a\7Q\2\2\u020a\u020b\7K\2\2\u020b\u020c\7P\2\2"+ + "\u020c\\\3\2\2\2\u020d\u020e\7N\2\2\u020e\u020f\7C\2\2\u020f\u0210\7U"+ + "\2\2\u0210\u0211\7V\2\2\u0211^\3\2\2\2\u0212\u0213\7N\2\2\u0213\u0214"+ + "\7G\2\2\u0214\u0215\7H\2\2\u0215\u0216\7V\2\2\u0216`\3\2\2\2\u0217\u0218"+ + "\7N\2\2\u0218\u0219\7K\2\2\u0219\u021a\7M\2\2\u021a\u021b\7G\2\2\u021b"+ + "b\3\2\2\2\u021c\u021d\7N\2\2\u021d\u021e\7K\2\2\u021e\u021f\7O\2\2\u021f"+ + "\u0220\7K\2\2\u0220\u0221\7V\2\2\u0221d\3\2\2\2\u0222\u0223\7O\2\2\u0223"+ + "\u0224\7C\2\2\u0224\u0225\7R\2\2\u0225\u0226\7R\2\2\u0226\u0227\7G\2\2"+ + "\u0227\u0228\7F\2\2\u0228f\3\2\2\2\u0229\u022a\7O\2\2\u022a\u022b\7C\2"+ + "\2\u022b\u022c\7V\2\2\u022c\u022d\7E\2\2\u022d\u022e\7J\2\2\u022eh\3\2"+ + "\2\2\u022f\u0230\7O\2\2\u0230\u0231\7K\2\2\u0231\u0232\7P\2\2\u0232\u0233"+ + "\7W\2\2\u0233\u0234\7V\2\2\u0234\u0235\7G\2\2\u0235j\3\2\2\2\u0236\u0237"+ + "\7O\2\2\u0237\u0238\7K\2\2\u0238\u0239\7P\2\2\u0239\u023a\7W\2\2\u023a"+ + "\u023b\7V\2\2\u023b\u023c\7G\2\2\u023c\u023d\7U\2\2\u023dl\3\2\2\2\u023e"+ + "\u023f\7O\2\2\u023f\u0240\7Q\2\2\u0240\u0241\7P\2\2\u0241\u0242\7V\2\2"+ + "\u0242\u0243\7J\2\2\u0243n\3\2\2\2\u0244\u0245\7O\2\2\u0245\u0246\7Q\2"+ + "\2\u0246\u0247\7P\2\2\u0247\u0248\7V\2\2\u0248\u0249\7J\2\2\u0249\u024a"+ + "\7U\2\2\u024ap\3\2\2\2\u024b\u024c\7P\2\2\u024c\u024d\7C\2\2\u024d\u024e"+ + "\7V\2\2\u024e\u024f\7W\2\2\u024f\u0250\7T\2\2\u0250\u0251\7C\2\2\u0251"+ + "\u0252\7N\2\2\u0252r\3\2\2\2\u0253\u0254\7P\2\2\u0254\u0255\7Q\2\2\u0255"+ + "\u0256\7V\2\2\u0256t\3\2\2\2\u0257\u0258\7P\2\2\u0258\u0259\7W\2\2\u0259"+ + "\u025a\7N\2\2\u025a\u025b\7N\2\2\u025bv\3\2\2\2\u025c\u025d\7P\2\2\u025d"+ + "\u025e\7W\2\2\u025e\u025f\7N\2\2\u025f\u0260\7N\2\2\u0260\u0261\7U\2\2"+ + "\u0261x\3\2\2\2\u0262\u0263\7Q\2\2\u0263\u0264\7P\2\2\u0264z\3\2\2\2\u0265"+ + "\u0266\7Q\2\2\u0266\u0267\7R\2\2\u0267\u0268\7V\2\2\u0268\u0269\7K\2\2"+ + "\u0269\u026a\7O\2\2\u026a\u026b\7K\2\2\u026b\u026c\7\\\2\2\u026c\u026d"+ + "\7G\2\2\u026d\u026e\7F\2\2\u026e|\3\2\2\2\u026f\u0270\7Q\2\2\u0270\u0271"+ + "\7T\2\2\u0271~\3\2\2\2\u0272\u0273\7Q\2\2\u0273\u0274\7T\2\2\u0274\u0275"+ + "\7F\2\2\u0275\u0276\7G\2\2\u0276\u0277\7T\2\2\u0277\u0080\3\2\2\2\u0278"+ + "\u0279\7Q\2\2\u0279\u027a\7W\2\2\u027a\u027b\7V\2\2\u027b\u027c\7G\2\2"+ + "\u027c\u027d\7T\2\2\u027d\u0082\3\2\2\2\u027e\u027f\7R\2\2\u027f\u0280"+ + "\7C\2\2\u0280\u0281\7T\2\2\u0281\u0282\7U\2\2\u0282\u0283\7G\2\2\u0283"+ + "\u0284\7F\2\2\u0284\u0084\3\2\2\2\u0285\u0286\7R\2\2\u0286\u0287\7J\2"+ + "\2\u0287\u0288\7[\2\2\u0288\u0289\7U\2\2\u0289\u028a\7K\2\2\u028a\u028b"+ + "\7E\2\2\u028b\u028c\7C\2\2\u028c\u028d\7N\2\2\u028d\u0086\3\2\2\2\u028e"+ + "\u028f\7R\2\2\u028f\u0290\7N\2\2\u0290\u0291\7C\2\2\u0291\u0292\7P\2\2"+ + "\u0292\u0088\3\2\2\2\u0293\u0294\7T\2\2\u0294\u0295\7K\2\2\u0295\u0296"+ + "\7I\2\2\u0296\u0297\7J\2\2\u0297\u0298\7V\2\2\u0298\u008a\3\2\2\2\u0299"+ + "\u029a\7T\2\2\u029a\u029b\7N\2\2\u029b\u029c\7K\2\2\u029c\u029d\7M\2\2"+ + "\u029d\u029e\7G\2\2\u029e\u008c\3\2\2\2\u029f\u02a0\7S\2\2\u02a0\u02a1"+ + "\7W\2\2\u02a1\u02a2\7G\2\2\u02a2\u02a3\7T\2\2\u02a3\u02a4\7[\2\2\u02a4"+ + "\u008e\3\2\2\2\u02a5\u02a6\7U\2\2\u02a6\u02a7\7E\2\2\u02a7\u02a8\7J\2"+ + "\2\u02a8\u02a9\7G\2\2\u02a9\u02aa\7O\2\2\u02aa\u02ab\7C\2\2\u02ab\u02ac"+ + "\7U\2\2\u02ac\u0090\3\2\2\2\u02ad\u02ae\7U\2\2\u02ae\u02af\7G\2\2\u02af"+ + "\u02b0\7E\2\2\u02b0\u02b1\7Q\2\2\u02b1\u02b2\7P\2\2\u02b2\u02b3\7F\2\2"+ + "\u02b3\u0092\3\2\2\2\u02b4\u02b5\7U\2\2\u02b5\u02b6\7G\2\2\u02b6\u02b7"+ + "\7E\2\2\u02b7\u02b8\7Q\2\2\u02b8\u02b9\7P\2\2\u02b9\u02ba\7F\2\2\u02ba"+ + "\u02bb\7U\2\2\u02bb\u0094\3\2\2\2\u02bc\u02bd\7U\2\2\u02bd\u02be\7G\2"+ + "\2\u02be\u02bf\7N\2\2\u02bf\u02c0\7G\2\2\u02c0\u02c1\7E\2\2\u02c1\u02c2"+ + "\7V\2\2\u02c2\u0096\3\2\2\2\u02c3\u02c4\7U\2\2\u02c4\u02c5\7J\2\2\u02c5"+ + "\u02c6\7Q\2\2\u02c6\u02c7\7Y\2\2\u02c7\u0098\3\2\2\2\u02c8\u02c9\7U\2"+ + "\2\u02c9\u02ca\7[\2\2\u02ca\u02cb\7U\2\2\u02cb\u009a\3\2\2\2\u02cc\u02cd"+ + "\7V\2\2\u02cd\u02ce\7C\2\2\u02ce\u02cf\7D\2\2\u02cf\u02d0\7N\2\2\u02d0"+ + "\u02d1\7G\2\2\u02d1\u009c\3\2\2\2\u02d2\u02d3\7V\2\2\u02d3\u02d4\7C\2"+ + "\2\u02d4\u02d5\7D\2\2\u02d5\u02d6\7N\2\2\u02d6\u02d7\7G\2\2\u02d7\u02d8"+ + "\7U\2\2\u02d8\u009e\3\2\2\2\u02d9\u02da\7V\2\2\u02da\u02db\7G\2\2\u02db"+ + "\u02dc\7Z\2\2\u02dc\u02dd\7V\2\2\u02dd\u00a0\3\2\2\2\u02de\u02df\7V\2"+ + "\2\u02df\u02e0\7T\2\2\u02e0\u02e1\7W\2\2\u02e1\u02e2\7G\2\2\u02e2\u00a2"+ + "\3\2\2\2\u02e3\u02e4\7V\2\2\u02e4\u02e5\7Q\2\2\u02e5\u00a4\3\2\2\2\u02e6"+ + "\u02e7\7V\2\2\u02e7\u02e8\7[\2\2\u02e8\u02e9\7R\2\2\u02e9\u02ea\7G\2\2"+ + "\u02ea\u00a6\3\2\2\2\u02eb\u02ec\7V\2\2\u02ec\u02ed\7[\2\2\u02ed\u02ee"+ + "\7R\2\2\u02ee\u02ef\7G\2\2\u02ef\u02f0\7U\2\2\u02f0\u00a8\3\2\2\2\u02f1"+ + "\u02f2\7W\2\2\u02f2\u02f3\7U\2\2\u02f3\u02f4\7K\2\2\u02f4\u02f5\7P\2\2"+ + "\u02f5\u02f6\7I\2\2\u02f6\u00aa\3\2\2\2\u02f7\u02f8\7X\2\2\u02f8\u02f9"+ + "\7G\2\2\u02f9\u02fa\7T\2\2\u02fa\u02fb\7K\2\2\u02fb\u02fc\7H\2\2\u02fc"+ + "\u02fd\7[\2\2\u02fd\u00ac\3\2\2\2\u02fe\u02ff\7Y\2\2\u02ff\u0300\7J\2"+ + "\2\u0300\u0301\7G\2\2\u0301\u0302\7T\2\2\u0302\u0303\7G\2\2\u0303\u00ae"+ + "\3\2\2\2\u0304\u0305\7Y\2\2\u0305\u0306\7K\2\2\u0306\u0307\7V\2\2\u0307"+ + "\u0308\7J\2\2\u0308\u00b0\3\2\2\2\u0309\u030a\7[\2\2\u030a\u030b\7G\2"+ + "\2\u030b\u030c\7C\2\2\u030c\u030d\7T\2\2\u030d\u00b2\3\2\2\2\u030e\u030f"+ + "\7[\2\2\u030f\u0310\7G\2\2\u0310\u0311\7C\2\2\u0311\u0312\7T\2\2\u0312"+ + "\u0313\7U\2\2\u0313\u00b4\3\2\2\2\u0314\u0315\7}\2\2\u0315\u0316\7G\2"+ + "\2\u0316\u0317\7U\2\2\u0317\u0318\7E\2\2\u0318\u0319\7C\2\2\u0319\u031a"+ + "\7R\2\2\u031a\u031b\7G\2\2\u031b\u00b6\3\2\2\2\u031c\u031d\7}\2\2\u031d"+ + "\u031e\7H\2\2\u031e\u031f\7P\2\2\u031f\u00b8\3\2\2\2\u0320\u0321\7}\2"+ + "\2\u0321\u0322\7N\2\2\u0322\u0323\7K\2\2\u0323\u0324\7O\2\2\u0324\u0325"+ + "\7K\2\2\u0325\u0326\7V\2\2\u0326\u00ba\3\2\2\2\u0327\u0328\7}\2\2\u0328"+ + "\u0329\7F\2\2\u0329\u00bc\3\2\2\2\u032a\u032b\7}\2\2\u032b\u032c\7V\2"+ + "\2\u032c\u00be\3\2\2\2\u032d\u032e\7}\2\2\u032e\u032f\7V\2\2\u032f\u0330"+ + "\7U\2\2\u0330\u00c0\3\2\2\2\u0331\u0332\7}\2\2\u0332\u0333\7I\2\2\u0333"+ + "\u0334\7W\2\2\u0334\u0335\7K\2\2\u0335\u0336\7F\2\2\u0336\u00c2\3\2\2"+ + "\2\u0337\u0338\7\177\2\2\u0338\u00c4\3\2\2\2\u0339\u033a\7?\2\2\u033a"+ + "\u00c6\3\2\2\2\u033b\u033c\7>\2\2\u033c\u0343\7@\2\2\u033d\u033e\7#\2"+ + "\2\u033e\u0343\7?\2\2\u033f\u0340\7>\2\2\u0340\u0341\7?\2\2\u0341\u0343"+ + "\7@\2\2\u0342\u033b\3\2\2\2\u0342\u033d\3\2\2\2\u0342\u033f\3\2\2\2\u0343"+ + "\u00c8\3\2\2\2\u0344\u0345\7>\2\2\u0345\u00ca\3\2\2\2\u0346\u0347\7>\2"+ + "\2\u0347\u0348\7?\2\2\u0348\u00cc\3\2\2\2\u0349\u034a\7@\2\2\u034a\u00ce"+ + "\3\2\2\2\u034b\u034c\7@\2\2\u034c\u034d\7?\2\2\u034d\u00d0\3\2\2\2\u034e"+ + "\u034f\7-\2\2\u034f\u00d2\3\2\2\2\u0350\u0351\7/\2\2\u0351\u00d4\3\2\2"+ + "\2\u0352\u0353\7,\2\2\u0353\u00d6\3\2\2\2\u0354\u0355\7\61\2\2\u0355\u00d8"+ + "\3\2\2\2\u0356\u0357\7\'\2\2\u0357\u00da\3\2\2\2\u0358\u0359\7~\2\2\u0359"+ + "\u035a\7~\2\2\u035a\u00dc\3\2\2\2\u035b\u035c\7\60\2\2\u035c\u00de\3\2"+ + "\2\2\u035d\u035e\7A\2\2\u035e\u00e0\3\2\2\2\u035f\u0365\7)\2\2\u0360\u0364"+ + "\n\2\2\2\u0361\u0362\7)\2\2\u0362\u0364\7)\2\2\u0363\u0360\3\2\2\2\u0363"+ + "\u0361\3\2\2\2\u0364\u0367\3\2\2\2\u0365\u0363\3\2\2\2\u0365\u0366\3\2"+ + "\2\2\u0366\u0368\3\2\2\2\u0367\u0365\3\2\2\2\u0368\u0369\7)\2\2\u0369"+ + "\u00e2\3\2\2\2\u036a\u036c\5\u00f3z\2\u036b\u036a\3\2\2\2\u036c\u036d"+ + "\3\2\2\2\u036d\u036b\3\2\2\2\u036d\u036e\3\2\2\2\u036e\u00e4\3\2\2\2\u036f"+ + "\u0371\5\u00f3z\2\u0370\u036f\3\2\2\2\u0371\u0372\3\2\2\2\u0372\u0370"+ + "\3\2\2\2\u0372\u0373\3\2\2\2\u0373\u0374\3\2\2\2\u0374\u0378\5\u00ddo"+ + "\2\u0375\u0377\5\u00f3z\2\u0376\u0375\3\2\2\2\u0377\u037a\3\2\2\2\u0378"+ + "\u0376\3\2\2\2\u0378\u0379\3\2\2\2\u0379\u039a\3\2\2\2\u037a\u0378\3\2"+ + "\2\2\u037b\u037d\5\u00ddo\2\u037c\u037e\5\u00f3z\2\u037d\u037c\3\2\2\2"+ + "\u037e\u037f\3\2\2\2\u037f\u037d\3\2\2\2\u037f\u0380\3\2\2\2\u0380\u039a"+ + "\3\2\2\2\u0381\u0383\5\u00f3z\2\u0382\u0381\3\2\2\2\u0383\u0384\3\2\2"+ + "\2\u0384\u0382\3\2\2\2\u0384\u0385\3\2\2\2\u0385\u038d\3\2\2\2\u0386\u038a"+ + "\5\u00ddo\2\u0387\u0389\5\u00f3z\2\u0388\u0387\3\2\2\2\u0389\u038c\3\2"+ + "\2\2\u038a\u0388\3\2\2\2\u038a\u038b\3\2\2\2\u038b\u038e\3\2\2\2\u038c"+ + "\u038a\3\2\2\2\u038d\u0386\3\2\2\2\u038d\u038e\3\2\2\2\u038e\u038f\3\2"+ + "\2\2\u038f\u0390\5\u00f1y\2\u0390\u039a\3\2\2\2\u0391\u0393\5\u00ddo\2"+ + "\u0392\u0394\5\u00f3z\2\u0393\u0392\3\2\2\2\u0394\u0395\3\2\2\2\u0395"+ + "\u0393\3\2\2\2\u0395\u0396\3\2\2\2\u0396\u0397\3\2\2\2\u0397\u0398\5\u00f1"+ + "y\2\u0398\u039a\3\2\2\2\u0399\u0370\3\2\2\2\u0399\u037b\3\2\2\2\u0399"+ + "\u0382\3\2\2\2\u0399\u0391\3\2\2\2\u039a\u00e6\3\2\2\2\u039b\u039e\5\u00f5"+ + "{\2\u039c\u039e\7a\2\2\u039d\u039b\3\2\2\2\u039d\u039c\3\2\2\2\u039e\u03a4"+ + "\3\2\2\2\u039f\u03a3\5\u00f5{\2\u03a0\u03a3\5\u00f3z\2\u03a1\u03a3\t\3"+ + "\2\2\u03a2\u039f\3\2\2\2\u03a2\u03a0\3\2\2\2\u03a2\u03a1\3\2\2\2\u03a3"+ + "\u03a6\3\2\2\2\u03a4\u03a2\3\2\2\2\u03a4\u03a5\3\2\2\2\u03a5\u00e8\3\2"+ + "\2\2\u03a6\u03a4\3\2\2\2\u03a7\u03ab\5\u00f3z\2\u03a8\u03ac\5\u00f5{\2"+ + "\u03a9\u03ac\5\u00f3z\2\u03aa\u03ac\t\4\2\2\u03ab\u03a8\3\2\2\2\u03ab"+ + "\u03a9\3\2\2\2\u03ab\u03aa\3\2\2\2\u03ac\u03ad\3\2\2\2\u03ad\u03ab\3\2"+ + "\2\2\u03ad\u03ae\3\2\2\2\u03ae\u00ea\3\2\2\2\u03af\u03b3\5\u00f5{\2\u03b0"+ + "\u03b3\5\u00f3z\2\u03b1\u03b3\7a\2\2\u03b2\u03af\3\2\2\2\u03b2\u03b0\3"+ + "\2\2\2\u03b2\u03b1\3\2\2\2\u03b3\u03b4\3\2\2\2\u03b4\u03b2\3\2\2\2\u03b4"+ + "\u03b5\3\2\2\2\u03b5\u00ec\3\2\2\2\u03b6\u03bc\7$\2\2\u03b7\u03bb\n\5"+ + "\2\2\u03b8\u03b9\7$\2\2\u03b9\u03bb\7$\2\2\u03ba\u03b7\3\2\2\2\u03ba\u03b8"+ + "\3\2\2\2\u03bb\u03be\3\2\2\2\u03bc\u03ba\3\2\2\2\u03bc\u03bd\3\2\2\2\u03bd"+ + "\u03bf\3\2\2\2\u03be\u03bc\3\2\2\2\u03bf\u03c0\7$\2\2\u03c0\u00ee\3\2"+ + "\2\2\u03c1\u03c7\7b\2\2\u03c2\u03c6\n\6\2\2\u03c3\u03c4\7b\2\2\u03c4\u03c6"+ + "\7b\2\2\u03c5\u03c2\3\2\2\2\u03c5\u03c3\3\2\2\2\u03c6\u03c9\3\2\2\2\u03c7"+ + "\u03c5\3\2\2\2\u03c7\u03c8\3\2\2\2\u03c8\u03ca\3\2\2\2\u03c9\u03c7\3\2"+ + "\2\2\u03ca\u03cb\7b\2\2\u03cb\u00f0\3\2\2\2\u03cc\u03ce\7G\2\2\u03cd\u03cf"+ + "\t\7\2\2\u03ce\u03cd\3\2\2\2\u03ce\u03cf\3\2\2\2\u03cf\u03d1\3\2\2\2\u03d0"+ + "\u03d2\5\u00f3z\2\u03d1\u03d0\3\2\2\2\u03d2\u03d3\3\2\2\2\u03d3\u03d1"+ + "\3\2\2\2\u03d3\u03d4\3\2\2\2\u03d4\u00f2\3\2\2\2\u03d5\u03d6\t\b\2\2\u03d6"+ + "\u00f4\3\2\2\2\u03d7\u03d8\t\t\2\2\u03d8\u00f6\3\2\2\2\u03d9\u03da\7/"+ + "\2\2\u03da\u03db\7/\2\2\u03db\u03df\3\2\2\2\u03dc\u03de\n\n\2\2\u03dd"+ + "\u03dc\3\2\2\2\u03de\u03e1\3\2\2\2\u03df\u03dd\3\2\2\2\u03df\u03e0\3\2"+ + "\2\2\u03e0\u03e3\3\2\2\2\u03e1\u03df\3\2\2\2\u03e2\u03e4\7\17\2\2\u03e3"+ + "\u03e2\3\2\2\2\u03e3\u03e4\3\2\2\2\u03e4\u03e6\3\2\2\2\u03e5\u03e7\7\f"+ + "\2\2\u03e6\u03e5\3\2\2\2\u03e6\u03e7\3\2\2\2\u03e7\u03e8\3\2\2\2\u03e8"+ + "\u03e9\b|\2\2\u03e9\u00f8\3\2\2\2\u03ea\u03eb\7\61\2\2\u03eb\u03ec\7,"+ + "\2\2\u03ec\u03f1\3\2\2\2\u03ed\u03f0\5\u00f9}\2\u03ee\u03f0\13\2\2\2\u03ef"+ + "\u03ed\3\2\2\2\u03ef\u03ee\3\2\2\2\u03f0\u03f3\3\2\2\2\u03f1\u03f2\3\2"+ + "\2\2\u03f1\u03ef\3\2\2\2\u03f2\u03f4\3\2\2\2\u03f3\u03f1\3\2\2\2\u03f4"+ + "\u03f5\7,\2\2\u03f5\u03f6\7\61\2\2\u03f6\u03f7\3\2\2\2\u03f7\u03f8\b}"+ + "\2\2\u03f8\u00fa\3\2\2\2\u03f9\u03fb\t\13\2\2\u03fa\u03f9\3\2\2\2\u03fb"+ + "\u03fc\3\2\2\2\u03fc\u03fa\3\2\2\2\u03fc\u03fd\3\2\2\2\u03fd\u03fe\3\2"+ + "\2\2\u03fe\u03ff\b~\2\2\u03ff\u00fc\3\2\2\2\u0400\u0401\13\2\2\2\u0401"+ + "\u00fe\3\2\2\2\"\2\u0342\u0363\u0365\u036d\u0372\u0378\u037f\u0384\u038a"+ + "\u038d\u0395\u0399\u039d\u03a2\u03a4\u03ab\u03ad\u03b2\u03b4\u03ba\u03bc"+ + "\u03c5\u03c7\u03ce\u03d3\u03df\u03e3\u03e6\u03ef\u03f1\u03fc\3\2\3\2"; public static final ATN _ATN = new ATNDeserializer().deserialize(_serializedATN.toCharArray()); static { diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/parser/SqlBaseListener.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/parser/SqlBaseListener.java index 4e449809cfea4..2086c069f99c0 100644 --- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/parser/SqlBaseListener.java +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/parser/SqlBaseListener.java @@ -785,6 +785,18 @@ interface SqlBaseListener extends ParseTreeListener { * @param ctx the parse tree */ void exitNullLiteral(SqlBaseParser.NullLiteralContext ctx); + /** + * Enter a parse tree produced by the {@code intervalLiteral} + * labeled alternative in {@link SqlBaseParser#constant}. + * @param ctx the parse tree + */ + void enterIntervalLiteral(SqlBaseParser.IntervalLiteralContext ctx); + /** + * Exit a parse tree produced by the {@code intervalLiteral} + * labeled alternative in {@link SqlBaseParser#constant}. + * @param ctx the parse tree + */ + void exitIntervalLiteral(SqlBaseParser.IntervalLiteralContext ctx); /** * Enter a parse tree produced by the {@code numericLiteral} * labeled alternative in {@link SqlBaseParser#constant}. @@ -901,6 +913,36 @@ interface SqlBaseListener extends ParseTreeListener { * @param ctx the parse tree */ void exitBooleanValue(SqlBaseParser.BooleanValueContext ctx); + /** + * Enter a parse tree produced by {@link SqlBaseParser#interval}. + * @param ctx the parse tree + */ + void enterInterval(SqlBaseParser.IntervalContext ctx); + /** + * Exit a parse tree produced by {@link SqlBaseParser#interval}. + * @param ctx the parse tree + */ + void exitInterval(SqlBaseParser.IntervalContext ctx); + /** + * Enter a parse tree produced by {@link SqlBaseParser#intervalValue}. + * @param ctx the parse tree + */ + void enterIntervalValue(SqlBaseParser.IntervalValueContext ctx); + /** + * Exit a parse tree produced by {@link SqlBaseParser#intervalValue}. + * @param ctx the parse tree + */ + void exitIntervalValue(SqlBaseParser.IntervalValueContext ctx); + /** + * Enter a parse tree produced by {@link SqlBaseParser#intervalField}. + * @param ctx the parse tree + */ + void enterIntervalField(SqlBaseParser.IntervalFieldContext ctx); + /** + * Exit a parse tree produced by {@link SqlBaseParser#intervalField}. + * @param ctx the parse tree + */ + void exitIntervalField(SqlBaseParser.IntervalFieldContext ctx); /** * Enter a parse tree produced by the {@code primitiveDataType} * labeled alternative in {@link SqlBaseParser#dataType}. diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/parser/SqlBaseParser.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/parser/SqlBaseParser.java index 4e25bd18dc90f..6abaeceb633f8 100644 --- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/parser/SqlBaseParser.java +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/parser/SqlBaseParser.java @@ -19,20 +19,23 @@ class SqlBaseParser extends Parser { public static final int T__0=1, T__1=2, T__2=3, T__3=4, ALL=5, ANALYZE=6, ANALYZED=7, AND=8, ANY=9, AS=10, ASC=11, BETWEEN=12, BY=13, CAST=14, CATALOG=15, CATALOGS=16, COLUMNS=17, - CONVERT=18, DEBUG=19, DESC=20, DESCRIBE=21, DISTINCT=22, ESCAPE=23, EXECUTABLE=24, - EXISTS=25, EXPLAIN=26, EXTRACT=27, FALSE=28, FIRST=29, FORMAT=30, FROM=31, - FULL=32, FUNCTIONS=33, GRAPHVIZ=34, GROUP=35, HAVING=36, IN=37, INNER=38, - IS=39, JOIN=40, LAST=41, LEFT=42, LIKE=43, LIMIT=44, MAPPED=45, MATCH=46, - NATURAL=47, NOT=48, NULL=49, NULLS=50, ON=51, OPTIMIZED=52, OR=53, ORDER=54, - OUTER=55, PARSED=56, PHYSICAL=57, PLAN=58, RIGHT=59, RLIKE=60, QUERY=61, - SCHEMAS=62, SELECT=63, SHOW=64, SYS=65, TABLE=66, TABLES=67, TEXT=68, - TRUE=69, TYPE=70, TYPES=71, USING=72, VERIFY=73, WHERE=74, WITH=75, ESCAPE_ESC=76, - FUNCTION_ESC=77, LIMIT_ESC=78, DATE_ESC=79, TIME_ESC=80, TIMESTAMP_ESC=81, - GUID_ESC=82, ESC_END=83, EQ=84, NEQ=85, LT=86, LTE=87, GT=88, GTE=89, - PLUS=90, MINUS=91, ASTERISK=92, SLASH=93, PERCENT=94, CONCAT=95, DOT=96, - PARAM=97, STRING=98, INTEGER_VALUE=99, DECIMAL_VALUE=100, IDENTIFIER=101, - DIGIT_IDENTIFIER=102, TABLE_IDENTIFIER=103, QUOTED_IDENTIFIER=104, BACKQUOTED_IDENTIFIER=105, - SIMPLE_COMMENT=106, BRACKETED_COMMENT=107, WS=108, UNRECOGNIZED=109, DELIMITER=110; + CONVERT=18, DAY=19, DAYS=20, DEBUG=21, DESC=22, DESCRIBE=23, DISTINCT=24, + ESCAPE=25, EXECUTABLE=26, EXISTS=27, EXPLAIN=28, EXTRACT=29, FALSE=30, + FIRST=31, FORMAT=32, FROM=33, FULL=34, FUNCTIONS=35, GRAPHVIZ=36, GROUP=37, + HAVING=38, HOUR=39, HOURS=40, IN=41, INNER=42, INTERVAL=43, IS=44, JOIN=45, + LAST=46, LEFT=47, LIKE=48, LIMIT=49, MAPPED=50, MATCH=51, MINUTE=52, MINUTES=53, + MONTH=54, MONTHS=55, NATURAL=56, NOT=57, NULL=58, NULLS=59, ON=60, OPTIMIZED=61, + OR=62, ORDER=63, OUTER=64, PARSED=65, PHYSICAL=66, PLAN=67, RIGHT=68, + RLIKE=69, QUERY=70, SCHEMAS=71, SECOND=72, SECONDS=73, SELECT=74, SHOW=75, + SYS=76, TABLE=77, TABLES=78, TEXT=79, TRUE=80, TO=81, TYPE=82, TYPES=83, + USING=84, VERIFY=85, WHERE=86, WITH=87, YEAR=88, YEARS=89, ESCAPE_ESC=90, + FUNCTION_ESC=91, LIMIT_ESC=92, DATE_ESC=93, TIME_ESC=94, TIMESTAMP_ESC=95, + GUID_ESC=96, ESC_END=97, EQ=98, NEQ=99, LT=100, LTE=101, GT=102, GTE=103, + PLUS=104, MINUS=105, ASTERISK=106, SLASH=107, PERCENT=108, CONCAT=109, + DOT=110, PARAM=111, STRING=112, INTEGER_VALUE=113, DECIMAL_VALUE=114, + IDENTIFIER=115, DIGIT_IDENTIFIER=116, TABLE_IDENTIFIER=117, QUOTED_IDENTIFIER=118, + BACKQUOTED_IDENTIFIER=119, SIMPLE_COMMENT=120, BRACKETED_COMMENT=121, + WS=122, UNRECOGNIZED=123, DELIMITER=124; public static final int RULE_singleStatement = 0, RULE_singleExpression = 1, RULE_statement = 2, RULE_query = 3, RULE_queryNoWith = 4, RULE_limitClause = 5, RULE_queryTerm = 6, @@ -46,10 +49,11 @@ class SqlBaseParser extends Parser { RULE_castExpression = 31, RULE_castTemplate = 32, RULE_convertTemplate = 33, RULE_extractExpression = 34, RULE_extractTemplate = 35, RULE_functionExpression = 36, RULE_functionTemplate = 37, RULE_functionName = 38, RULE_constant = 39, - RULE_comparisonOperator = 40, RULE_booleanValue = 41, RULE_dataType = 42, - RULE_qualifiedName = 43, RULE_identifier = 44, RULE_tableIdentifier = 45, - RULE_quoteIdentifier = 46, RULE_unquoteIdentifier = 47, RULE_number = 48, - RULE_string = 49, RULE_nonReserved = 50; + RULE_comparisonOperator = 40, RULE_booleanValue = 41, RULE_interval = 42, + RULE_intervalValue = 43, RULE_intervalField = 44, RULE_dataType = 45, + RULE_qualifiedName = 46, RULE_identifier = 47, RULE_tableIdentifier = 48, + RULE_quoteIdentifier = 49, RULE_unquoteIdentifier = 50, RULE_number = 51, + RULE_string = 52, RULE_nonReserved = 53; public static final String[] ruleNames = { "singleStatement", "singleExpression", "statement", "query", "queryNoWith", "limitClause", "queryTerm", "orderBy", "querySpecification", "fromClause", @@ -60,42 +64,47 @@ class SqlBaseParser extends Parser { "valueExpression", "primaryExpression", "castExpression", "castTemplate", "convertTemplate", "extractExpression", "extractTemplate", "functionExpression", "functionTemplate", "functionName", "constant", "comparisonOperator", - "booleanValue", "dataType", "qualifiedName", "identifier", "tableIdentifier", - "quoteIdentifier", "unquoteIdentifier", "number", "string", "nonReserved" + "booleanValue", "interval", "intervalValue", "intervalField", "dataType", + "qualifiedName", "identifier", "tableIdentifier", "quoteIdentifier", "unquoteIdentifier", + "number", "string", "nonReserved" }; private static final String[] _LITERAL_NAMES = { null, "'('", "')'", "','", "':'", "'ALL'", "'ANALYZE'", "'ANALYZED'", "'AND'", "'ANY'", "'AS'", "'ASC'", "'BETWEEN'", "'BY'", "'CAST'", "'CATALOG'", - "'CATALOGS'", "'COLUMNS'", "'CONVERT'", "'DEBUG'", "'DESC'", "'DESCRIBE'", - "'DISTINCT'", "'ESCAPE'", "'EXECUTABLE'", "'EXISTS'", "'EXPLAIN'", "'EXTRACT'", - "'FALSE'", "'FIRST'", "'FORMAT'", "'FROM'", "'FULL'", "'FUNCTIONS'", "'GRAPHVIZ'", - "'GROUP'", "'HAVING'", "'IN'", "'INNER'", "'IS'", "'JOIN'", "'LAST'", - "'LEFT'", "'LIKE'", "'LIMIT'", "'MAPPED'", "'MATCH'", "'NATURAL'", "'NOT'", - "'NULL'", "'NULLS'", "'ON'", "'OPTIMIZED'", "'OR'", "'ORDER'", "'OUTER'", - "'PARSED'", "'PHYSICAL'", "'PLAN'", "'RIGHT'", "'RLIKE'", "'QUERY'", "'SCHEMAS'", + "'CATALOGS'", "'COLUMNS'", "'CONVERT'", "'DAY'", "'DAYS'", "'DEBUG'", + "'DESC'", "'DESCRIBE'", "'DISTINCT'", "'ESCAPE'", "'EXECUTABLE'", "'EXISTS'", + "'EXPLAIN'", "'EXTRACT'", "'FALSE'", "'FIRST'", "'FORMAT'", "'FROM'", + "'FULL'", "'FUNCTIONS'", "'GRAPHVIZ'", "'GROUP'", "'HAVING'", "'HOUR'", + "'HOURS'", "'IN'", "'INNER'", "'INTERVAL'", "'IS'", "'JOIN'", "'LAST'", + "'LEFT'", "'LIKE'", "'LIMIT'", "'MAPPED'", "'MATCH'", "'MINUTE'", "'MINUTES'", + "'MONTH'", "'MONTHS'", "'NATURAL'", "'NOT'", "'NULL'", "'NULLS'", "'ON'", + "'OPTIMIZED'", "'OR'", "'ORDER'", "'OUTER'", "'PARSED'", "'PHYSICAL'", + "'PLAN'", "'RIGHT'", "'RLIKE'", "'QUERY'", "'SCHEMAS'", "'SECOND'", "'SECONDS'", "'SELECT'", "'SHOW'", "'SYS'", "'TABLE'", "'TABLES'", "'TEXT'", "'TRUE'", - "'TYPE'", "'TYPES'", "'USING'", "'VERIFY'", "'WHERE'", "'WITH'", "'{ESCAPE'", - "'{FN'", "'{LIMIT'", "'{D'", "'{T'", "'{TS'", "'{GUID'", "'}'", "'='", - null, "'<'", "'<='", "'>'", "'>='", "'+'", "'-'", "'*'", "'/'", "'%'", - "'||'", "'.'", "'?'" + "'TO'", "'TYPE'", "'TYPES'", "'USING'", "'VERIFY'", "'WHERE'", "'WITH'", + "'YEAR'", "'YEARS'", "'{ESCAPE'", "'{FN'", "'{LIMIT'", "'{D'", "'{T'", + "'{TS'", "'{GUID'", "'}'", "'='", null, "'<'", "'<='", "'>'", "'>='", + "'+'", "'-'", "'*'", "'/'", "'%'", "'||'", "'.'", "'?'" }; private static final String[] _SYMBOLIC_NAMES = { null, null, null, null, null, "ALL", "ANALYZE", "ANALYZED", "AND", "ANY", "AS", "ASC", "BETWEEN", "BY", "CAST", "CATALOG", "CATALOGS", "COLUMNS", - "CONVERT", "DEBUG", "DESC", "DESCRIBE", "DISTINCT", "ESCAPE", "EXECUTABLE", - "EXISTS", "EXPLAIN", "EXTRACT", "FALSE", "FIRST", "FORMAT", "FROM", "FULL", - "FUNCTIONS", "GRAPHVIZ", "GROUP", "HAVING", "IN", "INNER", "IS", "JOIN", - "LAST", "LEFT", "LIKE", "LIMIT", "MAPPED", "MATCH", "NATURAL", "NOT", - "NULL", "NULLS", "ON", "OPTIMIZED", "OR", "ORDER", "OUTER", "PARSED", - "PHYSICAL", "PLAN", "RIGHT", "RLIKE", "QUERY", "SCHEMAS", "SELECT", "SHOW", - "SYS", "TABLE", "TABLES", "TEXT", "TRUE", "TYPE", "TYPES", "USING", "VERIFY", - "WHERE", "WITH", "ESCAPE_ESC", "FUNCTION_ESC", "LIMIT_ESC", "DATE_ESC", - "TIME_ESC", "TIMESTAMP_ESC", "GUID_ESC", "ESC_END", "EQ", "NEQ", "LT", - "LTE", "GT", "GTE", "PLUS", "MINUS", "ASTERISK", "SLASH", "PERCENT", "CONCAT", - "DOT", "PARAM", "STRING", "INTEGER_VALUE", "DECIMAL_VALUE", "IDENTIFIER", - "DIGIT_IDENTIFIER", "TABLE_IDENTIFIER", "QUOTED_IDENTIFIER", "BACKQUOTED_IDENTIFIER", - "SIMPLE_COMMENT", "BRACKETED_COMMENT", "WS", "UNRECOGNIZED", "DELIMITER" + "CONVERT", "DAY", "DAYS", "DEBUG", "DESC", "DESCRIBE", "DISTINCT", "ESCAPE", + "EXECUTABLE", "EXISTS", "EXPLAIN", "EXTRACT", "FALSE", "FIRST", "FORMAT", + "FROM", "FULL", "FUNCTIONS", "GRAPHVIZ", "GROUP", "HAVING", "HOUR", "HOURS", + "IN", "INNER", "INTERVAL", "IS", "JOIN", "LAST", "LEFT", "LIKE", "LIMIT", + "MAPPED", "MATCH", "MINUTE", "MINUTES", "MONTH", "MONTHS", "NATURAL", + "NOT", "NULL", "NULLS", "ON", "OPTIMIZED", "OR", "ORDER", "OUTER", "PARSED", + "PHYSICAL", "PLAN", "RIGHT", "RLIKE", "QUERY", "SCHEMAS", "SECOND", "SECONDS", + "SELECT", "SHOW", "SYS", "TABLE", "TABLES", "TEXT", "TRUE", "TO", "TYPE", + "TYPES", "USING", "VERIFY", "WHERE", "WITH", "YEAR", "YEARS", "ESCAPE_ESC", + "FUNCTION_ESC", "LIMIT_ESC", "DATE_ESC", "TIME_ESC", "TIMESTAMP_ESC", + "GUID_ESC", "ESC_END", "EQ", "NEQ", "LT", "LTE", "GT", "GTE", "PLUS", + "MINUS", "ASTERISK", "SLASH", "PERCENT", "CONCAT", "DOT", "PARAM", "STRING", + "INTEGER_VALUE", "DECIMAL_VALUE", "IDENTIFIER", "DIGIT_IDENTIFIER", "TABLE_IDENTIFIER", + "QUOTED_IDENTIFIER", "BACKQUOTED_IDENTIFIER", "SIMPLE_COMMENT", "BRACKETED_COMMENT", + "WS", "UNRECOGNIZED", "DELIMITER" }; public static final Vocabulary VOCABULARY = new VocabularyImpl(_LITERAL_NAMES, _SYMBOLIC_NAMES); @@ -176,9 +185,9 @@ public final SingleStatementContext singleStatement() throws RecognitionExceptio try { enterOuterAlt(_localctx, 1); { - setState(102); + setState(108); statement(); - setState(103); + setState(109); match(EOF); } } @@ -223,9 +232,9 @@ public final SingleExpressionContext singleExpression() throws RecognitionExcept try { enterOuterAlt(_localctx, 1); { - setState(105); + setState(111); expression(); - setState(106); + setState(112); match(EOF); } } @@ -619,14 +628,14 @@ public final StatementContext statement() throws RecognitionException { enterRule(_localctx, 4, RULE_statement); int _la; try { - setState(208); + setState(214); _errHandler.sync(this); switch ( getInterpreter().adaptivePredict(_input,17,_ctx) ) { case 1: _localctx = new StatementDefaultContext(_localctx); enterOuterAlt(_localctx, 1); { - setState(108); + setState(114); query(); } break; @@ -634,30 +643,30 @@ public final StatementContext statement() throws RecognitionException { _localctx = new ExplainContext(_localctx); enterOuterAlt(_localctx, 2); { - setState(109); + setState(115); match(EXPLAIN); - setState(123); + setState(129); _errHandler.sync(this); switch ( getInterpreter().adaptivePredict(_input,2,_ctx) ) { case 1: { - setState(110); + setState(116); match(T__0); - setState(119); + setState(125); _errHandler.sync(this); _la = _input.LA(1); - while (((((_la - 30)) & ~0x3f) == 0 && ((1L << (_la - 30)) & ((1L << (FORMAT - 30)) | (1L << (PLAN - 30)) | (1L << (VERIFY - 30)))) != 0)) { + while (((((_la - 32)) & ~0x3f) == 0 && ((1L << (_la - 32)) & ((1L << (FORMAT - 32)) | (1L << (PLAN - 32)) | (1L << (VERIFY - 32)))) != 0)) { { - setState(117); + setState(123); switch (_input.LA(1)) { case PLAN: { - setState(111); + setState(117); match(PLAN); - setState(112); + setState(118); ((ExplainContext)_localctx).type = _input.LT(1); _la = _input.LA(1); - if ( !((((_la) & ~0x3f) == 0 && ((1L << _la) & ((1L << ALL) | (1L << ANALYZED) | (1L << EXECUTABLE) | (1L << MAPPED) | (1L << OPTIMIZED) | (1L << PARSED))) != 0)) ) { + if ( !(((((_la - 5)) & ~0x3f) == 0 && ((1L << (_la - 5)) & ((1L << (ALL - 5)) | (1L << (ANALYZED - 5)) | (1L << (EXECUTABLE - 5)) | (1L << (MAPPED - 5)) | (1L << (OPTIMIZED - 5)) | (1L << (PARSED - 5)))) != 0)) ) { ((ExplainContext)_localctx).type = (Token)_errHandler.recoverInline(this); } else { consume(); @@ -666,9 +675,9 @@ public final StatementContext statement() throws RecognitionException { break; case FORMAT: { - setState(113); + setState(119); match(FORMAT); - setState(114); + setState(120); ((ExplainContext)_localctx).format = _input.LT(1); _la = _input.LA(1); if ( !(_la==GRAPHVIZ || _la==TEXT) ) { @@ -680,9 +689,9 @@ public final StatementContext statement() throws RecognitionException { break; case VERIFY: { - setState(115); + setState(121); match(VERIFY); - setState(116); + setState(122); ((ExplainContext)_localctx).verify = booleanValue(); } break; @@ -690,16 +699,16 @@ public final StatementContext statement() throws RecognitionException { throw new NoViableAltException(this); } } - setState(121); + setState(127); _errHandler.sync(this); _la = _input.LA(1); } - setState(122); + setState(128); match(T__1); } break; } - setState(125); + setState(131); statement(); } break; @@ -707,27 +716,27 @@ public final StatementContext statement() throws RecognitionException { _localctx = new DebugContext(_localctx); enterOuterAlt(_localctx, 3); { - setState(126); + setState(132); match(DEBUG); - setState(138); + setState(144); _errHandler.sync(this); switch ( getInterpreter().adaptivePredict(_input,5,_ctx) ) { case 1: { - setState(127); + setState(133); match(T__0); - setState(134); + setState(140); _errHandler.sync(this); _la = _input.LA(1); while (_la==FORMAT || _la==PLAN) { { - setState(132); + setState(138); switch (_input.LA(1)) { case PLAN: { - setState(128); + setState(134); match(PLAN); - setState(129); + setState(135); ((DebugContext)_localctx).type = _input.LT(1); _la = _input.LA(1); if ( !(_la==ANALYZED || _la==OPTIMIZED) ) { @@ -739,9 +748,9 @@ public final StatementContext statement() throws RecognitionException { break; case FORMAT: { - setState(130); + setState(136); match(FORMAT); - setState(131); + setState(137); ((DebugContext)_localctx).format = _input.LT(1); _la = _input.LA(1); if ( !(_la==GRAPHVIZ || _la==TEXT) ) { @@ -755,16 +764,16 @@ public final StatementContext statement() throws RecognitionException { throw new NoViableAltException(this); } } - setState(136); + setState(142); _errHandler.sync(this); _la = _input.LA(1); } - setState(137); + setState(143); match(T__1); } break; } - setState(140); + setState(146); statement(); } break; @@ -772,15 +781,15 @@ public final StatementContext statement() throws RecognitionException { _localctx = new ShowTablesContext(_localctx); enterOuterAlt(_localctx, 4); { - setState(141); + setState(147); match(SHOW); - setState(142); + setState(148); match(TABLES); - setState(145); + setState(151); switch (_input.LA(1)) { case LIKE: { - setState(143); + setState(149); ((ShowTablesContext)_localctx).tableLike = likePattern(); } break; @@ -788,13 +797,21 @@ public final StatementContext statement() throws RecognitionException { case ANALYZED: case CATALOGS: case COLUMNS: + case DAY: case DEBUG: case EXECUTABLE: case EXPLAIN: + case FIRST: case FORMAT: case FUNCTIONS: case GRAPHVIZ: + case HOUR: + case INTERVAL: + case LAST: + case LIMIT: case MAPPED: + case MINUTE: + case MONTH: case OPTIMIZED: case PARSED: case PHYSICAL: @@ -802,6 +819,7 @@ public final StatementContext statement() throws RecognitionException { case RLIKE: case QUERY: case SCHEMAS: + case SECOND: case SHOW: case SYS: case TABLES: @@ -809,13 +827,14 @@ public final StatementContext statement() throws RecognitionException { case TYPE: case TYPES: case VERIFY: + case YEAR: case IDENTIFIER: case DIGIT_IDENTIFIER: case TABLE_IDENTIFIER: case QUOTED_IDENTIFIER: case BACKQUOTED_IDENTIFIER: { - setState(144); + setState(150); ((ShowTablesContext)_localctx).tableIdent = tableIdentifier(); } break; @@ -830,22 +849,22 @@ public final StatementContext statement() throws RecognitionException { _localctx = new ShowColumnsContext(_localctx); enterOuterAlt(_localctx, 5); { - setState(147); + setState(153); match(SHOW); - setState(148); + setState(154); match(COLUMNS); - setState(149); + setState(155); _la = _input.LA(1); if ( !(_la==FROM || _la==IN) ) { _errHandler.recoverInline(this); } else { consume(); } - setState(152); + setState(158); switch (_input.LA(1)) { case LIKE: { - setState(150); + setState(156); ((ShowColumnsContext)_localctx).tableLike = likePattern(); } break; @@ -853,13 +872,21 @@ public final StatementContext statement() throws RecognitionException { case ANALYZED: case CATALOGS: case COLUMNS: + case DAY: case DEBUG: case EXECUTABLE: case EXPLAIN: + case FIRST: case FORMAT: case FUNCTIONS: case GRAPHVIZ: + case HOUR: + case INTERVAL: + case LAST: + case LIMIT: case MAPPED: + case MINUTE: + case MONTH: case OPTIMIZED: case PARSED: case PHYSICAL: @@ -867,6 +894,7 @@ public final StatementContext statement() throws RecognitionException { case RLIKE: case QUERY: case SCHEMAS: + case SECOND: case SHOW: case SYS: case TABLES: @@ -874,13 +902,14 @@ public final StatementContext statement() throws RecognitionException { case TYPE: case TYPES: case VERIFY: + case YEAR: case IDENTIFIER: case DIGIT_IDENTIFIER: case TABLE_IDENTIFIER: case QUOTED_IDENTIFIER: case BACKQUOTED_IDENTIFIER: { - setState(151); + setState(157); ((ShowColumnsContext)_localctx).tableIdent = tableIdentifier(); } break; @@ -893,18 +922,18 @@ public final StatementContext statement() throws RecognitionException { _localctx = new ShowColumnsContext(_localctx); enterOuterAlt(_localctx, 6); { - setState(154); + setState(160); _la = _input.LA(1); if ( !(_la==DESC || _la==DESCRIBE) ) { _errHandler.recoverInline(this); } else { consume(); } - setState(157); + setState(163); switch (_input.LA(1)) { case LIKE: { - setState(155); + setState(161); ((ShowColumnsContext)_localctx).tableLike = likePattern(); } break; @@ -912,13 +941,21 @@ public final StatementContext statement() throws RecognitionException { case ANALYZED: case CATALOGS: case COLUMNS: + case DAY: case DEBUG: case EXECUTABLE: case EXPLAIN: + case FIRST: case FORMAT: case FUNCTIONS: case GRAPHVIZ: + case HOUR: + case INTERVAL: + case LAST: + case LIMIT: case MAPPED: + case MINUTE: + case MONTH: case OPTIMIZED: case PARSED: case PHYSICAL: @@ -926,6 +963,7 @@ public final StatementContext statement() throws RecognitionException { case RLIKE: case QUERY: case SCHEMAS: + case SECOND: case SHOW: case SYS: case TABLES: @@ -933,13 +971,14 @@ public final StatementContext statement() throws RecognitionException { case TYPE: case TYPES: case VERIFY: + case YEAR: case IDENTIFIER: case DIGIT_IDENTIFIER: case TABLE_IDENTIFIER: case QUOTED_IDENTIFIER: case BACKQUOTED_IDENTIFIER: { - setState(156); + setState(162); ((ShowColumnsContext)_localctx).tableIdent = tableIdentifier(); } break; @@ -952,15 +991,15 @@ public final StatementContext statement() throws RecognitionException { _localctx = new ShowFunctionsContext(_localctx); enterOuterAlt(_localctx, 7); { - setState(159); + setState(165); match(SHOW); - setState(160); + setState(166); match(FUNCTIONS); - setState(162); + setState(168); _la = _input.LA(1); if (_la==LIKE) { { - setState(161); + setState(167); likePattern(); } } @@ -971,9 +1010,9 @@ public final StatementContext statement() throws RecognitionException { _localctx = new ShowSchemasContext(_localctx); enterOuterAlt(_localctx, 8); { - setState(164); + setState(170); match(SHOW); - setState(165); + setState(171); match(SCHEMAS); } break; @@ -981,9 +1020,9 @@ public final StatementContext statement() throws RecognitionException { _localctx = new SysCatalogsContext(_localctx); enterOuterAlt(_localctx, 9); { - setState(166); + setState(172); match(SYS); - setState(167); + setState(173); match(CATALOGS); } break; @@ -991,58 +1030,58 @@ public final StatementContext statement() throws RecognitionException { _localctx = new SysTablesContext(_localctx); enterOuterAlt(_localctx, 10); { - setState(168); + setState(174); match(SYS); - setState(169); + setState(175); match(TABLES); - setState(172); + setState(178); _la = _input.LA(1); if (_la==CATALOG) { { - setState(170); + setState(176); match(CATALOG); - setState(171); + setState(177); ((SysTablesContext)_localctx).clusterLike = likePattern(); } } - setState(176); + setState(182); _errHandler.sync(this); switch ( getInterpreter().adaptivePredict(_input,11,_ctx) ) { case 1: { - setState(174); + setState(180); ((SysTablesContext)_localctx).tableLike = likePattern(); } break; case 2: { - setState(175); + setState(181); ((SysTablesContext)_localctx).tableIdent = tableIdentifier(); } break; } - setState(187); + setState(193); _la = _input.LA(1); if (_la==TYPE) { { - setState(178); + setState(184); match(TYPE); - setState(179); + setState(185); string(); - setState(184); + setState(190); _errHandler.sync(this); _la = _input.LA(1); while (_la==T__2) { { { - setState(180); + setState(186); match(T__2); - setState(181); + setState(187); string(); } } - setState(186); + setState(192); _errHandler.sync(this); _la = _input.LA(1); } @@ -1055,28 +1094,28 @@ public final StatementContext statement() throws RecognitionException { _localctx = new SysColumnsContext(_localctx); enterOuterAlt(_localctx, 11); { - setState(189); + setState(195); match(SYS); - setState(190); + setState(196); match(COLUMNS); - setState(193); + setState(199); _la = _input.LA(1); if (_la==CATALOG) { { - setState(191); + setState(197); match(CATALOG); - setState(192); + setState(198); ((SysColumnsContext)_localctx).cluster = string(); } } - setState(198); + setState(204); switch (_input.LA(1)) { case TABLE: { - setState(195); + setState(201); match(TABLE); - setState(196); + setState(202); ((SysColumnsContext)_localctx).tableLike = likePattern(); } break; @@ -1084,13 +1123,21 @@ public final StatementContext statement() throws RecognitionException { case ANALYZED: case CATALOGS: case COLUMNS: + case DAY: case DEBUG: case EXECUTABLE: case EXPLAIN: + case FIRST: case FORMAT: case FUNCTIONS: case GRAPHVIZ: + case HOUR: + case INTERVAL: + case LAST: + case LIMIT: case MAPPED: + case MINUTE: + case MONTH: case OPTIMIZED: case PARSED: case PHYSICAL: @@ -1098,6 +1145,7 @@ public final StatementContext statement() throws RecognitionException { case RLIKE: case QUERY: case SCHEMAS: + case SECOND: case SHOW: case SYS: case TABLES: @@ -1105,13 +1153,14 @@ public final StatementContext statement() throws RecognitionException { case TYPE: case TYPES: case VERIFY: + case YEAR: case IDENTIFIER: case DIGIT_IDENTIFIER: case TABLE_IDENTIFIER: case QUOTED_IDENTIFIER: case BACKQUOTED_IDENTIFIER: { - setState(197); + setState(203); ((SysColumnsContext)_localctx).tableIdent = tableIdentifier(); } break; @@ -1121,11 +1170,11 @@ public final StatementContext statement() throws RecognitionException { default: throw new NoViableAltException(this); } - setState(201); + setState(207); _la = _input.LA(1); if (_la==LIKE) { { - setState(200); + setState(206); ((SysColumnsContext)_localctx).columnPattern = likePattern(); } } @@ -1136,9 +1185,9 @@ public final StatementContext statement() throws RecognitionException { _localctx = new SysTypesContext(_localctx); enterOuterAlt(_localctx, 12); { - setState(203); + setState(209); match(SYS); - setState(204); + setState(210); match(TYPES); } break; @@ -1146,11 +1195,11 @@ public final StatementContext statement() throws RecognitionException { _localctx = new SysTableTypesContext(_localctx); enterOuterAlt(_localctx, 13); { - setState(205); + setState(211); match(SYS); - setState(206); + setState(212); match(TABLE); - setState(207); + setState(213); match(TYPES); } break; @@ -1204,34 +1253,34 @@ public final QueryContext query() throws RecognitionException { try { enterOuterAlt(_localctx, 1); { - setState(219); + setState(225); _la = _input.LA(1); if (_la==WITH) { { - setState(210); + setState(216); match(WITH); - setState(211); + setState(217); namedQuery(); - setState(216); + setState(222); _errHandler.sync(this); _la = _input.LA(1); while (_la==T__2) { { { - setState(212); + setState(218); match(T__2); - setState(213); + setState(219); namedQuery(); } } - setState(218); + setState(224); _errHandler.sync(this); _la = _input.LA(1); } } } - setState(221); + setState(227); queryNoWith(); } } @@ -1287,42 +1336,42 @@ public final QueryNoWithContext queryNoWith() throws RecognitionException { try { enterOuterAlt(_localctx, 1); { - setState(223); + setState(229); queryTerm(); - setState(234); + setState(240); _la = _input.LA(1); if (_la==ORDER) { { - setState(224); + setState(230); match(ORDER); - setState(225); + setState(231); match(BY); - setState(226); + setState(232); orderBy(); - setState(231); + setState(237); _errHandler.sync(this); _la = _input.LA(1); while (_la==T__2) { { { - setState(227); + setState(233); match(T__2); - setState(228); + setState(234); orderBy(); } } - setState(233); + setState(239); _errHandler.sync(this); _la = _input.LA(1); } } } - setState(237); + setState(243); _la = _input.LA(1); if (_la==LIMIT || _la==LIMIT_ESC) { { - setState(236); + setState(242); limitClause(); } } @@ -1371,14 +1420,14 @@ public final LimitClauseContext limitClause() throws RecognitionException { enterRule(_localctx, 10, RULE_limitClause); int _la; try { - setState(244); + setState(250); switch (_input.LA(1)) { case LIMIT: enterOuterAlt(_localctx, 1); { - setState(239); + setState(245); match(LIMIT); - setState(240); + setState(246); ((LimitClauseContext)_localctx).limit = _input.LT(1); _la = _input.LA(1); if ( !(_la==ALL || _la==INTEGER_VALUE) ) { @@ -1391,9 +1440,9 @@ public final LimitClauseContext limitClause() throws RecognitionException { case LIMIT_ESC: enterOuterAlt(_localctx, 2); { - setState(241); + setState(247); match(LIMIT_ESC); - setState(242); + setState(248); ((LimitClauseContext)_localctx).limit = _input.LT(1); _la = _input.LA(1); if ( !(_la==ALL || _la==INTEGER_VALUE) ) { @@ -1401,7 +1450,7 @@ public final LimitClauseContext limitClause() throws RecognitionException { } else { consume(); } - setState(243); + setState(249); match(ESC_END); } break; @@ -1474,13 +1523,13 @@ public final QueryTermContext queryTerm() throws RecognitionException { QueryTermContext _localctx = new QueryTermContext(_ctx, getState()); enterRule(_localctx, 12, RULE_queryTerm); try { - setState(251); + setState(257); switch (_input.LA(1)) { case SELECT: _localctx = new QueryPrimaryDefaultContext(_localctx); enterOuterAlt(_localctx, 1); { - setState(246); + setState(252); querySpecification(); } break; @@ -1488,11 +1537,11 @@ public final QueryTermContext queryTerm() throws RecognitionException { _localctx = new SubqueryContext(_localctx); enterOuterAlt(_localctx, 2); { - setState(247); + setState(253); match(T__0); - setState(248); + setState(254); queryNoWith(); - setState(249); + setState(255); match(T__1); } break; @@ -1548,13 +1597,13 @@ public final OrderByContext orderBy() throws RecognitionException { try { enterOuterAlt(_localctx, 1); { - setState(253); + setState(259); expression(); - setState(255); + setState(261); _la = _input.LA(1); if (_la==ASC || _la==DESC) { { - setState(254); + setState(260); ((OrderByContext)_localctx).ordering = _input.LT(1); _la = _input.LA(1); if ( !(_la==ASC || _la==DESC) ) { @@ -1565,13 +1614,13 @@ public final OrderByContext orderBy() throws RecognitionException { } } - setState(259); + setState(265); _la = _input.LA(1); if (_la==NULLS) { { - setState(257); + setState(263); match(NULLS); - setState(258); + setState(264); ((OrderByContext)_localctx).nullOrdering = _input.LT(1); _la = _input.LA(1); if ( !(_la==FIRST || _la==LAST) ) { @@ -1650,75 +1699,75 @@ public final QuerySpecificationContext querySpecification() throws RecognitionEx try { enterOuterAlt(_localctx, 1); { - setState(261); + setState(267); match(SELECT); - setState(263); + setState(269); _la = _input.LA(1); if (_la==ALL || _la==DISTINCT) { { - setState(262); + setState(268); setQuantifier(); } } - setState(265); + setState(271); selectItem(); - setState(270); + setState(276); _errHandler.sync(this); _la = _input.LA(1); while (_la==T__2) { { { - setState(266); + setState(272); match(T__2); - setState(267); + setState(273); selectItem(); } } - setState(272); + setState(278); _errHandler.sync(this); _la = _input.LA(1); } - setState(274); + setState(280); _la = _input.LA(1); if (_la==FROM) { { - setState(273); + setState(279); fromClause(); } } - setState(278); + setState(284); _la = _input.LA(1); if (_la==WHERE) { { - setState(276); + setState(282); match(WHERE); - setState(277); + setState(283); ((QuerySpecificationContext)_localctx).where = booleanExpression(0); } } - setState(283); + setState(289); _la = _input.LA(1); if (_la==GROUP) { { - setState(280); + setState(286); match(GROUP); - setState(281); + setState(287); match(BY); - setState(282); + setState(288); groupBy(); } } - setState(287); + setState(293); _la = _input.LA(1); if (_la==HAVING) { { - setState(285); + setState(291); match(HAVING); - setState(286); + setState(292); ((QuerySpecificationContext)_localctx).having = booleanExpression(0); } } @@ -1770,23 +1819,23 @@ public final FromClauseContext fromClause() throws RecognitionException { try { enterOuterAlt(_localctx, 1); { - setState(289); + setState(295); match(FROM); - setState(290); + setState(296); relation(); - setState(295); + setState(301); _errHandler.sync(this); _la = _input.LA(1); while (_la==T__2) { { { - setState(291); + setState(297); match(T__2); - setState(292); + setState(298); relation(); } } - setState(297); + setState(303); _errHandler.sync(this); _la = _input.LA(1); } @@ -1839,30 +1888,30 @@ public final GroupByContext groupBy() throws RecognitionException { try { enterOuterAlt(_localctx, 1); { - setState(299); + setState(305); _la = _input.LA(1); if (_la==ALL || _la==DISTINCT) { { - setState(298); + setState(304); setQuantifier(); } } - setState(301); + setState(307); groupingElement(); - setState(306); + setState(312); _errHandler.sync(this); _la = _input.LA(1); while (_la==T__2) { { { - setState(302); + setState(308); match(T__2); - setState(303); + setState(309); groupingElement(); } } - setState(308); + setState(314); _errHandler.sync(this); _la = _input.LA(1); } @@ -1917,7 +1966,7 @@ public final GroupingElementContext groupingElement() throws RecognitionExceptio _localctx = new SingleGroupingSetContext(_localctx); enterOuterAlt(_localctx, 1); { - setState(309); + setState(315); groupingExpressions(); } } @@ -1963,47 +2012,47 @@ public final GroupingExpressionsContext groupingExpressions() throws Recognition enterRule(_localctx, 24, RULE_groupingExpressions); int _la; try { - setState(324); + setState(330); _errHandler.sync(this); switch ( getInterpreter().adaptivePredict(_input,38,_ctx) ) { case 1: enterOuterAlt(_localctx, 1); { - setState(311); + setState(317); match(T__0); - setState(320); + setState(326); _la = _input.LA(1); - if ((((_la) & ~0x3f) == 0 && ((1L << _la) & ((1L << T__0) | (1L << ANALYZE) | (1L << ANALYZED) | (1L << CAST) | (1L << CATALOGS) | (1L << COLUMNS) | (1L << CONVERT) | (1L << DEBUG) | (1L << EXECUTABLE) | (1L << EXISTS) | (1L << EXPLAIN) | (1L << EXTRACT) | (1L << FALSE) | (1L << FORMAT) | (1L << FUNCTIONS) | (1L << GRAPHVIZ) | (1L << LEFT) | (1L << MAPPED) | (1L << MATCH) | (1L << NOT) | (1L << NULL) | (1L << OPTIMIZED) | (1L << PARSED) | (1L << PHYSICAL) | (1L << PLAN) | (1L << RIGHT) | (1L << RLIKE) | (1L << QUERY) | (1L << SCHEMAS))) != 0) || ((((_la - 64)) & ~0x3f) == 0 && ((1L << (_la - 64)) & ((1L << (SHOW - 64)) | (1L << (SYS - 64)) | (1L << (TABLES - 64)) | (1L << (TEXT - 64)) | (1L << (TRUE - 64)) | (1L << (TYPE - 64)) | (1L << (TYPES - 64)) | (1L << (VERIFY - 64)) | (1L << (FUNCTION_ESC - 64)) | (1L << (DATE_ESC - 64)) | (1L << (TIME_ESC - 64)) | (1L << (TIMESTAMP_ESC - 64)) | (1L << (GUID_ESC - 64)) | (1L << (PLUS - 64)) | (1L << (MINUS - 64)) | (1L << (ASTERISK - 64)) | (1L << (PARAM - 64)) | (1L << (STRING - 64)) | (1L << (INTEGER_VALUE - 64)) | (1L << (DECIMAL_VALUE - 64)) | (1L << (IDENTIFIER - 64)) | (1L << (DIGIT_IDENTIFIER - 64)) | (1L << (QUOTED_IDENTIFIER - 64)) | (1L << (BACKQUOTED_IDENTIFIER - 64)))) != 0)) { + if ((((_la) & ~0x3f) == 0 && ((1L << _la) & ((1L << T__0) | (1L << ANALYZE) | (1L << ANALYZED) | (1L << CAST) | (1L << CATALOGS) | (1L << COLUMNS) | (1L << CONVERT) | (1L << DAY) | (1L << DEBUG) | (1L << EXECUTABLE) | (1L << EXISTS) | (1L << EXPLAIN) | (1L << EXTRACT) | (1L << FALSE) | (1L << FIRST) | (1L << FORMAT) | (1L << FUNCTIONS) | (1L << GRAPHVIZ) | (1L << HOUR) | (1L << INTERVAL) | (1L << LAST) | (1L << LEFT) | (1L << LIMIT) | (1L << MAPPED) | (1L << MATCH) | (1L << MINUTE) | (1L << MONTH) | (1L << NOT) | (1L << NULL) | (1L << OPTIMIZED))) != 0) || ((((_la - 65)) & ~0x3f) == 0 && ((1L << (_la - 65)) & ((1L << (PARSED - 65)) | (1L << (PHYSICAL - 65)) | (1L << (PLAN - 65)) | (1L << (RIGHT - 65)) | (1L << (RLIKE - 65)) | (1L << (QUERY - 65)) | (1L << (SCHEMAS - 65)) | (1L << (SECOND - 65)) | (1L << (SHOW - 65)) | (1L << (SYS - 65)) | (1L << (TABLES - 65)) | (1L << (TEXT - 65)) | (1L << (TRUE - 65)) | (1L << (TYPE - 65)) | (1L << (TYPES - 65)) | (1L << (VERIFY - 65)) | (1L << (YEAR - 65)) | (1L << (FUNCTION_ESC - 65)) | (1L << (DATE_ESC - 65)) | (1L << (TIME_ESC - 65)) | (1L << (TIMESTAMP_ESC - 65)) | (1L << (GUID_ESC - 65)) | (1L << (PLUS - 65)) | (1L << (MINUS - 65)) | (1L << (ASTERISK - 65)) | (1L << (PARAM - 65)) | (1L << (STRING - 65)) | (1L << (INTEGER_VALUE - 65)) | (1L << (DECIMAL_VALUE - 65)) | (1L << (IDENTIFIER - 65)) | (1L << (DIGIT_IDENTIFIER - 65)) | (1L << (QUOTED_IDENTIFIER - 65)) | (1L << (BACKQUOTED_IDENTIFIER - 65)))) != 0)) { { - setState(312); + setState(318); expression(); - setState(317); + setState(323); _errHandler.sync(this); _la = _input.LA(1); while (_la==T__2) { { { - setState(313); + setState(319); match(T__2); - setState(314); + setState(320); expression(); } } - setState(319); + setState(325); _errHandler.sync(this); _la = _input.LA(1); } } } - setState(322); + setState(328); match(T__1); } break; case 2: enterOuterAlt(_localctx, 2); { - setState(323); + setState(329); expression(); } break; @@ -2054,15 +2103,15 @@ public final NamedQueryContext namedQuery() throws RecognitionException { try { enterOuterAlt(_localctx, 1); { - setState(326); + setState(332); ((NamedQueryContext)_localctx).name = identifier(); - setState(327); + setState(333); match(AS); - setState(328); + setState(334); match(T__0); - setState(329); + setState(335); queryNoWith(); - setState(330); + setState(336); match(T__1); } } @@ -2106,7 +2155,7 @@ public final SetQuantifierContext setQuantifier() throws RecognitionException { try { enterOuterAlt(_localctx, 1); { - setState(332); + setState(338); _la = _input.LA(1); if ( !(_la==ALL || _la==DISTINCT) ) { _errHandler.recoverInline(this); @@ -2169,26 +2218,27 @@ public final SelectItemContext selectItem() throws RecognitionException { _localctx = new SelectExpressionContext(_localctx); enterOuterAlt(_localctx, 1); { - setState(334); + setState(340); expression(); - setState(339); - _la = _input.LA(1); - if ((((_la) & ~0x3f) == 0 && ((1L << _la) & ((1L << ANALYZE) | (1L << ANALYZED) | (1L << AS) | (1L << CATALOGS) | (1L << COLUMNS) | (1L << DEBUG) | (1L << EXECUTABLE) | (1L << EXPLAIN) | (1L << FORMAT) | (1L << FUNCTIONS) | (1L << GRAPHVIZ) | (1L << MAPPED) | (1L << OPTIMIZED) | (1L << PARSED) | (1L << PHYSICAL) | (1L << PLAN) | (1L << RLIKE) | (1L << QUERY) | (1L << SCHEMAS))) != 0) || ((((_la - 64)) & ~0x3f) == 0 && ((1L << (_la - 64)) & ((1L << (SHOW - 64)) | (1L << (SYS - 64)) | (1L << (TABLES - 64)) | (1L << (TEXT - 64)) | (1L << (TYPE - 64)) | (1L << (TYPES - 64)) | (1L << (VERIFY - 64)) | (1L << (IDENTIFIER - 64)) | (1L << (DIGIT_IDENTIFIER - 64)) | (1L << (QUOTED_IDENTIFIER - 64)) | (1L << (BACKQUOTED_IDENTIFIER - 64)))) != 0)) { + setState(345); + _errHandler.sync(this); + switch ( getInterpreter().adaptivePredict(_input,40,_ctx) ) { + case 1: { - setState(336); + setState(342); _la = _input.LA(1); if (_la==AS) { { - setState(335); + setState(341); match(AS); } } - setState(338); + setState(344); identifier(); } + break; } - } } catch (RecognitionException re) { @@ -2238,19 +2288,19 @@ public final RelationContext relation() throws RecognitionException { try { enterOuterAlt(_localctx, 1); { - setState(341); + setState(347); relationPrimary(); - setState(345); + setState(351); _errHandler.sync(this); _la = _input.LA(1); - while ((((_la) & ~0x3f) == 0 && ((1L << _la) & ((1L << FULL) | (1L << INNER) | (1L << JOIN) | (1L << LEFT) | (1L << NATURAL) | (1L << RIGHT))) != 0)) { + while (((((_la - 34)) & ~0x3f) == 0 && ((1L << (_la - 34)) & ((1L << (FULL - 34)) | (1L << (INNER - 34)) | (1L << (JOIN - 34)) | (1L << (LEFT - 34)) | (1L << (NATURAL - 34)) | (1L << (RIGHT - 34)))) != 0)) { { { - setState(342); + setState(348); joinRelation(); } } - setState(347); + setState(353); _errHandler.sync(this); _la = _input.LA(1); } @@ -2304,7 +2354,7 @@ public final JoinRelationContext joinRelation() throws RecognitionException { enterRule(_localctx, 34, RULE_joinRelation); int _la; try { - setState(359); + setState(365); switch (_input.LA(1)) { case FULL: case INNER: @@ -2314,18 +2364,18 @@ public final JoinRelationContext joinRelation() throws RecognitionException { enterOuterAlt(_localctx, 1); { { - setState(348); + setState(354); joinType(); } - setState(349); + setState(355); match(JOIN); - setState(350); + setState(356); ((JoinRelationContext)_localctx).right = relationPrimary(); - setState(352); + setState(358); _la = _input.LA(1); if (_la==ON || _la==USING) { { - setState(351); + setState(357); joinCriteria(); } } @@ -2335,13 +2385,13 @@ public final JoinRelationContext joinRelation() throws RecognitionException { case NATURAL: enterOuterAlt(_localctx, 2); { - setState(354); + setState(360); match(NATURAL); - setState(355); + setState(361); joinType(); - setState(356); + setState(362); match(JOIN); - setState(357); + setState(363); ((JoinRelationContext)_localctx).right = relationPrimary(); } break; @@ -2390,17 +2440,17 @@ public final JoinTypeContext joinType() throws RecognitionException { enterRule(_localctx, 36, RULE_joinType); int _la; try { - setState(376); + setState(382); switch (_input.LA(1)) { case INNER: case JOIN: enterOuterAlt(_localctx, 1); { - setState(362); + setState(368); _la = _input.LA(1); if (_la==INNER) { { - setState(361); + setState(367); match(INNER); } } @@ -2410,13 +2460,13 @@ public final JoinTypeContext joinType() throws RecognitionException { case LEFT: enterOuterAlt(_localctx, 2); { - setState(364); + setState(370); match(LEFT); - setState(366); + setState(372); _la = _input.LA(1); if (_la==OUTER) { { - setState(365); + setState(371); match(OUTER); } } @@ -2426,13 +2476,13 @@ public final JoinTypeContext joinType() throws RecognitionException { case RIGHT: enterOuterAlt(_localctx, 3); { - setState(368); + setState(374); match(RIGHT); - setState(370); + setState(376); _la = _input.LA(1); if (_la==OUTER) { { - setState(369); + setState(375); match(OUTER); } } @@ -2442,13 +2492,13 @@ public final JoinTypeContext joinType() throws RecognitionException { case FULL: enterOuterAlt(_localctx, 4); { - setState(372); + setState(378); match(FULL); - setState(374); + setState(380); _la = _input.LA(1); if (_la==OUTER) { { - setState(373); + setState(379); match(OUTER); } } @@ -2506,43 +2556,43 @@ public final JoinCriteriaContext joinCriteria() throws RecognitionException { enterRule(_localctx, 38, RULE_joinCriteria); int _la; try { - setState(392); + setState(398); switch (_input.LA(1)) { case ON: enterOuterAlt(_localctx, 1); { - setState(378); + setState(384); match(ON); - setState(379); + setState(385); booleanExpression(0); } break; case USING: enterOuterAlt(_localctx, 2); { - setState(380); + setState(386); match(USING); - setState(381); + setState(387); match(T__0); - setState(382); + setState(388); identifier(); - setState(387); + setState(393); _errHandler.sync(this); _la = _input.LA(1); while (_la==T__2) { { { - setState(383); + setState(389); match(T__2); - setState(384); + setState(390); identifier(); } } - setState(389); + setState(395); _errHandler.sync(this); _la = _input.LA(1); } - setState(390); + setState(396); match(T__1); } break; @@ -2647,93 +2697,96 @@ public final RelationPrimaryContext relationPrimary() throws RecognitionExceptio enterRule(_localctx, 40, RULE_relationPrimary); int _la; try { - setState(419); + setState(425); _errHandler.sync(this); switch ( getInterpreter().adaptivePredict(_input,57,_ctx) ) { case 1: _localctx = new TableNameContext(_localctx); enterOuterAlt(_localctx, 1); { - setState(394); + setState(400); tableIdentifier(); - setState(399); - _la = _input.LA(1); - if ((((_la) & ~0x3f) == 0 && ((1L << _la) & ((1L << ANALYZE) | (1L << ANALYZED) | (1L << AS) | (1L << CATALOGS) | (1L << COLUMNS) | (1L << DEBUG) | (1L << EXECUTABLE) | (1L << EXPLAIN) | (1L << FORMAT) | (1L << FUNCTIONS) | (1L << GRAPHVIZ) | (1L << MAPPED) | (1L << OPTIMIZED) | (1L << PARSED) | (1L << PHYSICAL) | (1L << PLAN) | (1L << RLIKE) | (1L << QUERY) | (1L << SCHEMAS))) != 0) || ((((_la - 64)) & ~0x3f) == 0 && ((1L << (_la - 64)) & ((1L << (SHOW - 64)) | (1L << (SYS - 64)) | (1L << (TABLES - 64)) | (1L << (TEXT - 64)) | (1L << (TYPE - 64)) | (1L << (TYPES - 64)) | (1L << (VERIFY - 64)) | (1L << (IDENTIFIER - 64)) | (1L << (DIGIT_IDENTIFIER - 64)) | (1L << (QUOTED_IDENTIFIER - 64)) | (1L << (BACKQUOTED_IDENTIFIER - 64)))) != 0)) { + setState(405); + _errHandler.sync(this); + switch ( getInterpreter().adaptivePredict(_input,52,_ctx) ) { + case 1: { - setState(396); + setState(402); _la = _input.LA(1); if (_la==AS) { { - setState(395); + setState(401); match(AS); } } - setState(398); + setState(404); qualifiedName(); } + break; } - } break; case 2: _localctx = new AliasedQueryContext(_localctx); enterOuterAlt(_localctx, 2); { - setState(401); + setState(407); match(T__0); - setState(402); + setState(408); queryNoWith(); - setState(403); + setState(409); match(T__1); - setState(408); - _la = _input.LA(1); - if ((((_la) & ~0x3f) == 0 && ((1L << _la) & ((1L << ANALYZE) | (1L << ANALYZED) | (1L << AS) | (1L << CATALOGS) | (1L << COLUMNS) | (1L << DEBUG) | (1L << EXECUTABLE) | (1L << EXPLAIN) | (1L << FORMAT) | (1L << FUNCTIONS) | (1L << GRAPHVIZ) | (1L << MAPPED) | (1L << OPTIMIZED) | (1L << PARSED) | (1L << PHYSICAL) | (1L << PLAN) | (1L << RLIKE) | (1L << QUERY) | (1L << SCHEMAS))) != 0) || ((((_la - 64)) & ~0x3f) == 0 && ((1L << (_la - 64)) & ((1L << (SHOW - 64)) | (1L << (SYS - 64)) | (1L << (TABLES - 64)) | (1L << (TEXT - 64)) | (1L << (TYPE - 64)) | (1L << (TYPES - 64)) | (1L << (VERIFY - 64)) | (1L << (IDENTIFIER - 64)) | (1L << (DIGIT_IDENTIFIER - 64)) | (1L << (QUOTED_IDENTIFIER - 64)) | (1L << (BACKQUOTED_IDENTIFIER - 64)))) != 0)) { + setState(414); + _errHandler.sync(this); + switch ( getInterpreter().adaptivePredict(_input,54,_ctx) ) { + case 1: { - setState(405); + setState(411); _la = _input.LA(1); if (_la==AS) { { - setState(404); + setState(410); match(AS); } } - setState(407); + setState(413); qualifiedName(); } + break; } - } break; case 3: _localctx = new AliasedRelationContext(_localctx); enterOuterAlt(_localctx, 3); { - setState(410); + setState(416); match(T__0); - setState(411); + setState(417); relation(); - setState(412); + setState(418); match(T__1); - setState(417); - _la = _input.LA(1); - if ((((_la) & ~0x3f) == 0 && ((1L << _la) & ((1L << ANALYZE) | (1L << ANALYZED) | (1L << AS) | (1L << CATALOGS) | (1L << COLUMNS) | (1L << DEBUG) | (1L << EXECUTABLE) | (1L << EXPLAIN) | (1L << FORMAT) | (1L << FUNCTIONS) | (1L << GRAPHVIZ) | (1L << MAPPED) | (1L << OPTIMIZED) | (1L << PARSED) | (1L << PHYSICAL) | (1L << PLAN) | (1L << RLIKE) | (1L << QUERY) | (1L << SCHEMAS))) != 0) || ((((_la - 64)) & ~0x3f) == 0 && ((1L << (_la - 64)) & ((1L << (SHOW - 64)) | (1L << (SYS - 64)) | (1L << (TABLES - 64)) | (1L << (TEXT - 64)) | (1L << (TYPE - 64)) | (1L << (TYPES - 64)) | (1L << (VERIFY - 64)) | (1L << (IDENTIFIER - 64)) | (1L << (DIGIT_IDENTIFIER - 64)) | (1L << (QUOTED_IDENTIFIER - 64)) | (1L << (BACKQUOTED_IDENTIFIER - 64)))) != 0)) { + setState(423); + _errHandler.sync(this); + switch ( getInterpreter().adaptivePredict(_input,56,_ctx) ) { + case 1: { - setState(414); + setState(420); _la = _input.LA(1); if (_la==AS) { { - setState(413); + setState(419); match(AS); } } - setState(416); + setState(422); qualifiedName(); } + break; } - } break; } @@ -2778,7 +2831,7 @@ public final ExpressionContext expression() throws RecognitionException { try { enterOuterAlt(_localctx, 1); { - setState(421); + setState(427); booleanExpression(0); } } @@ -2986,7 +3039,7 @@ private BooleanExpressionContext booleanExpression(int _p) throws RecognitionExc int _alt; enterOuterAlt(_localctx, 1); { - setState(454); + setState(460); _errHandler.sync(this); switch ( getInterpreter().adaptivePredict(_input,58,_ctx) ) { case 1: @@ -2995,9 +3048,9 @@ private BooleanExpressionContext booleanExpression(int _p) throws RecognitionExc _ctx = _localctx; _prevctx = _localctx; - setState(424); + setState(430); match(NOT); - setState(425); + setState(431); booleanExpression(8); } break; @@ -3006,13 +3059,13 @@ private BooleanExpressionContext booleanExpression(int _p) throws RecognitionExc _localctx = new ExistsContext(_localctx); _ctx = _localctx; _prevctx = _localctx; - setState(426); + setState(432); match(EXISTS); - setState(427); + setState(433); match(T__0); - setState(428); + setState(434); query(); - setState(429); + setState(435); match(T__1); } break; @@ -3021,15 +3074,15 @@ private BooleanExpressionContext booleanExpression(int _p) throws RecognitionExc _localctx = new StringQueryContext(_localctx); _ctx = _localctx; _prevctx = _localctx; - setState(431); + setState(437); match(QUERY); - setState(432); + setState(438); match(T__0); - setState(433); + setState(439); ((StringQueryContext)_localctx).queryString = string(); - setState(434); + setState(440); matchQueryOptions(); - setState(435); + setState(441); match(T__1); } break; @@ -3038,19 +3091,19 @@ private BooleanExpressionContext booleanExpression(int _p) throws RecognitionExc _localctx = new MatchQueryContext(_localctx); _ctx = _localctx; _prevctx = _localctx; - setState(437); + setState(443); match(MATCH); - setState(438); + setState(444); match(T__0); - setState(439); + setState(445); ((MatchQueryContext)_localctx).singleField = qualifiedName(); - setState(440); + setState(446); match(T__2); - setState(441); + setState(447); ((MatchQueryContext)_localctx).queryString = string(); - setState(442); + setState(448); matchQueryOptions(); - setState(443); + setState(449); match(T__1); } break; @@ -3059,19 +3112,19 @@ private BooleanExpressionContext booleanExpression(int _p) throws RecognitionExc _localctx = new MultiMatchQueryContext(_localctx); _ctx = _localctx; _prevctx = _localctx; - setState(445); + setState(451); match(MATCH); - setState(446); + setState(452); match(T__0); - setState(447); + setState(453); ((MultiMatchQueryContext)_localctx).multiFields = string(); - setState(448); + setState(454); match(T__2); - setState(449); + setState(455); ((MultiMatchQueryContext)_localctx).queryString = string(); - setState(450); + setState(456); matchQueryOptions(); - setState(451); + setState(457); match(T__1); } break; @@ -3080,13 +3133,13 @@ private BooleanExpressionContext booleanExpression(int _p) throws RecognitionExc _localctx = new BooleanDefaultContext(_localctx); _ctx = _localctx; _prevctx = _localctx; - setState(453); + setState(459); predicated(); } break; } _ctx.stop = _input.LT(-1); - setState(464); + setState(470); _errHandler.sync(this); _alt = getInterpreter().adaptivePredict(_input,60,_ctx); while ( _alt!=2 && _alt!=org.antlr.v4.runtime.atn.ATN.INVALID_ALT_NUMBER ) { @@ -3094,7 +3147,7 @@ private BooleanExpressionContext booleanExpression(int _p) throws RecognitionExc if ( _parseListeners!=null ) triggerExitRuleEvent(); _prevctx = _localctx; { - setState(462); + setState(468); _errHandler.sync(this); switch ( getInterpreter().adaptivePredict(_input,59,_ctx) ) { case 1: @@ -3102,11 +3155,11 @@ private BooleanExpressionContext booleanExpression(int _p) throws RecognitionExc _localctx = new LogicalBinaryContext(new BooleanExpressionContext(_parentctx, _parentState)); ((LogicalBinaryContext)_localctx).left = _prevctx; pushNewRecursionContext(_localctx, _startState, RULE_booleanExpression); - setState(456); + setState(462); if (!(precpred(_ctx, 2))) throw new FailedPredicateException(this, "precpred(_ctx, 2)"); - setState(457); + setState(463); ((LogicalBinaryContext)_localctx).operator = match(AND); - setState(458); + setState(464); ((LogicalBinaryContext)_localctx).right = booleanExpression(3); } break; @@ -3115,18 +3168,18 @@ private BooleanExpressionContext booleanExpression(int _p) throws RecognitionExc _localctx = new LogicalBinaryContext(new BooleanExpressionContext(_parentctx, _parentState)); ((LogicalBinaryContext)_localctx).left = _prevctx; pushNewRecursionContext(_localctx, _startState, RULE_booleanExpression); - setState(459); + setState(465); if (!(precpred(_ctx, 1))) throw new FailedPredicateException(this, "precpred(_ctx, 1)"); - setState(460); + setState(466); ((LogicalBinaryContext)_localctx).operator = match(OR); - setState(461); + setState(467); ((LogicalBinaryContext)_localctx).right = booleanExpression(2); } break; } } } - setState(466); + setState(472); _errHandler.sync(this); _alt = getInterpreter().adaptivePredict(_input,60,_ctx); } @@ -3176,19 +3229,19 @@ public final MatchQueryOptionsContext matchQueryOptions() throws RecognitionExce try { enterOuterAlt(_localctx, 1); { - setState(471); + setState(477); _errHandler.sync(this); _la = _input.LA(1); while (_la==T__2) { { { - setState(467); + setState(473); match(T__2); - setState(468); + setState(474); string(); } } - setState(473); + setState(479); _errHandler.sync(this); _la = _input.LA(1); } @@ -3237,14 +3290,14 @@ public final PredicatedContext predicated() throws RecognitionException { try { enterOuterAlt(_localctx, 1); { - setState(474); + setState(480); valueExpression(0); - setState(476); + setState(482); _errHandler.sync(this); switch ( getInterpreter().adaptivePredict(_input,62,_ctx) ) { case 1: { - setState(475); + setState(481); predicate(); } break; @@ -3320,142 +3373,142 @@ public final PredicateContext predicate() throws RecognitionException { enterRule(_localctx, 50, RULE_predicate); int _la; try { - setState(524); + setState(530); _errHandler.sync(this); switch ( getInterpreter().adaptivePredict(_input,70,_ctx) ) { case 1: enterOuterAlt(_localctx, 1); { - setState(479); + setState(485); _la = _input.LA(1); if (_la==NOT) { { - setState(478); + setState(484); match(NOT); } } - setState(481); + setState(487); ((PredicateContext)_localctx).kind = match(BETWEEN); - setState(482); + setState(488); ((PredicateContext)_localctx).lower = valueExpression(0); - setState(483); + setState(489); match(AND); - setState(484); + setState(490); ((PredicateContext)_localctx).upper = valueExpression(0); } break; case 2: enterOuterAlt(_localctx, 2); { - setState(487); + setState(493); _la = _input.LA(1); if (_la==NOT) { { - setState(486); + setState(492); match(NOT); } } - setState(489); + setState(495); ((PredicateContext)_localctx).kind = match(IN); - setState(490); + setState(496); match(T__0); - setState(491); + setState(497); expression(); - setState(496); + setState(502); _errHandler.sync(this); _la = _input.LA(1); while (_la==T__2) { { { - setState(492); + setState(498); match(T__2); - setState(493); + setState(499); expression(); } } - setState(498); + setState(504); _errHandler.sync(this); _la = _input.LA(1); } - setState(499); + setState(505); match(T__1); } break; case 3: enterOuterAlt(_localctx, 3); { - setState(502); + setState(508); _la = _input.LA(1); if (_la==NOT) { { - setState(501); + setState(507); match(NOT); } } - setState(504); + setState(510); ((PredicateContext)_localctx).kind = match(IN); - setState(505); + setState(511); match(T__0); - setState(506); + setState(512); query(); - setState(507); + setState(513); match(T__1); } break; case 4: enterOuterAlt(_localctx, 4); { - setState(510); + setState(516); _la = _input.LA(1); if (_la==NOT) { { - setState(509); + setState(515); match(NOT); } } - setState(512); + setState(518); ((PredicateContext)_localctx).kind = match(LIKE); - setState(513); + setState(519); pattern(); } break; case 5: enterOuterAlt(_localctx, 5); { - setState(515); + setState(521); _la = _input.LA(1); if (_la==NOT) { { - setState(514); + setState(520); match(NOT); } } - setState(517); + setState(523); ((PredicateContext)_localctx).kind = match(RLIKE); - setState(518); + setState(524); ((PredicateContext)_localctx).regex = string(); } break; case 6: enterOuterAlt(_localctx, 6); { - setState(519); + setState(525); match(IS); - setState(521); + setState(527); _la = _input.LA(1); if (_la==NOT) { { - setState(520); + setState(526); match(NOT); } } - setState(523); + setState(529); ((PredicateContext)_localctx).kind = match(NULL); } break; @@ -3502,9 +3555,9 @@ public final LikePatternContext likePattern() throws RecognitionException { try { enterOuterAlt(_localctx, 1); { - setState(526); + setState(532); match(LIKE); - setState(527); + setState(533); pattern(); } } @@ -3552,14 +3605,14 @@ public final PatternContext pattern() throws RecognitionException { try { enterOuterAlt(_localctx, 1); { - setState(529); + setState(535); ((PatternContext)_localctx).value = string(); - setState(531); + setState(537); _errHandler.sync(this); switch ( getInterpreter().adaptivePredict(_input,71,_ctx) ) { case 1: { - setState(530); + setState(536); patternEscape(); } break; @@ -3607,25 +3660,25 @@ public final PatternEscapeContext patternEscape() throws RecognitionException { PatternEscapeContext _localctx = new PatternEscapeContext(_ctx, getState()); enterRule(_localctx, 56, RULE_patternEscape); try { - setState(539); + setState(545); switch (_input.LA(1)) { case ESCAPE: enterOuterAlt(_localctx, 1); { - setState(533); + setState(539); match(ESCAPE); - setState(534); + setState(540); ((PatternEscapeContext)_localctx).escape = string(); } break; case ESCAPE_ESC: enterOuterAlt(_localctx, 2); { - setState(535); + setState(541); match(ESCAPE_ESC); - setState(536); + setState(542); ((PatternEscapeContext)_localctx).escape = string(); - setState(537); + setState(543); match(ESC_END); } break; @@ -3770,7 +3823,7 @@ private ValueExpressionContext valueExpression(int _p) throws RecognitionExcepti int _alt; enterOuterAlt(_localctx, 1); { - setState(545); + setState(551); switch (_input.LA(1)) { case T__0: case ANALYZE: @@ -3779,16 +3832,24 @@ private ValueExpressionContext valueExpression(int _p) throws RecognitionExcepti case CATALOGS: case COLUMNS: case CONVERT: + case DAY: case DEBUG: case EXECUTABLE: case EXPLAIN: case EXTRACT: case FALSE: + case FIRST: case FORMAT: case FUNCTIONS: case GRAPHVIZ: + case HOUR: + case INTERVAL: + case LAST: case LEFT: + case LIMIT: case MAPPED: + case MINUTE: + case MONTH: case NULL: case OPTIMIZED: case PARSED: @@ -3798,6 +3859,7 @@ private ValueExpressionContext valueExpression(int _p) throws RecognitionExcepti case RLIKE: case QUERY: case SCHEMAS: + case SECOND: case SHOW: case SYS: case TABLES: @@ -3806,6 +3868,7 @@ private ValueExpressionContext valueExpression(int _p) throws RecognitionExcepti case TYPE: case TYPES: case VERIFY: + case YEAR: case FUNCTION_ESC: case DATE_ESC: case TIME_ESC: @@ -3825,7 +3888,7 @@ private ValueExpressionContext valueExpression(int _p) throws RecognitionExcepti _ctx = _localctx; _prevctx = _localctx; - setState(542); + setState(548); primaryExpression(); } break; @@ -3835,7 +3898,7 @@ private ValueExpressionContext valueExpression(int _p) throws RecognitionExcepti _localctx = new ArithmeticUnaryContext(_localctx); _ctx = _localctx; _prevctx = _localctx; - setState(543); + setState(549); ((ArithmeticUnaryContext)_localctx).operator = _input.LT(1); _la = _input.LA(1); if ( !(_la==PLUS || _la==MINUS) ) { @@ -3843,7 +3906,7 @@ private ValueExpressionContext valueExpression(int _p) throws RecognitionExcepti } else { consume(); } - setState(544); + setState(550); valueExpression(4); } break; @@ -3851,7 +3914,7 @@ private ValueExpressionContext valueExpression(int _p) throws RecognitionExcepti throw new NoViableAltException(this); } _ctx.stop = _input.LT(-1); - setState(559); + setState(565); _errHandler.sync(this); _alt = getInterpreter().adaptivePredict(_input,75,_ctx); while ( _alt!=2 && _alt!=org.antlr.v4.runtime.atn.ATN.INVALID_ALT_NUMBER ) { @@ -3859,7 +3922,7 @@ private ValueExpressionContext valueExpression(int _p) throws RecognitionExcepti if ( _parseListeners!=null ) triggerExitRuleEvent(); _prevctx = _localctx; { - setState(557); + setState(563); _errHandler.sync(this); switch ( getInterpreter().adaptivePredict(_input,74,_ctx) ) { case 1: @@ -3867,17 +3930,17 @@ private ValueExpressionContext valueExpression(int _p) throws RecognitionExcepti _localctx = new ArithmeticBinaryContext(new ValueExpressionContext(_parentctx, _parentState)); ((ArithmeticBinaryContext)_localctx).left = _prevctx; pushNewRecursionContext(_localctx, _startState, RULE_valueExpression); - setState(547); + setState(553); if (!(precpred(_ctx, 3))) throw new FailedPredicateException(this, "precpred(_ctx, 3)"); - setState(548); + setState(554); ((ArithmeticBinaryContext)_localctx).operator = _input.LT(1); _la = _input.LA(1); - if ( !(((((_la - 92)) & ~0x3f) == 0 && ((1L << (_la - 92)) & ((1L << (ASTERISK - 92)) | (1L << (SLASH - 92)) | (1L << (PERCENT - 92)))) != 0)) ) { + if ( !(((((_la - 106)) & ~0x3f) == 0 && ((1L << (_la - 106)) & ((1L << (ASTERISK - 106)) | (1L << (SLASH - 106)) | (1L << (PERCENT - 106)))) != 0)) ) { ((ArithmeticBinaryContext)_localctx).operator = (Token)_errHandler.recoverInline(this); } else { consume(); } - setState(549); + setState(555); ((ArithmeticBinaryContext)_localctx).right = valueExpression(4); } break; @@ -3886,9 +3949,9 @@ private ValueExpressionContext valueExpression(int _p) throws RecognitionExcepti _localctx = new ArithmeticBinaryContext(new ValueExpressionContext(_parentctx, _parentState)); ((ArithmeticBinaryContext)_localctx).left = _prevctx; pushNewRecursionContext(_localctx, _startState, RULE_valueExpression); - setState(550); + setState(556); if (!(precpred(_ctx, 2))) throw new FailedPredicateException(this, "precpred(_ctx, 2)"); - setState(551); + setState(557); ((ArithmeticBinaryContext)_localctx).operator = _input.LT(1); _la = _input.LA(1); if ( !(_la==PLUS || _la==MINUS) ) { @@ -3896,7 +3959,7 @@ private ValueExpressionContext valueExpression(int _p) throws RecognitionExcepti } else { consume(); } - setState(552); + setState(558); ((ArithmeticBinaryContext)_localctx).right = valueExpression(3); } break; @@ -3905,18 +3968,18 @@ private ValueExpressionContext valueExpression(int _p) throws RecognitionExcepti _localctx = new ComparisonContext(new ValueExpressionContext(_parentctx, _parentState)); ((ComparisonContext)_localctx).left = _prevctx; pushNewRecursionContext(_localctx, _startState, RULE_valueExpression); - setState(553); + setState(559); if (!(precpred(_ctx, 1))) throw new FailedPredicateException(this, "precpred(_ctx, 1)"); - setState(554); + setState(560); comparisonOperator(); - setState(555); + setState(561); ((ComparisonContext)_localctx).right = valueExpression(2); } break; } } } - setState(561); + setState(567); _errHandler.sync(this); _alt = getInterpreter().adaptivePredict(_input,75,_ctx); } @@ -4104,14 +4167,14 @@ public final PrimaryExpressionContext primaryExpression() throws RecognitionExce enterRule(_localctx, 60, RULE_primaryExpression); int _la; try { - setState(581); + setState(587); _errHandler.sync(this); switch ( getInterpreter().adaptivePredict(_input,77,_ctx) ) { case 1: _localctx = new CastContext(_localctx); enterOuterAlt(_localctx, 1); { - setState(562); + setState(568); castExpression(); } break; @@ -4119,7 +4182,7 @@ public final PrimaryExpressionContext primaryExpression() throws RecognitionExce _localctx = new ExtractContext(_localctx); enterOuterAlt(_localctx, 2); { - setState(563); + setState(569); extractExpression(); } break; @@ -4127,7 +4190,7 @@ public final PrimaryExpressionContext primaryExpression() throws RecognitionExce _localctx = new ConstantDefaultContext(_localctx); enterOuterAlt(_localctx, 3); { - setState(564); + setState(570); constant(); } break; @@ -4135,18 +4198,18 @@ public final PrimaryExpressionContext primaryExpression() throws RecognitionExce _localctx = new StarContext(_localctx); enterOuterAlt(_localctx, 4); { - setState(568); + setState(574); _la = _input.LA(1); - if ((((_la) & ~0x3f) == 0 && ((1L << _la) & ((1L << ANALYZE) | (1L << ANALYZED) | (1L << CATALOGS) | (1L << COLUMNS) | (1L << DEBUG) | (1L << EXECUTABLE) | (1L << EXPLAIN) | (1L << FORMAT) | (1L << FUNCTIONS) | (1L << GRAPHVIZ) | (1L << MAPPED) | (1L << OPTIMIZED) | (1L << PARSED) | (1L << PHYSICAL) | (1L << PLAN) | (1L << RLIKE) | (1L << QUERY) | (1L << SCHEMAS))) != 0) || ((((_la - 64)) & ~0x3f) == 0 && ((1L << (_la - 64)) & ((1L << (SHOW - 64)) | (1L << (SYS - 64)) | (1L << (TABLES - 64)) | (1L << (TEXT - 64)) | (1L << (TYPE - 64)) | (1L << (TYPES - 64)) | (1L << (VERIFY - 64)) | (1L << (IDENTIFIER - 64)) | (1L << (DIGIT_IDENTIFIER - 64)) | (1L << (QUOTED_IDENTIFIER - 64)) | (1L << (BACKQUOTED_IDENTIFIER - 64)))) != 0)) { + if ((((_la) & ~0x3f) == 0 && ((1L << _la) & ((1L << ANALYZE) | (1L << ANALYZED) | (1L << CATALOGS) | (1L << COLUMNS) | (1L << DAY) | (1L << DEBUG) | (1L << EXECUTABLE) | (1L << EXPLAIN) | (1L << FIRST) | (1L << FORMAT) | (1L << FUNCTIONS) | (1L << GRAPHVIZ) | (1L << HOUR) | (1L << INTERVAL) | (1L << LAST) | (1L << LIMIT) | (1L << MAPPED) | (1L << MINUTE) | (1L << MONTH) | (1L << OPTIMIZED))) != 0) || ((((_la - 65)) & ~0x3f) == 0 && ((1L << (_la - 65)) & ((1L << (PARSED - 65)) | (1L << (PHYSICAL - 65)) | (1L << (PLAN - 65)) | (1L << (RLIKE - 65)) | (1L << (QUERY - 65)) | (1L << (SCHEMAS - 65)) | (1L << (SECOND - 65)) | (1L << (SHOW - 65)) | (1L << (SYS - 65)) | (1L << (TABLES - 65)) | (1L << (TEXT - 65)) | (1L << (TYPE - 65)) | (1L << (TYPES - 65)) | (1L << (VERIFY - 65)) | (1L << (YEAR - 65)) | (1L << (IDENTIFIER - 65)) | (1L << (DIGIT_IDENTIFIER - 65)) | (1L << (QUOTED_IDENTIFIER - 65)) | (1L << (BACKQUOTED_IDENTIFIER - 65)))) != 0)) { { - setState(565); + setState(571); qualifiedName(); - setState(566); + setState(572); match(DOT); } } - setState(570); + setState(576); match(ASTERISK); } break; @@ -4154,7 +4217,7 @@ public final PrimaryExpressionContext primaryExpression() throws RecognitionExce _localctx = new FunctionContext(_localctx); enterOuterAlt(_localctx, 5); { - setState(571); + setState(577); functionExpression(); } break; @@ -4162,11 +4225,11 @@ public final PrimaryExpressionContext primaryExpression() throws RecognitionExce _localctx = new SubqueryExpressionContext(_localctx); enterOuterAlt(_localctx, 6); { - setState(572); + setState(578); match(T__0); - setState(573); + setState(579); query(); - setState(574); + setState(580); match(T__1); } break; @@ -4174,7 +4237,7 @@ public final PrimaryExpressionContext primaryExpression() throws RecognitionExce _localctx = new DereferenceContext(_localctx); enterOuterAlt(_localctx, 7); { - setState(576); + setState(582); qualifiedName(); } break; @@ -4182,11 +4245,11 @@ public final PrimaryExpressionContext primaryExpression() throws RecognitionExce _localctx = new ParenthesizedExpressionContext(_localctx); enterOuterAlt(_localctx, 8); { - setState(577); + setState(583); match(T__0); - setState(578); + setState(584); expression(); - setState(579); + setState(585); match(T__1); } break; @@ -4235,42 +4298,42 @@ public final CastExpressionContext castExpression() throws RecognitionException CastExpressionContext _localctx = new CastExpressionContext(_ctx, getState()); enterRule(_localctx, 62, RULE_castExpression); try { - setState(593); + setState(599); _errHandler.sync(this); switch ( getInterpreter().adaptivePredict(_input,78,_ctx) ) { case 1: enterOuterAlt(_localctx, 1); { - setState(583); + setState(589); castTemplate(); } break; case 2: enterOuterAlt(_localctx, 2); { - setState(584); + setState(590); match(FUNCTION_ESC); - setState(585); + setState(591); castTemplate(); - setState(586); + setState(592); match(ESC_END); } break; case 3: enterOuterAlt(_localctx, 3); { - setState(588); + setState(594); convertTemplate(); } break; case 4: enterOuterAlt(_localctx, 4); { - setState(589); + setState(595); match(FUNCTION_ESC); - setState(590); + setState(596); convertTemplate(); - setState(591); + setState(597); match(ESC_END); } break; @@ -4321,17 +4384,17 @@ public final CastTemplateContext castTemplate() throws RecognitionException { try { enterOuterAlt(_localctx, 1); { - setState(595); + setState(601); match(CAST); - setState(596); + setState(602); match(T__0); - setState(597); + setState(603); expression(); - setState(598); + setState(604); match(AS); - setState(599); + setState(605); dataType(); - setState(600); + setState(606); match(T__1); } } @@ -4379,17 +4442,17 @@ public final ConvertTemplateContext convertTemplate() throws RecognitionExceptio try { enterOuterAlt(_localctx, 1); { - setState(602); + setState(608); match(CONVERT); - setState(603); + setState(609); match(T__0); - setState(604); + setState(610); expression(); - setState(605); + setState(611); match(T__2); - setState(606); + setState(612); dataType(); - setState(607); + setState(613); match(T__1); } } @@ -4433,23 +4496,23 @@ public final ExtractExpressionContext extractExpression() throws RecognitionExce ExtractExpressionContext _localctx = new ExtractExpressionContext(_ctx, getState()); enterRule(_localctx, 68, RULE_extractExpression); try { - setState(614); + setState(620); switch (_input.LA(1)) { case EXTRACT: enterOuterAlt(_localctx, 1); { - setState(609); + setState(615); extractTemplate(); } break; case FUNCTION_ESC: enterOuterAlt(_localctx, 2); { - setState(610); + setState(616); match(FUNCTION_ESC); - setState(611); + setState(617); extractTemplate(); - setState(612); + setState(618); match(ESC_END); } break; @@ -4503,17 +4566,17 @@ public final ExtractTemplateContext extractTemplate() throws RecognitionExceptio try { enterOuterAlt(_localctx, 1); { - setState(616); + setState(622); match(EXTRACT); - setState(617); + setState(623); match(T__0); - setState(618); + setState(624); ((ExtractTemplateContext)_localctx).field = identifier(); - setState(619); + setState(625); match(FROM); - setState(620); + setState(626); valueExpression(0); - setState(621); + setState(627); match(T__1); } } @@ -4556,20 +4619,28 @@ public final FunctionExpressionContext functionExpression() throws RecognitionEx FunctionExpressionContext _localctx = new FunctionExpressionContext(_ctx, getState()); enterRule(_localctx, 72, RULE_functionExpression); try { - setState(628); + setState(634); switch (_input.LA(1)) { case ANALYZE: case ANALYZED: case CATALOGS: case COLUMNS: + case DAY: case DEBUG: case EXECUTABLE: case EXPLAIN: + case FIRST: case FORMAT: case FUNCTIONS: case GRAPHVIZ: + case HOUR: + case INTERVAL: + case LAST: case LEFT: + case LIMIT: case MAPPED: + case MINUTE: + case MONTH: case OPTIMIZED: case PARSED: case PHYSICAL: @@ -4578,6 +4649,7 @@ public final FunctionExpressionContext functionExpression() throws RecognitionEx case RLIKE: case QUERY: case SCHEMAS: + case SECOND: case SHOW: case SYS: case TABLES: @@ -4585,24 +4657,25 @@ public final FunctionExpressionContext functionExpression() throws RecognitionEx case TYPE: case TYPES: case VERIFY: + case YEAR: case IDENTIFIER: case DIGIT_IDENTIFIER: case QUOTED_IDENTIFIER: case BACKQUOTED_IDENTIFIER: enterOuterAlt(_localctx, 1); { - setState(623); + setState(629); functionTemplate(); } break; case FUNCTION_ESC: enterOuterAlt(_localctx, 2); { - setState(624); + setState(630); match(FUNCTION_ESC); - setState(625); + setState(631); functionTemplate(); - setState(626); + setState(632); match(ESC_END); } break; @@ -4660,45 +4733,45 @@ public final FunctionTemplateContext functionTemplate() throws RecognitionExcept try { enterOuterAlt(_localctx, 1); { - setState(630); + setState(636); functionName(); - setState(631); + setState(637); match(T__0); - setState(643); + setState(649); _la = _input.LA(1); - if ((((_la) & ~0x3f) == 0 && ((1L << _la) & ((1L << T__0) | (1L << ALL) | (1L << ANALYZE) | (1L << ANALYZED) | (1L << CAST) | (1L << CATALOGS) | (1L << COLUMNS) | (1L << CONVERT) | (1L << DEBUG) | (1L << DISTINCT) | (1L << EXECUTABLE) | (1L << EXISTS) | (1L << EXPLAIN) | (1L << EXTRACT) | (1L << FALSE) | (1L << FORMAT) | (1L << FUNCTIONS) | (1L << GRAPHVIZ) | (1L << LEFT) | (1L << MAPPED) | (1L << MATCH) | (1L << NOT) | (1L << NULL) | (1L << OPTIMIZED) | (1L << PARSED) | (1L << PHYSICAL) | (1L << PLAN) | (1L << RIGHT) | (1L << RLIKE) | (1L << QUERY) | (1L << SCHEMAS))) != 0) || ((((_la - 64)) & ~0x3f) == 0 && ((1L << (_la - 64)) & ((1L << (SHOW - 64)) | (1L << (SYS - 64)) | (1L << (TABLES - 64)) | (1L << (TEXT - 64)) | (1L << (TRUE - 64)) | (1L << (TYPE - 64)) | (1L << (TYPES - 64)) | (1L << (VERIFY - 64)) | (1L << (FUNCTION_ESC - 64)) | (1L << (DATE_ESC - 64)) | (1L << (TIME_ESC - 64)) | (1L << (TIMESTAMP_ESC - 64)) | (1L << (GUID_ESC - 64)) | (1L << (PLUS - 64)) | (1L << (MINUS - 64)) | (1L << (ASTERISK - 64)) | (1L << (PARAM - 64)) | (1L << (STRING - 64)) | (1L << (INTEGER_VALUE - 64)) | (1L << (DECIMAL_VALUE - 64)) | (1L << (IDENTIFIER - 64)) | (1L << (DIGIT_IDENTIFIER - 64)) | (1L << (QUOTED_IDENTIFIER - 64)) | (1L << (BACKQUOTED_IDENTIFIER - 64)))) != 0)) { + if ((((_la) & ~0x3f) == 0 && ((1L << _la) & ((1L << T__0) | (1L << ALL) | (1L << ANALYZE) | (1L << ANALYZED) | (1L << CAST) | (1L << CATALOGS) | (1L << COLUMNS) | (1L << CONVERT) | (1L << DAY) | (1L << DEBUG) | (1L << DISTINCT) | (1L << EXECUTABLE) | (1L << EXISTS) | (1L << EXPLAIN) | (1L << EXTRACT) | (1L << FALSE) | (1L << FIRST) | (1L << FORMAT) | (1L << FUNCTIONS) | (1L << GRAPHVIZ) | (1L << HOUR) | (1L << INTERVAL) | (1L << LAST) | (1L << LEFT) | (1L << LIMIT) | (1L << MAPPED) | (1L << MATCH) | (1L << MINUTE) | (1L << MONTH) | (1L << NOT) | (1L << NULL) | (1L << OPTIMIZED))) != 0) || ((((_la - 65)) & ~0x3f) == 0 && ((1L << (_la - 65)) & ((1L << (PARSED - 65)) | (1L << (PHYSICAL - 65)) | (1L << (PLAN - 65)) | (1L << (RIGHT - 65)) | (1L << (RLIKE - 65)) | (1L << (QUERY - 65)) | (1L << (SCHEMAS - 65)) | (1L << (SECOND - 65)) | (1L << (SHOW - 65)) | (1L << (SYS - 65)) | (1L << (TABLES - 65)) | (1L << (TEXT - 65)) | (1L << (TRUE - 65)) | (1L << (TYPE - 65)) | (1L << (TYPES - 65)) | (1L << (VERIFY - 65)) | (1L << (YEAR - 65)) | (1L << (FUNCTION_ESC - 65)) | (1L << (DATE_ESC - 65)) | (1L << (TIME_ESC - 65)) | (1L << (TIMESTAMP_ESC - 65)) | (1L << (GUID_ESC - 65)) | (1L << (PLUS - 65)) | (1L << (MINUS - 65)) | (1L << (ASTERISK - 65)) | (1L << (PARAM - 65)) | (1L << (STRING - 65)) | (1L << (INTEGER_VALUE - 65)) | (1L << (DECIMAL_VALUE - 65)) | (1L << (IDENTIFIER - 65)) | (1L << (DIGIT_IDENTIFIER - 65)) | (1L << (QUOTED_IDENTIFIER - 65)) | (1L << (BACKQUOTED_IDENTIFIER - 65)))) != 0)) { { - setState(633); + setState(639); _la = _input.LA(1); if (_la==ALL || _la==DISTINCT) { { - setState(632); + setState(638); setQuantifier(); } } - setState(635); + setState(641); expression(); - setState(640); + setState(646); _errHandler.sync(this); _la = _input.LA(1); while (_la==T__2) { { { - setState(636); + setState(642); match(T__2); - setState(637); + setState(643); expression(); } } - setState(642); + setState(648); _errHandler.sync(this); _la = _input.LA(1); } } } - setState(645); + setState(651); match(T__1); } } @@ -4742,19 +4815,19 @@ public final FunctionNameContext functionName() throws RecognitionException { FunctionNameContext _localctx = new FunctionNameContext(_ctx, getState()); enterRule(_localctx, 76, RULE_functionName); try { - setState(650); + setState(656); switch (_input.LA(1)) { case LEFT: enterOuterAlt(_localctx, 1); { - setState(647); + setState(653); match(LEFT); } break; case RIGHT: enterOuterAlt(_localctx, 2); { - setState(648); + setState(654); match(RIGHT); } break; @@ -4762,13 +4835,21 @@ public final FunctionNameContext functionName() throws RecognitionException { case ANALYZED: case CATALOGS: case COLUMNS: + case DAY: case DEBUG: case EXECUTABLE: case EXPLAIN: + case FIRST: case FORMAT: case FUNCTIONS: case GRAPHVIZ: + case HOUR: + case INTERVAL: + case LAST: + case LIMIT: case MAPPED: + case MINUTE: + case MONTH: case OPTIMIZED: case PARSED: case PHYSICAL: @@ -4776,6 +4857,7 @@ public final FunctionNameContext functionName() throws RecognitionException { case RLIKE: case QUERY: case SCHEMAS: + case SECOND: case SHOW: case SYS: case TABLES: @@ -4783,13 +4865,14 @@ public final FunctionNameContext functionName() throws RecognitionException { case TYPE: case TYPES: case VERIFY: + case YEAR: case IDENTIFIER: case DIGIT_IDENTIFIER: case QUOTED_IDENTIFIER: case BACKQUOTED_IDENTIFIER: enterOuterAlt(_localctx, 3); { - setState(649); + setState(655); identifier(); } break; @@ -4936,6 +5019,25 @@ public T accept(ParseTreeVisitor visitor) { else return visitor.visitChildren(this); } } + public static class IntervalLiteralContext extends ConstantContext { + public IntervalContext interval() { + return getRuleContext(IntervalContext.class,0); + } + public IntervalLiteralContext(ConstantContext ctx) { copyFrom(ctx); } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof SqlBaseListener ) ((SqlBaseListener)listener).enterIntervalLiteral(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof SqlBaseListener ) ((SqlBaseListener)listener).exitIntervalLiteral(this); + } + @Override + public T accept(ParseTreeVisitor visitor) { + if ( visitor instanceof SqlBaseVisitor ) return ((SqlBaseVisitor)visitor).visitIntervalLiteral(this); + else return visitor.visitChildren(this); + } + } public static class NumericLiteralContext extends ConstantContext { public NumberContext number() { return getRuleContext(NumberContext.class,0); @@ -5001,39 +5103,47 @@ public final ConstantContext constant() throws RecognitionException { enterRule(_localctx, 78, RULE_constant); try { int _alt; - setState(677); + setState(684); switch (_input.LA(1)) { case NULL: _localctx = new NullLiteralContext(_localctx); enterOuterAlt(_localctx, 1); { - setState(652); + setState(658); match(NULL); } break; + case INTERVAL: + _localctx = new IntervalLiteralContext(_localctx); + enterOuterAlt(_localctx, 2); + { + setState(659); + interval(); + } + break; case INTEGER_VALUE: case DECIMAL_VALUE: _localctx = new NumericLiteralContext(_localctx); - enterOuterAlt(_localctx, 2); + enterOuterAlt(_localctx, 3); { - setState(653); + setState(660); number(); } break; case FALSE: case TRUE: _localctx = new BooleanLiteralContext(_localctx); - enterOuterAlt(_localctx, 3); + enterOuterAlt(_localctx, 4); { - setState(654); + setState(661); booleanValue(); } break; case STRING: _localctx = new StringLiteralContext(_localctx); - enterOuterAlt(_localctx, 4); + enterOuterAlt(_localctx, 5); { - setState(656); + setState(663); _errHandler.sync(this); _alt = 1; do { @@ -5041,7 +5151,7 @@ public final ConstantContext constant() throws RecognitionException { case 1: { { - setState(655); + setState(662); match(STRING); } } @@ -5049,7 +5159,7 @@ public final ConstantContext constant() throws RecognitionException { default: throw new NoViableAltException(this); } - setState(658); + setState(665); _errHandler.sync(this); _alt = getInterpreter().adaptivePredict(_input,85,_ctx); } while ( _alt!=2 && _alt!=org.antlr.v4.runtime.atn.ATN.INVALID_ALT_NUMBER ); @@ -5057,57 +5167,57 @@ public final ConstantContext constant() throws RecognitionException { break; case PARAM: _localctx = new ParamLiteralContext(_localctx); - enterOuterAlt(_localctx, 5); + enterOuterAlt(_localctx, 6); { - setState(660); + setState(667); match(PARAM); } break; case DATE_ESC: _localctx = new DateEscapedLiteralContext(_localctx); - enterOuterAlt(_localctx, 6); + enterOuterAlt(_localctx, 7); { - setState(661); + setState(668); match(DATE_ESC); - setState(662); + setState(669); string(); - setState(663); + setState(670); match(ESC_END); } break; case TIME_ESC: _localctx = new TimeEscapedLiteralContext(_localctx); - enterOuterAlt(_localctx, 7); + enterOuterAlt(_localctx, 8); { - setState(665); + setState(672); match(TIME_ESC); - setState(666); + setState(673); string(); - setState(667); + setState(674); match(ESC_END); } break; case TIMESTAMP_ESC: _localctx = new TimestampEscapedLiteralContext(_localctx); - enterOuterAlt(_localctx, 8); + enterOuterAlt(_localctx, 9); { - setState(669); + setState(676); match(TIMESTAMP_ESC); - setState(670); + setState(677); string(); - setState(671); + setState(678); match(ESC_END); } break; case GUID_ESC: _localctx = new GuidEscapedLiteralContext(_localctx); - enterOuterAlt(_localctx, 9); + enterOuterAlt(_localctx, 10); { - setState(673); + setState(680); match(GUID_ESC); - setState(674); + setState(681); string(); - setState(675); + setState(682); match(ESC_END); } break; @@ -5159,9 +5269,9 @@ public final ComparisonOperatorContext comparisonOperator() throws RecognitionEx try { enterOuterAlt(_localctx, 1); { - setState(679); + setState(686); _la = _input.LA(1); - if ( !(((((_la - 84)) & ~0x3f) == 0 && ((1L << (_la - 84)) & ((1L << (EQ - 84)) | (1L << (NEQ - 84)) | (1L << (LT - 84)) | (1L << (LTE - 84)) | (1L << (GT - 84)) | (1L << (GTE - 84)))) != 0)) ) { + if ( !(((((_la - 98)) & ~0x3f) == 0 && ((1L << (_la - 98)) & ((1L << (EQ - 98)) | (1L << (NEQ - 98)) | (1L << (LT - 98)) | (1L << (LTE - 98)) | (1L << (GT - 98)) | (1L << (GTE - 98)))) != 0)) ) { _errHandler.recoverInline(this); } else { consume(); @@ -5208,7 +5318,7 @@ public final BooleanValueContext booleanValue() throws RecognitionException { try { enterOuterAlt(_localctx, 1); { - setState(681); + setState(688); _la = _input.LA(1); if ( !(_la==FALSE || _la==TRUE) ) { _errHandler.recoverInline(this); @@ -5228,6 +5338,239 @@ public final BooleanValueContext booleanValue() throws RecognitionException { return _localctx; } + public static class IntervalContext extends ParserRuleContext { + public Token sign; + public NumberContext valueNumeric; + public StringContext valuePattern; + public IntervalFieldContext leading; + public IntervalFieldContext trailing; + public TerminalNode INTERVAL() { return getToken(SqlBaseParser.INTERVAL, 0); } + public List intervalField() { + return getRuleContexts(IntervalFieldContext.class); + } + public IntervalFieldContext intervalField(int i) { + return getRuleContext(IntervalFieldContext.class,i); + } + public NumberContext number() { + return getRuleContext(NumberContext.class,0); + } + public StringContext string() { + return getRuleContext(StringContext.class,0); + } + public TerminalNode TO() { return getToken(SqlBaseParser.TO, 0); } + public TerminalNode PLUS() { return getToken(SqlBaseParser.PLUS, 0); } + public TerminalNode MINUS() { return getToken(SqlBaseParser.MINUS, 0); } + public IntervalContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + @Override public int getRuleIndex() { return RULE_interval; } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof SqlBaseListener ) ((SqlBaseListener)listener).enterInterval(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof SqlBaseListener ) ((SqlBaseListener)listener).exitInterval(this); + } + @Override + public T accept(ParseTreeVisitor visitor) { + if ( visitor instanceof SqlBaseVisitor ) return ((SqlBaseVisitor)visitor).visitInterval(this); + else return visitor.visitChildren(this); + } + } + + public final IntervalContext interval() throws RecognitionException { + IntervalContext _localctx = new IntervalContext(_ctx, getState()); + enterRule(_localctx, 84, RULE_interval); + int _la; + try { + enterOuterAlt(_localctx, 1); + { + setState(690); + match(INTERVAL); + setState(692); + _la = _input.LA(1); + if (_la==PLUS || _la==MINUS) { + { + setState(691); + ((IntervalContext)_localctx).sign = _input.LT(1); + _la = _input.LA(1); + if ( !(_la==PLUS || _la==MINUS) ) { + ((IntervalContext)_localctx).sign = (Token)_errHandler.recoverInline(this); + } else { + consume(); + } + } + } + + setState(696); + switch (_input.LA(1)) { + case INTEGER_VALUE: + case DECIMAL_VALUE: + { + setState(694); + ((IntervalContext)_localctx).valueNumeric = number(); + } + break; + case PARAM: + case STRING: + { + setState(695); + ((IntervalContext)_localctx).valuePattern = string(); + } + break; + default: + throw new NoViableAltException(this); + } + setState(698); + ((IntervalContext)_localctx).leading = intervalField(); + setState(701); + _errHandler.sync(this); + switch ( getInterpreter().adaptivePredict(_input,89,_ctx) ) { + case 1: + { + setState(699); + match(TO); + setState(700); + ((IntervalContext)_localctx).trailing = intervalField(); + } + break; + } + } + } + catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } + finally { + exitRule(); + } + return _localctx; + } + + public static class IntervalValueContext extends ParserRuleContext { + public NumberContext number() { + return getRuleContext(NumberContext.class,0); + } + public StringContext string() { + return getRuleContext(StringContext.class,0); + } + public IntervalValueContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + @Override public int getRuleIndex() { return RULE_intervalValue; } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof SqlBaseListener ) ((SqlBaseListener)listener).enterIntervalValue(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof SqlBaseListener ) ((SqlBaseListener)listener).exitIntervalValue(this); + } + @Override + public T accept(ParseTreeVisitor visitor) { + if ( visitor instanceof SqlBaseVisitor ) return ((SqlBaseVisitor)visitor).visitIntervalValue(this); + else return visitor.visitChildren(this); + } + } + + public final IntervalValueContext intervalValue() throws RecognitionException { + IntervalValueContext _localctx = new IntervalValueContext(_ctx, getState()); + enterRule(_localctx, 86, RULE_intervalValue); + try { + setState(705); + switch (_input.LA(1)) { + case INTEGER_VALUE: + case DECIMAL_VALUE: + enterOuterAlt(_localctx, 1); + { + setState(703); + number(); + } + break; + case PARAM: + case STRING: + enterOuterAlt(_localctx, 2); + { + setState(704); + string(); + } + break; + default: + throw new NoViableAltException(this); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } + finally { + exitRule(); + } + return _localctx; + } + + public static class IntervalFieldContext extends ParserRuleContext { + public TerminalNode YEAR() { return getToken(SqlBaseParser.YEAR, 0); } + public TerminalNode YEARS() { return getToken(SqlBaseParser.YEARS, 0); } + public TerminalNode MONTH() { return getToken(SqlBaseParser.MONTH, 0); } + public TerminalNode MONTHS() { return getToken(SqlBaseParser.MONTHS, 0); } + public TerminalNode DAY() { return getToken(SqlBaseParser.DAY, 0); } + public TerminalNode DAYS() { return getToken(SqlBaseParser.DAYS, 0); } + public TerminalNode HOUR() { return getToken(SqlBaseParser.HOUR, 0); } + public TerminalNode HOURS() { return getToken(SqlBaseParser.HOURS, 0); } + public TerminalNode MINUTE() { return getToken(SqlBaseParser.MINUTE, 0); } + public TerminalNode MINUTES() { return getToken(SqlBaseParser.MINUTES, 0); } + public TerminalNode SECOND() { return getToken(SqlBaseParser.SECOND, 0); } + public TerminalNode SECONDS() { return getToken(SqlBaseParser.SECONDS, 0); } + public IntervalFieldContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + @Override public int getRuleIndex() { return RULE_intervalField; } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof SqlBaseListener ) ((SqlBaseListener)listener).enterIntervalField(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof SqlBaseListener ) ((SqlBaseListener)listener).exitIntervalField(this); + } + @Override + public T accept(ParseTreeVisitor visitor) { + if ( visitor instanceof SqlBaseVisitor ) return ((SqlBaseVisitor)visitor).visitIntervalField(this); + else return visitor.visitChildren(this); + } + } + + public final IntervalFieldContext intervalField() throws RecognitionException { + IntervalFieldContext _localctx = new IntervalFieldContext(_ctx, getState()); + enterRule(_localctx, 88, RULE_intervalField); + int _la; + try { + enterOuterAlt(_localctx, 1); + { + setState(707); + _la = _input.LA(1); + if ( !((((_la) & ~0x3f) == 0 && ((1L << _la) & ((1L << DAY) | (1L << DAYS) | (1L << HOUR) | (1L << HOURS) | (1L << MINUTE) | (1L << MINUTES) | (1L << MONTH) | (1L << MONTHS))) != 0) || ((((_la - 72)) & ~0x3f) == 0 && ((1L << (_la - 72)) & ((1L << (SECOND - 72)) | (1L << (SECONDS - 72)) | (1L << (YEAR - 72)) | (1L << (YEARS - 72)))) != 0)) ) { + _errHandler.recoverInline(this); + } else { + consume(); + } + } + } + catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } + finally { + exitRule(); + } + return _localctx; + } + public static class DataTypeContext extends ParserRuleContext { public DataTypeContext(ParserRuleContext parent, int invokingState) { super(parent, invokingState); @@ -5261,12 +5604,12 @@ public T accept(ParseTreeVisitor visitor) { public final DataTypeContext dataType() throws RecognitionException { DataTypeContext _localctx = new DataTypeContext(_ctx, getState()); - enterRule(_localctx, 84, RULE_dataType); + enterRule(_localctx, 90, RULE_dataType); try { _localctx = new PrimitiveDataTypeContext(_localctx); enterOuterAlt(_localctx, 1); { - setState(683); + setState(709); identifier(); } } @@ -5313,30 +5656,30 @@ public T accept(ParseTreeVisitor visitor) { public final QualifiedNameContext qualifiedName() throws RecognitionException { QualifiedNameContext _localctx = new QualifiedNameContext(_ctx, getState()); - enterRule(_localctx, 86, RULE_qualifiedName); + enterRule(_localctx, 92, RULE_qualifiedName); try { int _alt; enterOuterAlt(_localctx, 1); { - setState(690); + setState(716); _errHandler.sync(this); - _alt = getInterpreter().adaptivePredict(_input,87,_ctx); + _alt = getInterpreter().adaptivePredict(_input,91,_ctx); while ( _alt!=2 && _alt!=org.antlr.v4.runtime.atn.ATN.INVALID_ALT_NUMBER ) { if ( _alt==1 ) { { { - setState(685); + setState(711); identifier(); - setState(686); + setState(712); match(DOT); } } } - setState(692); + setState(718); _errHandler.sync(this); - _alt = getInterpreter().adaptivePredict(_input,87,_ctx); + _alt = getInterpreter().adaptivePredict(_input,91,_ctx); } - setState(693); + setState(719); identifier(); } } @@ -5379,15 +5722,15 @@ public T accept(ParseTreeVisitor visitor) { public final IdentifierContext identifier() throws RecognitionException { IdentifierContext _localctx = new IdentifierContext(_ctx, getState()); - enterRule(_localctx, 88, RULE_identifier); + enterRule(_localctx, 94, RULE_identifier); try { - setState(697); + setState(723); switch (_input.LA(1)) { case QUOTED_IDENTIFIER: case BACKQUOTED_IDENTIFIER: enterOuterAlt(_localctx, 1); { - setState(695); + setState(721); quoteIdentifier(); } break; @@ -5395,13 +5738,21 @@ public final IdentifierContext identifier() throws RecognitionException { case ANALYZED: case CATALOGS: case COLUMNS: + case DAY: case DEBUG: case EXECUTABLE: case EXPLAIN: + case FIRST: case FORMAT: case FUNCTIONS: case GRAPHVIZ: + case HOUR: + case INTERVAL: + case LAST: + case LIMIT: case MAPPED: + case MINUTE: + case MONTH: case OPTIMIZED: case PARSED: case PHYSICAL: @@ -5409,6 +5760,7 @@ public final IdentifierContext identifier() throws RecognitionException { case RLIKE: case QUERY: case SCHEMAS: + case SECOND: case SHOW: case SYS: case TABLES: @@ -5416,11 +5768,12 @@ public final IdentifierContext identifier() throws RecognitionException { case TYPE: case TYPES: case VERIFY: + case YEAR: case IDENTIFIER: case DIGIT_IDENTIFIER: enterOuterAlt(_localctx, 2); { - setState(696); + setState(722); unquoteIdentifier(); } break; @@ -5470,46 +5823,46 @@ public T accept(ParseTreeVisitor visitor) { public final TableIdentifierContext tableIdentifier() throws RecognitionException { TableIdentifierContext _localctx = new TableIdentifierContext(_ctx, getState()); - enterRule(_localctx, 90, RULE_tableIdentifier); + enterRule(_localctx, 96, RULE_tableIdentifier); int _la; try { - setState(711); + setState(737); _errHandler.sync(this); - switch ( getInterpreter().adaptivePredict(_input,91,_ctx) ) { + switch ( getInterpreter().adaptivePredict(_input,95,_ctx) ) { case 1: enterOuterAlt(_localctx, 1); { - setState(702); + setState(728); _la = _input.LA(1); - if ((((_la) & ~0x3f) == 0 && ((1L << _la) & ((1L << ANALYZE) | (1L << ANALYZED) | (1L << CATALOGS) | (1L << COLUMNS) | (1L << DEBUG) | (1L << EXECUTABLE) | (1L << EXPLAIN) | (1L << FORMAT) | (1L << FUNCTIONS) | (1L << GRAPHVIZ) | (1L << MAPPED) | (1L << OPTIMIZED) | (1L << PARSED) | (1L << PHYSICAL) | (1L << PLAN) | (1L << RLIKE) | (1L << QUERY) | (1L << SCHEMAS))) != 0) || ((((_la - 64)) & ~0x3f) == 0 && ((1L << (_la - 64)) & ((1L << (SHOW - 64)) | (1L << (SYS - 64)) | (1L << (TABLES - 64)) | (1L << (TEXT - 64)) | (1L << (TYPE - 64)) | (1L << (TYPES - 64)) | (1L << (VERIFY - 64)) | (1L << (IDENTIFIER - 64)) | (1L << (DIGIT_IDENTIFIER - 64)) | (1L << (QUOTED_IDENTIFIER - 64)) | (1L << (BACKQUOTED_IDENTIFIER - 64)))) != 0)) { + if ((((_la) & ~0x3f) == 0 && ((1L << _la) & ((1L << ANALYZE) | (1L << ANALYZED) | (1L << CATALOGS) | (1L << COLUMNS) | (1L << DAY) | (1L << DEBUG) | (1L << EXECUTABLE) | (1L << EXPLAIN) | (1L << FIRST) | (1L << FORMAT) | (1L << FUNCTIONS) | (1L << GRAPHVIZ) | (1L << HOUR) | (1L << INTERVAL) | (1L << LAST) | (1L << LIMIT) | (1L << MAPPED) | (1L << MINUTE) | (1L << MONTH) | (1L << OPTIMIZED))) != 0) || ((((_la - 65)) & ~0x3f) == 0 && ((1L << (_la - 65)) & ((1L << (PARSED - 65)) | (1L << (PHYSICAL - 65)) | (1L << (PLAN - 65)) | (1L << (RLIKE - 65)) | (1L << (QUERY - 65)) | (1L << (SCHEMAS - 65)) | (1L << (SECOND - 65)) | (1L << (SHOW - 65)) | (1L << (SYS - 65)) | (1L << (TABLES - 65)) | (1L << (TEXT - 65)) | (1L << (TYPE - 65)) | (1L << (TYPES - 65)) | (1L << (VERIFY - 65)) | (1L << (YEAR - 65)) | (1L << (IDENTIFIER - 65)) | (1L << (DIGIT_IDENTIFIER - 65)) | (1L << (QUOTED_IDENTIFIER - 65)) | (1L << (BACKQUOTED_IDENTIFIER - 65)))) != 0)) { { - setState(699); + setState(725); ((TableIdentifierContext)_localctx).catalog = identifier(); - setState(700); + setState(726); match(T__3); } } - setState(704); + setState(730); match(TABLE_IDENTIFIER); } break; case 2: enterOuterAlt(_localctx, 2); { - setState(708); + setState(734); _errHandler.sync(this); - switch ( getInterpreter().adaptivePredict(_input,90,_ctx) ) { + switch ( getInterpreter().adaptivePredict(_input,94,_ctx) ) { case 1: { - setState(705); + setState(731); ((TableIdentifierContext)_localctx).catalog = identifier(); - setState(706); + setState(732); match(T__3); } break; } - setState(710); + setState(736); ((TableIdentifierContext)_localctx).name = identifier(); } break; @@ -5574,15 +5927,15 @@ public T accept(ParseTreeVisitor visitor) { public final QuoteIdentifierContext quoteIdentifier() throws RecognitionException { QuoteIdentifierContext _localctx = new QuoteIdentifierContext(_ctx, getState()); - enterRule(_localctx, 92, RULE_quoteIdentifier); + enterRule(_localctx, 98, RULE_quoteIdentifier); try { - setState(715); + setState(741); switch (_input.LA(1)) { case QUOTED_IDENTIFIER: _localctx = new QuotedIdentifierContext(_localctx); enterOuterAlt(_localctx, 1); { - setState(713); + setState(739); match(QUOTED_IDENTIFIER); } break; @@ -5590,7 +5943,7 @@ public final QuoteIdentifierContext quoteIdentifier() throws RecognitionExceptio _localctx = new BackQuotedIdentifierContext(_localctx); enterOuterAlt(_localctx, 2); { - setState(714); + setState(740); match(BACKQUOTED_IDENTIFIER); } break; @@ -5660,15 +6013,15 @@ public T accept(ParseTreeVisitor visitor) { public final UnquoteIdentifierContext unquoteIdentifier() throws RecognitionException { UnquoteIdentifierContext _localctx = new UnquoteIdentifierContext(_ctx, getState()); - enterRule(_localctx, 94, RULE_unquoteIdentifier); + enterRule(_localctx, 100, RULE_unquoteIdentifier); try { - setState(720); + setState(746); switch (_input.LA(1)) { case IDENTIFIER: _localctx = new UnquotedIdentifierContext(_localctx); enterOuterAlt(_localctx, 1); { - setState(717); + setState(743); match(IDENTIFIER); } break; @@ -5676,13 +6029,21 @@ public final UnquoteIdentifierContext unquoteIdentifier() throws RecognitionExce case ANALYZED: case CATALOGS: case COLUMNS: + case DAY: case DEBUG: case EXECUTABLE: case EXPLAIN: + case FIRST: case FORMAT: case FUNCTIONS: case GRAPHVIZ: + case HOUR: + case INTERVAL: + case LAST: + case LIMIT: case MAPPED: + case MINUTE: + case MONTH: case OPTIMIZED: case PARSED: case PHYSICAL: @@ -5690,6 +6051,7 @@ public final UnquoteIdentifierContext unquoteIdentifier() throws RecognitionExce case RLIKE: case QUERY: case SCHEMAS: + case SECOND: case SHOW: case SYS: case TABLES: @@ -5697,10 +6059,11 @@ public final UnquoteIdentifierContext unquoteIdentifier() throws RecognitionExce case TYPE: case TYPES: case VERIFY: + case YEAR: _localctx = new UnquotedIdentifierContext(_localctx); enterOuterAlt(_localctx, 2); { - setState(718); + setState(744); nonReserved(); } break; @@ -5708,7 +6071,7 @@ public final UnquoteIdentifierContext unquoteIdentifier() throws RecognitionExce _localctx = new DigitIdentifierContext(_localctx); enterOuterAlt(_localctx, 3); { - setState(719); + setState(745); match(DIGIT_IDENTIFIER); } break; @@ -5775,15 +6138,15 @@ public T accept(ParseTreeVisitor visitor) { public final NumberContext number() throws RecognitionException { NumberContext _localctx = new NumberContext(_ctx, getState()); - enterRule(_localctx, 96, RULE_number); + enterRule(_localctx, 102, RULE_number); try { - setState(724); + setState(750); switch (_input.LA(1)) { case DECIMAL_VALUE: _localctx = new DecimalLiteralContext(_localctx); enterOuterAlt(_localctx, 1); { - setState(722); + setState(748); match(DECIMAL_VALUE); } break; @@ -5791,7 +6154,7 @@ public final NumberContext number() throws RecognitionException { _localctx = new IntegerLiteralContext(_localctx); enterOuterAlt(_localctx, 2); { - setState(723); + setState(749); match(INTEGER_VALUE); } break; @@ -5834,12 +6197,12 @@ public T accept(ParseTreeVisitor visitor) { public final StringContext string() throws RecognitionException { StringContext _localctx = new StringContext(_ctx, getState()); - enterRule(_localctx, 98, RULE_string); + enterRule(_localctx, 104, RULE_string); int _la; try { enterOuterAlt(_localctx, 1); { - setState(726); + setState(752); _la = _input.LA(1); if ( !(_la==PARAM || _la==STRING) ) { _errHandler.recoverInline(this); @@ -5864,13 +6227,21 @@ public static class NonReservedContext extends ParserRuleContext { public TerminalNode ANALYZED() { return getToken(SqlBaseParser.ANALYZED, 0); } public TerminalNode CATALOGS() { return getToken(SqlBaseParser.CATALOGS, 0); } public TerminalNode COLUMNS() { return getToken(SqlBaseParser.COLUMNS, 0); } + public TerminalNode DAY() { return getToken(SqlBaseParser.DAY, 0); } public TerminalNode DEBUG() { return getToken(SqlBaseParser.DEBUG, 0); } public TerminalNode EXECUTABLE() { return getToken(SqlBaseParser.EXECUTABLE, 0); } public TerminalNode EXPLAIN() { return getToken(SqlBaseParser.EXPLAIN, 0); } + public TerminalNode FIRST() { return getToken(SqlBaseParser.FIRST, 0); } public TerminalNode FORMAT() { return getToken(SqlBaseParser.FORMAT, 0); } public TerminalNode FUNCTIONS() { return getToken(SqlBaseParser.FUNCTIONS, 0); } public TerminalNode GRAPHVIZ() { return getToken(SqlBaseParser.GRAPHVIZ, 0); } + public TerminalNode HOUR() { return getToken(SqlBaseParser.HOUR, 0); } + public TerminalNode INTERVAL() { return getToken(SqlBaseParser.INTERVAL, 0); } + public TerminalNode LAST() { return getToken(SqlBaseParser.LAST, 0); } + public TerminalNode LIMIT() { return getToken(SqlBaseParser.LIMIT, 0); } public TerminalNode MAPPED() { return getToken(SqlBaseParser.MAPPED, 0); } + public TerminalNode MINUTE() { return getToken(SqlBaseParser.MINUTE, 0); } + public TerminalNode MONTH() { return getToken(SqlBaseParser.MONTH, 0); } public TerminalNode OPTIMIZED() { return getToken(SqlBaseParser.OPTIMIZED, 0); } public TerminalNode PARSED() { return getToken(SqlBaseParser.PARSED, 0); } public TerminalNode PHYSICAL() { return getToken(SqlBaseParser.PHYSICAL, 0); } @@ -5878,6 +6249,7 @@ public static class NonReservedContext extends ParserRuleContext { public TerminalNode QUERY() { return getToken(SqlBaseParser.QUERY, 0); } public TerminalNode RLIKE() { return getToken(SqlBaseParser.RLIKE, 0); } public TerminalNode SCHEMAS() { return getToken(SqlBaseParser.SCHEMAS, 0); } + public TerminalNode SECOND() { return getToken(SqlBaseParser.SECOND, 0); } public TerminalNode SHOW() { return getToken(SqlBaseParser.SHOW, 0); } public TerminalNode SYS() { return getToken(SqlBaseParser.SYS, 0); } public TerminalNode TABLES() { return getToken(SqlBaseParser.TABLES, 0); } @@ -5885,6 +6257,7 @@ public static class NonReservedContext extends ParserRuleContext { public TerminalNode TYPE() { return getToken(SqlBaseParser.TYPE, 0); } public TerminalNode TYPES() { return getToken(SqlBaseParser.TYPES, 0); } public TerminalNode VERIFY() { return getToken(SqlBaseParser.VERIFY, 0); } + public TerminalNode YEAR() { return getToken(SqlBaseParser.YEAR, 0); } public NonReservedContext(ParserRuleContext parent, int invokingState) { super(parent, invokingState); } @@ -5906,14 +6279,14 @@ public T accept(ParseTreeVisitor visitor) { public final NonReservedContext nonReserved() throws RecognitionException { NonReservedContext _localctx = new NonReservedContext(_ctx, getState()); - enterRule(_localctx, 100, RULE_nonReserved); + enterRule(_localctx, 106, RULE_nonReserved); int _la; try { enterOuterAlt(_localctx, 1); { - setState(728); + setState(754); _la = _input.LA(1); - if ( !((((_la) & ~0x3f) == 0 && ((1L << _la) & ((1L << ANALYZE) | (1L << ANALYZED) | (1L << CATALOGS) | (1L << COLUMNS) | (1L << DEBUG) | (1L << EXECUTABLE) | (1L << EXPLAIN) | (1L << FORMAT) | (1L << FUNCTIONS) | (1L << GRAPHVIZ) | (1L << MAPPED) | (1L << OPTIMIZED) | (1L << PARSED) | (1L << PHYSICAL) | (1L << PLAN) | (1L << RLIKE) | (1L << QUERY) | (1L << SCHEMAS))) != 0) || ((((_la - 64)) & ~0x3f) == 0 && ((1L << (_la - 64)) & ((1L << (SHOW - 64)) | (1L << (SYS - 64)) | (1L << (TABLES - 64)) | (1L << (TEXT - 64)) | (1L << (TYPE - 64)) | (1L << (TYPES - 64)) | (1L << (VERIFY - 64)))) != 0)) ) { + if ( !((((_la) & ~0x3f) == 0 && ((1L << _la) & ((1L << ANALYZE) | (1L << ANALYZED) | (1L << CATALOGS) | (1L << COLUMNS) | (1L << DAY) | (1L << DEBUG) | (1L << EXECUTABLE) | (1L << EXPLAIN) | (1L << FIRST) | (1L << FORMAT) | (1L << FUNCTIONS) | (1L << GRAPHVIZ) | (1L << HOUR) | (1L << INTERVAL) | (1L << LAST) | (1L << LIMIT) | (1L << MAPPED) | (1L << MINUTE) | (1L << MONTH) | (1L << OPTIMIZED))) != 0) || ((((_la - 65)) & ~0x3f) == 0 && ((1L << (_la - 65)) & ((1L << (PARSED - 65)) | (1L << (PHYSICAL - 65)) | (1L << (PLAN - 65)) | (1L << (RLIKE - 65)) | (1L << (QUERY - 65)) | (1L << (SCHEMAS - 65)) | (1L << (SECOND - 65)) | (1L << (SHOW - 65)) | (1L << (SYS - 65)) | (1L << (TABLES - 65)) | (1L << (TEXT - 65)) | (1L << (TYPE - 65)) | (1L << (TYPES - 65)) | (1L << (VERIFY - 65)) | (1L << (YEAR - 65)))) != 0)) ) { _errHandler.recoverInline(this); } else { consume(); @@ -5962,293 +6335,306 @@ private boolean valueExpression_sempred(ValueExpressionContext _localctx, int pr } public static final String _serializedATN = - "\3\u0430\ud6d1\u8206\uad2d\u4417\uaef1\u8d80\uaadd\3p\u02dd\4\2\t\2\4"+ + "\3\u0430\ud6d1\u8206\uad2d\u4417\uaef1\u8d80\uaadd\3~\u02f7\4\2\t\2\4"+ "\3\t\3\4\4\t\4\4\5\t\5\4\6\t\6\4\7\t\7\4\b\t\b\4\t\t\t\4\n\t\n\4\13\t"+ "\13\4\f\t\f\4\r\t\r\4\16\t\16\4\17\t\17\4\20\t\20\4\21\t\21\4\22\t\22"+ "\4\23\t\23\4\24\t\24\4\25\t\25\4\26\t\26\4\27\t\27\4\30\t\30\4\31\t\31"+ "\4\32\t\32\4\33\t\33\4\34\t\34\4\35\t\35\4\36\t\36\4\37\t\37\4 \t \4!"+ "\t!\4\"\t\"\4#\t#\4$\t$\4%\t%\4&\t&\4\'\t\'\4(\t(\4)\t)\4*\t*\4+\t+\4"+ ",\t,\4-\t-\4.\t.\4/\t/\4\60\t\60\4\61\t\61\4\62\t\62\4\63\t\63\4\64\t"+ - "\64\3\2\3\2\3\2\3\3\3\3\3\3\3\4\3\4\3\4\3\4\3\4\3\4\3\4\3\4\3\4\7\4x\n"+ - "\4\f\4\16\4{\13\4\3\4\5\4~\n\4\3\4\3\4\3\4\3\4\3\4\3\4\3\4\7\4\u0087\n"+ - "\4\f\4\16\4\u008a\13\4\3\4\5\4\u008d\n\4\3\4\3\4\3\4\3\4\3\4\5\4\u0094"+ - "\n\4\3\4\3\4\3\4\3\4\3\4\5\4\u009b\n\4\3\4\3\4\3\4\5\4\u00a0\n\4\3\4\3"+ - "\4\3\4\5\4\u00a5\n\4\3\4\3\4\3\4\3\4\3\4\3\4\3\4\3\4\5\4\u00af\n\4\3\4"+ - "\3\4\5\4\u00b3\n\4\3\4\3\4\3\4\3\4\7\4\u00b9\n\4\f\4\16\4\u00bc\13\4\5"+ - "\4\u00be\n\4\3\4\3\4\3\4\3\4\5\4\u00c4\n\4\3\4\3\4\3\4\5\4\u00c9\n\4\3"+ - "\4\5\4\u00cc\n\4\3\4\3\4\3\4\3\4\3\4\5\4\u00d3\n\4\3\5\3\5\3\5\3\5\7\5"+ - "\u00d9\n\5\f\5\16\5\u00dc\13\5\5\5\u00de\n\5\3\5\3\5\3\6\3\6\3\6\3\6\3"+ - "\6\3\6\7\6\u00e8\n\6\f\6\16\6\u00eb\13\6\5\6\u00ed\n\6\3\6\5\6\u00f0\n"+ - "\6\3\7\3\7\3\7\3\7\3\7\5\7\u00f7\n\7\3\b\3\b\3\b\3\b\3\b\5\b\u00fe\n\b"+ - "\3\t\3\t\5\t\u0102\n\t\3\t\3\t\5\t\u0106\n\t\3\n\3\n\5\n\u010a\n\n\3\n"+ - "\3\n\3\n\7\n\u010f\n\n\f\n\16\n\u0112\13\n\3\n\5\n\u0115\n\n\3\n\3\n\5"+ - "\n\u0119\n\n\3\n\3\n\3\n\5\n\u011e\n\n\3\n\3\n\5\n\u0122\n\n\3\13\3\13"+ - "\3\13\3\13\7\13\u0128\n\13\f\13\16\13\u012b\13\13\3\f\5\f\u012e\n\f\3"+ - "\f\3\f\3\f\7\f\u0133\n\f\f\f\16\f\u0136\13\f\3\r\3\r\3\16\3\16\3\16\3"+ - "\16\7\16\u013e\n\16\f\16\16\16\u0141\13\16\5\16\u0143\n\16\3\16\3\16\5"+ - "\16\u0147\n\16\3\17\3\17\3\17\3\17\3\17\3\17\3\20\3\20\3\21\3\21\5\21"+ - "\u0153\n\21\3\21\5\21\u0156\n\21\3\22\3\22\7\22\u015a\n\22\f\22\16\22"+ - "\u015d\13\22\3\23\3\23\3\23\3\23\5\23\u0163\n\23\3\23\3\23\3\23\3\23\3"+ - "\23\5\23\u016a\n\23\3\24\5\24\u016d\n\24\3\24\3\24\5\24\u0171\n\24\3\24"+ - "\3\24\5\24\u0175\n\24\3\24\3\24\5\24\u0179\n\24\5\24\u017b\n\24\3\25\3"+ - "\25\3\25\3\25\3\25\3\25\3\25\7\25\u0184\n\25\f\25\16\25\u0187\13\25\3"+ - "\25\3\25\5\25\u018b\n\25\3\26\3\26\5\26\u018f\n\26\3\26\5\26\u0192\n\26"+ - "\3\26\3\26\3\26\3\26\5\26\u0198\n\26\3\26\5\26\u019b\n\26\3\26\3\26\3"+ - "\26\3\26\5\26\u01a1\n\26\3\26\5\26\u01a4\n\26\5\26\u01a6\n\26\3\27\3\27"+ - "\3\30\3\30\3\30\3\30\3\30\3\30\3\30\3\30\3\30\3\30\3\30\3\30\3\30\3\30"+ + "\64\4\65\t\65\4\66\t\66\4\67\t\67\3\2\3\2\3\2\3\3\3\3\3\3\3\4\3\4\3\4"+ + "\3\4\3\4\3\4\3\4\3\4\3\4\7\4~\n\4\f\4\16\4\u0081\13\4\3\4\5\4\u0084\n"+ + "\4\3\4\3\4\3\4\3\4\3\4\3\4\3\4\7\4\u008d\n\4\f\4\16\4\u0090\13\4\3\4\5"+ + "\4\u0093\n\4\3\4\3\4\3\4\3\4\3\4\5\4\u009a\n\4\3\4\3\4\3\4\3\4\3\4\5\4"+ + "\u00a1\n\4\3\4\3\4\3\4\5\4\u00a6\n\4\3\4\3\4\3\4\5\4\u00ab\n\4\3\4\3\4"+ + "\3\4\3\4\3\4\3\4\3\4\3\4\5\4\u00b5\n\4\3\4\3\4\5\4\u00b9\n\4\3\4\3\4\3"+ + "\4\3\4\7\4\u00bf\n\4\f\4\16\4\u00c2\13\4\5\4\u00c4\n\4\3\4\3\4\3\4\3\4"+ + "\5\4\u00ca\n\4\3\4\3\4\3\4\5\4\u00cf\n\4\3\4\5\4\u00d2\n\4\3\4\3\4\3\4"+ + "\3\4\3\4\5\4\u00d9\n\4\3\5\3\5\3\5\3\5\7\5\u00df\n\5\f\5\16\5\u00e2\13"+ + "\5\5\5\u00e4\n\5\3\5\3\5\3\6\3\6\3\6\3\6\3\6\3\6\7\6\u00ee\n\6\f\6\16"+ + "\6\u00f1\13\6\5\6\u00f3\n\6\3\6\5\6\u00f6\n\6\3\7\3\7\3\7\3\7\3\7\5\7"+ + "\u00fd\n\7\3\b\3\b\3\b\3\b\3\b\5\b\u0104\n\b\3\t\3\t\5\t\u0108\n\t\3\t"+ + "\3\t\5\t\u010c\n\t\3\n\3\n\5\n\u0110\n\n\3\n\3\n\3\n\7\n\u0115\n\n\f\n"+ + "\16\n\u0118\13\n\3\n\5\n\u011b\n\n\3\n\3\n\5\n\u011f\n\n\3\n\3\n\3\n\5"+ + "\n\u0124\n\n\3\n\3\n\5\n\u0128\n\n\3\13\3\13\3\13\3\13\7\13\u012e\n\13"+ + "\f\13\16\13\u0131\13\13\3\f\5\f\u0134\n\f\3\f\3\f\3\f\7\f\u0139\n\f\f"+ + "\f\16\f\u013c\13\f\3\r\3\r\3\16\3\16\3\16\3\16\7\16\u0144\n\16\f\16\16"+ + "\16\u0147\13\16\5\16\u0149\n\16\3\16\3\16\5\16\u014d\n\16\3\17\3\17\3"+ + "\17\3\17\3\17\3\17\3\20\3\20\3\21\3\21\5\21\u0159\n\21\3\21\5\21\u015c"+ + "\n\21\3\22\3\22\7\22\u0160\n\22\f\22\16\22\u0163\13\22\3\23\3\23\3\23"+ + "\3\23\5\23\u0169\n\23\3\23\3\23\3\23\3\23\3\23\5\23\u0170\n\23\3\24\5"+ + "\24\u0173\n\24\3\24\3\24\5\24\u0177\n\24\3\24\3\24\5\24\u017b\n\24\3\24"+ + "\3\24\5\24\u017f\n\24\5\24\u0181\n\24\3\25\3\25\3\25\3\25\3\25\3\25\3"+ + "\25\7\25\u018a\n\25\f\25\16\25\u018d\13\25\3\25\3\25\5\25\u0191\n\25\3"+ + "\26\3\26\5\26\u0195\n\26\3\26\5\26\u0198\n\26\3\26\3\26\3\26\3\26\5\26"+ + "\u019e\n\26\3\26\5\26\u01a1\n\26\3\26\3\26\3\26\3\26\5\26\u01a7\n\26\3"+ + "\26\5\26\u01aa\n\26\5\26\u01ac\n\26\3\27\3\27\3\30\3\30\3\30\3\30\3\30"+ "\3\30\3\30\3\30\3\30\3\30\3\30\3\30\3\30\3\30\3\30\3\30\3\30\3\30\3\30"+ - "\3\30\3\30\3\30\5\30\u01c9\n\30\3\30\3\30\3\30\3\30\3\30\3\30\7\30\u01d1"+ - "\n\30\f\30\16\30\u01d4\13\30\3\31\3\31\7\31\u01d8\n\31\f\31\16\31\u01db"+ - "\13\31\3\32\3\32\5\32\u01df\n\32\3\33\5\33\u01e2\n\33\3\33\3\33\3\33\3"+ - "\33\3\33\3\33\5\33\u01ea\n\33\3\33\3\33\3\33\3\33\3\33\7\33\u01f1\n\33"+ - "\f\33\16\33\u01f4\13\33\3\33\3\33\3\33\5\33\u01f9\n\33\3\33\3\33\3\33"+ - "\3\33\3\33\3\33\5\33\u0201\n\33\3\33\3\33\3\33\5\33\u0206\n\33\3\33\3"+ - "\33\3\33\3\33\5\33\u020c\n\33\3\33\5\33\u020f\n\33\3\34\3\34\3\34\3\35"+ - "\3\35\5\35\u0216\n\35\3\36\3\36\3\36\3\36\3\36\3\36\5\36\u021e\n\36\3"+ - "\37\3\37\3\37\3\37\5\37\u0224\n\37\3\37\3\37\3\37\3\37\3\37\3\37\3\37"+ - "\3\37\3\37\3\37\7\37\u0230\n\37\f\37\16\37\u0233\13\37\3 \3 \3 \3 \3 "+ - "\3 \5 \u023b\n \3 \3 \3 \3 \3 \3 \3 \3 \3 \3 \3 \5 \u0248\n \3!\3!\3!"+ - "\3!\3!\3!\3!\3!\3!\3!\5!\u0254\n!\3\"\3\"\3\"\3\"\3\"\3\"\3\"\3#\3#\3"+ - "#\3#\3#\3#\3#\3$\3$\3$\3$\3$\5$\u0269\n$\3%\3%\3%\3%\3%\3%\3%\3&\3&\3"+ - "&\3&\3&\5&\u0277\n&\3\'\3\'\3\'\5\'\u027c\n\'\3\'\3\'\3\'\7\'\u0281\n"+ - "\'\f\'\16\'\u0284\13\'\5\'\u0286\n\'\3\'\3\'\3(\3(\3(\5(\u028d\n(\3)\3"+ - ")\3)\3)\6)\u0293\n)\r)\16)\u0294\3)\3)\3)\3)\3)\3)\3)\3)\3)\3)\3)\3)\3"+ - ")\3)\3)\3)\3)\5)\u02a8\n)\3*\3*\3+\3+\3,\3,\3-\3-\3-\7-\u02b3\n-\f-\16"+ - "-\u02b6\13-\3-\3-\3.\3.\5.\u02bc\n.\3/\3/\3/\5/\u02c1\n/\3/\3/\3/\3/\5"+ - "/\u02c7\n/\3/\5/\u02ca\n/\3\60\3\60\5\60\u02ce\n\60\3\61\3\61\3\61\5\61"+ - "\u02d3\n\61\3\62\3\62\5\62\u02d7\n\62\3\63\3\63\3\64\3\64\3\64\2\4.<\65"+ - "\2\4\6\b\n\f\16\20\22\24\26\30\32\34\36 \"$&(*,.\60\62\64\668:<>@BDFH"+ - "JLNPRTVXZ\\^`bdf\2\21\b\2\7\7\t\t\32\32//\66\66::\4\2$$FF\4\2\t\t\66\66"+ - "\4\2!!\'\'\3\2\26\27\4\2\7\7ee\4\2\r\r\26\26\4\2\37\37++\4\2\7\7\30\30"+ - "\3\2\\]\3\2^`\3\2V[\4\2\36\36GG\3\2cd\21\2\b\t\22\23\25\25\32\32\34\34"+ - " #$//\66\66:<>@BCEFHIKK\u0334\2h\3\2\2\2\4k\3\2\2\2\6\u00d2\3\2\2\2\b"+ - "\u00dd\3\2\2\2\n\u00e1\3\2\2\2\f\u00f6\3\2\2\2\16\u00fd\3\2\2\2\20\u00ff"+ - "\3\2\2\2\22\u0107\3\2\2\2\24\u0123\3\2\2\2\26\u012d\3\2\2\2\30\u0137\3"+ - "\2\2\2\32\u0146\3\2\2\2\34\u0148\3\2\2\2\36\u014e\3\2\2\2 \u0150\3\2\2"+ - "\2\"\u0157\3\2\2\2$\u0169\3\2\2\2&\u017a\3\2\2\2(\u018a\3\2\2\2*\u01a5"+ - "\3\2\2\2,\u01a7\3\2\2\2.\u01c8\3\2\2\2\60\u01d9\3\2\2\2\62\u01dc\3\2\2"+ - "\2\64\u020e\3\2\2\2\66\u0210\3\2\2\28\u0213\3\2\2\2:\u021d\3\2\2\2<\u0223"+ - "\3\2\2\2>\u0247\3\2\2\2@\u0253\3\2\2\2B\u0255\3\2\2\2D\u025c\3\2\2\2F"+ - "\u0268\3\2\2\2H\u026a\3\2\2\2J\u0276\3\2\2\2L\u0278\3\2\2\2N\u028c\3\2"+ - "\2\2P\u02a7\3\2\2\2R\u02a9\3\2\2\2T\u02ab\3\2\2\2V\u02ad\3\2\2\2X\u02b4"+ - "\3\2\2\2Z\u02bb\3\2\2\2\\\u02c9\3\2\2\2^\u02cd\3\2\2\2`\u02d2\3\2\2\2"+ - "b\u02d6\3\2\2\2d\u02d8\3\2\2\2f\u02da\3\2\2\2hi\5\6\4\2ij\7\2\2\3j\3\3"+ - "\2\2\2kl\5,\27\2lm\7\2\2\3m\5\3\2\2\2n\u00d3\5\b\5\2o}\7\34\2\2py\7\3"+ - "\2\2qr\7<\2\2rx\t\2\2\2st\7 \2\2tx\t\3\2\2uv\7K\2\2vx\5T+\2wq\3\2\2\2"+ - "ws\3\2\2\2wu\3\2\2\2x{\3\2\2\2yw\3\2\2\2yz\3\2\2\2z|\3\2\2\2{y\3\2\2\2"+ - "|~\7\4\2\2}p\3\2\2\2}~\3\2\2\2~\177\3\2\2\2\177\u00d3\5\6\4\2\u0080\u008c"+ - "\7\25\2\2\u0081\u0088\7\3\2\2\u0082\u0083\7<\2\2\u0083\u0087\t\4\2\2\u0084"+ - "\u0085\7 \2\2\u0085\u0087\t\3\2\2\u0086\u0082\3\2\2\2\u0086\u0084\3\2"+ - "\2\2\u0087\u008a\3\2\2\2\u0088\u0086\3\2\2\2\u0088\u0089\3\2\2\2\u0089"+ - "\u008b\3\2\2\2\u008a\u0088\3\2\2\2\u008b\u008d\7\4\2\2\u008c\u0081\3\2"+ - "\2\2\u008c\u008d\3\2\2\2\u008d\u008e\3\2\2\2\u008e\u00d3\5\6\4\2\u008f"+ - "\u0090\7B\2\2\u0090\u0093\7E\2\2\u0091\u0094\5\66\34\2\u0092\u0094\5\\"+ - "/\2\u0093\u0091\3\2\2\2\u0093\u0092\3\2\2\2\u0093\u0094\3\2\2\2\u0094"+ - "\u00d3\3\2\2\2\u0095\u0096\7B\2\2\u0096\u0097\7\23\2\2\u0097\u009a\t\5"+ - "\2\2\u0098\u009b\5\66\34\2\u0099\u009b\5\\/\2\u009a\u0098\3\2\2\2\u009a"+ - "\u0099\3\2\2\2\u009b\u00d3\3\2\2\2\u009c\u009f\t\6\2\2\u009d\u00a0\5\66"+ - "\34\2\u009e\u00a0\5\\/\2\u009f\u009d\3\2\2\2\u009f\u009e\3\2\2\2\u00a0"+ - "\u00d3\3\2\2\2\u00a1\u00a2\7B\2\2\u00a2\u00a4\7#\2\2\u00a3\u00a5\5\66"+ - "\34\2\u00a4\u00a3\3\2\2\2\u00a4\u00a5\3\2\2\2\u00a5\u00d3\3\2\2\2\u00a6"+ - "\u00a7\7B\2\2\u00a7\u00d3\7@\2\2\u00a8\u00a9\7C\2\2\u00a9\u00d3\7\22\2"+ - "\2\u00aa\u00ab\7C\2\2\u00ab\u00ae\7E\2\2\u00ac\u00ad\7\21\2\2\u00ad\u00af"+ - "\5\66\34\2\u00ae\u00ac\3\2\2\2\u00ae\u00af\3\2\2\2\u00af\u00b2\3\2\2\2"+ - "\u00b0\u00b3\5\66\34\2\u00b1\u00b3\5\\/\2\u00b2\u00b0\3\2\2\2\u00b2\u00b1"+ - "\3\2\2\2\u00b2\u00b3\3\2\2\2\u00b3\u00bd\3\2\2\2\u00b4\u00b5\7H\2\2\u00b5"+ - "\u00ba\5d\63\2\u00b6\u00b7\7\5\2\2\u00b7\u00b9\5d\63\2\u00b8\u00b6\3\2"+ - "\2\2\u00b9\u00bc\3\2\2\2\u00ba\u00b8\3\2\2\2\u00ba\u00bb\3\2\2\2\u00bb"+ - "\u00be\3\2\2\2\u00bc\u00ba\3\2\2\2\u00bd\u00b4\3\2\2\2\u00bd\u00be\3\2"+ - "\2\2\u00be\u00d3\3\2\2\2\u00bf\u00c0\7C\2\2\u00c0\u00c3\7\23\2\2\u00c1"+ - "\u00c2\7\21\2\2\u00c2\u00c4\5d\63\2\u00c3\u00c1\3\2\2\2\u00c3\u00c4\3"+ - "\2\2\2\u00c4\u00c8\3\2\2\2\u00c5\u00c6\7D\2\2\u00c6\u00c9\5\66\34\2\u00c7"+ - "\u00c9\5\\/\2\u00c8\u00c5\3\2\2\2\u00c8\u00c7\3\2\2\2\u00c8\u00c9\3\2"+ - "\2\2\u00c9\u00cb\3\2\2\2\u00ca\u00cc\5\66\34\2\u00cb\u00ca\3\2\2\2\u00cb"+ - "\u00cc\3\2\2\2\u00cc\u00d3\3\2\2\2\u00cd\u00ce\7C\2\2\u00ce\u00d3\7I\2"+ - "\2\u00cf\u00d0\7C\2\2\u00d0\u00d1\7D\2\2\u00d1\u00d3\7I\2\2\u00d2n\3\2"+ - "\2\2\u00d2o\3\2\2\2\u00d2\u0080\3\2\2\2\u00d2\u008f\3\2\2\2\u00d2\u0095"+ - "\3\2\2\2\u00d2\u009c\3\2\2\2\u00d2\u00a1\3\2\2\2\u00d2\u00a6\3\2\2\2\u00d2"+ - "\u00a8\3\2\2\2\u00d2\u00aa\3\2\2\2\u00d2\u00bf\3\2\2\2\u00d2\u00cd\3\2"+ - "\2\2\u00d2\u00cf\3\2\2\2\u00d3\7\3\2\2\2\u00d4\u00d5\7M\2\2\u00d5\u00da"+ - "\5\34\17\2\u00d6\u00d7\7\5\2\2\u00d7\u00d9\5\34\17\2\u00d8\u00d6\3\2\2"+ - "\2\u00d9\u00dc\3\2\2\2\u00da\u00d8\3\2\2\2\u00da\u00db\3\2\2\2\u00db\u00de"+ - "\3\2\2\2\u00dc\u00da\3\2\2\2\u00dd\u00d4\3\2\2\2\u00dd\u00de\3\2\2\2\u00de"+ - "\u00df\3\2\2\2\u00df\u00e0\5\n\6\2\u00e0\t\3\2\2\2\u00e1\u00ec\5\16\b"+ - "\2\u00e2\u00e3\78\2\2\u00e3\u00e4\7\17\2\2\u00e4\u00e9\5\20\t\2\u00e5"+ - "\u00e6\7\5\2\2\u00e6\u00e8\5\20\t\2\u00e7\u00e5\3\2\2\2\u00e8\u00eb\3"+ - "\2\2\2\u00e9\u00e7\3\2\2\2\u00e9\u00ea\3\2\2\2\u00ea\u00ed\3\2\2\2\u00eb"+ - "\u00e9\3\2\2\2\u00ec\u00e2\3\2\2\2\u00ec\u00ed\3\2\2\2\u00ed\u00ef\3\2"+ - "\2\2\u00ee\u00f0\5\f\7\2\u00ef\u00ee\3\2\2\2\u00ef\u00f0\3\2\2\2\u00f0"+ - "\13\3\2\2\2\u00f1\u00f2\7.\2\2\u00f2\u00f7\t\7\2\2\u00f3\u00f4\7P\2\2"+ - "\u00f4\u00f5\t\7\2\2\u00f5\u00f7\7U\2\2\u00f6\u00f1\3\2\2\2\u00f6\u00f3"+ - "\3\2\2\2\u00f7\r\3\2\2\2\u00f8\u00fe\5\22\n\2\u00f9\u00fa\7\3\2\2\u00fa"+ - "\u00fb\5\n\6\2\u00fb\u00fc\7\4\2\2\u00fc\u00fe\3\2\2\2\u00fd\u00f8\3\2"+ - "\2\2\u00fd\u00f9\3\2\2\2\u00fe\17\3\2\2\2\u00ff\u0101\5,\27\2\u0100\u0102"+ - "\t\b\2\2\u0101\u0100\3\2\2\2\u0101\u0102\3\2\2\2\u0102\u0105\3\2\2\2\u0103"+ - "\u0104\7\64\2\2\u0104\u0106\t\t\2\2\u0105\u0103\3\2\2\2\u0105\u0106\3"+ - "\2\2\2\u0106\21\3\2\2\2\u0107\u0109\7A\2\2\u0108\u010a\5\36\20\2\u0109"+ - "\u0108\3\2\2\2\u0109\u010a\3\2\2\2\u010a\u010b\3\2\2\2\u010b\u0110\5 "+ - "\21\2\u010c\u010d\7\5\2\2\u010d\u010f\5 \21\2\u010e\u010c\3\2\2\2\u010f"+ - "\u0112\3\2\2\2\u0110\u010e\3\2\2\2\u0110\u0111\3\2\2\2\u0111\u0114\3\2"+ - "\2\2\u0112\u0110\3\2\2\2\u0113\u0115\5\24\13\2\u0114\u0113\3\2\2\2\u0114"+ - "\u0115\3\2\2\2\u0115\u0118\3\2\2\2\u0116\u0117\7L\2\2\u0117\u0119\5.\30"+ - "\2\u0118\u0116\3\2\2\2\u0118\u0119\3\2\2\2\u0119\u011d\3\2\2\2\u011a\u011b"+ - "\7%\2\2\u011b\u011c\7\17\2\2\u011c\u011e\5\26\f\2\u011d\u011a\3\2\2\2"+ - "\u011d\u011e\3\2\2\2\u011e\u0121\3\2\2\2\u011f\u0120\7&\2\2\u0120\u0122"+ - "\5.\30\2\u0121\u011f\3\2\2\2\u0121\u0122\3\2\2\2\u0122\23\3\2\2\2\u0123"+ - "\u0124\7!\2\2\u0124\u0129\5\"\22\2\u0125\u0126\7\5\2\2\u0126\u0128\5\""+ - "\22\2\u0127\u0125\3\2\2\2\u0128\u012b\3\2\2\2\u0129\u0127\3\2\2\2\u0129"+ - "\u012a\3\2\2\2\u012a\25\3\2\2\2\u012b\u0129\3\2\2\2\u012c\u012e\5\36\20"+ - "\2\u012d\u012c\3\2\2\2\u012d\u012e\3\2\2\2\u012e\u012f\3\2\2\2\u012f\u0134"+ - "\5\30\r\2\u0130\u0131\7\5\2\2\u0131\u0133\5\30\r\2\u0132\u0130\3\2\2\2"+ - "\u0133\u0136\3\2\2\2\u0134\u0132\3\2\2\2\u0134\u0135\3\2\2\2\u0135\27"+ - "\3\2\2\2\u0136\u0134\3\2\2\2\u0137\u0138\5\32\16\2\u0138\31\3\2\2\2\u0139"+ - "\u0142\7\3\2\2\u013a\u013f\5,\27\2\u013b\u013c\7\5\2\2\u013c\u013e\5,"+ - "\27\2\u013d\u013b\3\2\2\2\u013e\u0141\3\2\2\2\u013f\u013d\3\2\2\2\u013f"+ - "\u0140\3\2\2\2\u0140\u0143\3\2\2\2\u0141\u013f\3\2\2\2\u0142\u013a\3\2"+ - "\2\2\u0142\u0143\3\2\2\2\u0143\u0144\3\2\2\2\u0144\u0147\7\4\2\2\u0145"+ - "\u0147\5,\27\2\u0146\u0139\3\2\2\2\u0146\u0145\3\2\2\2\u0147\33\3\2\2"+ - "\2\u0148\u0149\5Z.\2\u0149\u014a\7\f\2\2\u014a\u014b\7\3\2\2\u014b\u014c"+ - "\5\n\6\2\u014c\u014d\7\4\2\2\u014d\35\3\2\2\2\u014e\u014f\t\n\2\2\u014f"+ - "\37\3\2\2\2\u0150\u0155\5,\27\2\u0151\u0153\7\f\2\2\u0152\u0151\3\2\2"+ - "\2\u0152\u0153\3\2\2\2\u0153\u0154\3\2\2\2\u0154\u0156\5Z.\2\u0155\u0152"+ - "\3\2\2\2\u0155\u0156\3\2\2\2\u0156!\3\2\2\2\u0157\u015b\5*\26\2\u0158"+ - "\u015a\5$\23\2\u0159\u0158\3\2\2\2\u015a\u015d\3\2\2\2\u015b\u0159\3\2"+ - "\2\2\u015b\u015c\3\2\2\2\u015c#\3\2\2\2\u015d\u015b\3\2\2\2\u015e\u015f"+ - "\5&\24\2\u015f\u0160\7*\2\2\u0160\u0162\5*\26\2\u0161\u0163\5(\25\2\u0162"+ - "\u0161\3\2\2\2\u0162\u0163\3\2\2\2\u0163\u016a\3\2\2\2\u0164\u0165\7\61"+ - "\2\2\u0165\u0166\5&\24\2\u0166\u0167\7*\2\2\u0167\u0168\5*\26\2\u0168"+ - "\u016a\3\2\2\2\u0169\u015e\3\2\2\2\u0169\u0164\3\2\2\2\u016a%\3\2\2\2"+ - "\u016b\u016d\7(\2\2\u016c\u016b\3\2\2\2\u016c\u016d\3\2\2\2\u016d\u017b"+ - "\3\2\2\2\u016e\u0170\7,\2\2\u016f\u0171\79\2\2\u0170\u016f\3\2\2\2\u0170"+ - "\u0171\3\2\2\2\u0171\u017b\3\2\2\2\u0172\u0174\7=\2\2\u0173\u0175\79\2"+ - "\2\u0174\u0173\3\2\2\2\u0174\u0175\3\2\2\2\u0175\u017b\3\2\2\2\u0176\u0178"+ - "\7\"\2\2\u0177\u0179\79\2\2\u0178\u0177\3\2\2\2\u0178\u0179\3\2\2\2\u0179"+ - "\u017b\3\2\2\2\u017a\u016c\3\2\2\2\u017a\u016e\3\2\2\2\u017a\u0172\3\2"+ - "\2\2\u017a\u0176\3\2\2\2\u017b\'\3\2\2\2\u017c\u017d\7\65\2\2\u017d\u018b"+ - "\5.\30\2\u017e\u017f\7J\2\2\u017f\u0180\7\3\2\2\u0180\u0185\5Z.\2\u0181"+ - "\u0182\7\5\2\2\u0182\u0184\5Z.\2\u0183\u0181\3\2\2\2\u0184\u0187\3\2\2"+ - "\2\u0185\u0183\3\2\2\2\u0185\u0186\3\2\2\2\u0186\u0188\3\2\2\2\u0187\u0185"+ - "\3\2\2\2\u0188\u0189\7\4\2\2\u0189\u018b\3\2\2\2\u018a\u017c\3\2\2\2\u018a"+ - "\u017e\3\2\2\2\u018b)\3\2\2\2\u018c\u0191\5\\/\2\u018d\u018f\7\f\2\2\u018e"+ - "\u018d\3\2\2\2\u018e\u018f\3\2\2\2\u018f\u0190\3\2\2\2\u0190\u0192\5X"+ - "-\2\u0191\u018e\3\2\2\2\u0191\u0192\3\2\2\2\u0192\u01a6\3\2\2\2\u0193"+ - "\u0194\7\3\2\2\u0194\u0195\5\n\6\2\u0195\u019a\7\4\2\2\u0196\u0198\7\f"+ - "\2\2\u0197\u0196\3\2\2\2\u0197\u0198\3\2\2\2\u0198\u0199\3\2\2\2\u0199"+ - "\u019b\5X-\2\u019a\u0197\3\2\2\2\u019a\u019b\3\2\2\2\u019b\u01a6\3\2\2"+ - "\2\u019c\u019d\7\3\2\2\u019d\u019e\5\"\22\2\u019e\u01a3\7\4\2\2\u019f"+ - "\u01a1\7\f\2\2\u01a0\u019f\3\2\2\2\u01a0\u01a1\3\2\2\2\u01a1\u01a2\3\2"+ - "\2\2\u01a2\u01a4\5X-\2\u01a3\u01a0\3\2\2\2\u01a3\u01a4\3\2\2\2\u01a4\u01a6"+ - "\3\2\2\2\u01a5\u018c\3\2\2\2\u01a5\u0193\3\2\2\2\u01a5\u019c\3\2\2\2\u01a6"+ - "+\3\2\2\2\u01a7\u01a8\5.\30\2\u01a8-\3\2\2\2\u01a9\u01aa\b\30\1\2\u01aa"+ - "\u01ab\7\62\2\2\u01ab\u01c9\5.\30\n\u01ac\u01ad\7\33\2\2\u01ad\u01ae\7"+ - "\3\2\2\u01ae\u01af\5\b\5\2\u01af\u01b0\7\4\2\2\u01b0\u01c9\3\2\2\2\u01b1"+ - "\u01b2\7?\2\2\u01b2\u01b3\7\3\2\2\u01b3\u01b4\5d\63\2\u01b4\u01b5\5\60"+ - "\31\2\u01b5\u01b6\7\4\2\2\u01b6\u01c9\3\2\2\2\u01b7\u01b8\7\60\2\2\u01b8"+ - "\u01b9\7\3\2\2\u01b9\u01ba\5X-\2\u01ba\u01bb\7\5\2\2\u01bb\u01bc\5d\63"+ - "\2\u01bc\u01bd\5\60\31\2\u01bd\u01be\7\4\2\2\u01be\u01c9\3\2\2\2\u01bf"+ - "\u01c0\7\60\2\2\u01c0\u01c1\7\3\2\2\u01c1\u01c2\5d\63\2\u01c2\u01c3\7"+ - "\5\2\2\u01c3\u01c4\5d\63\2\u01c4\u01c5\5\60\31\2\u01c5\u01c6\7\4\2\2\u01c6"+ - "\u01c9\3\2\2\2\u01c7\u01c9\5\62\32\2\u01c8\u01a9\3\2\2\2\u01c8\u01ac\3"+ - "\2\2\2\u01c8\u01b1\3\2\2\2\u01c8\u01b7\3\2\2\2\u01c8\u01bf\3\2\2\2\u01c8"+ - "\u01c7\3\2\2\2\u01c9\u01d2\3\2\2\2\u01ca\u01cb\f\4\2\2\u01cb\u01cc\7\n"+ - "\2\2\u01cc\u01d1\5.\30\5\u01cd\u01ce\f\3\2\2\u01ce\u01cf\7\67\2\2\u01cf"+ - "\u01d1\5.\30\4\u01d0\u01ca\3\2\2\2\u01d0\u01cd\3\2\2\2\u01d1\u01d4\3\2"+ - "\2\2\u01d2\u01d0\3\2\2\2\u01d2\u01d3\3\2\2\2\u01d3/\3\2\2\2\u01d4\u01d2"+ - "\3\2\2\2\u01d5\u01d6\7\5\2\2\u01d6\u01d8\5d\63\2\u01d7\u01d5\3\2\2\2\u01d8"+ - "\u01db\3\2\2\2\u01d9\u01d7\3\2\2\2\u01d9\u01da\3\2\2\2\u01da\61\3\2\2"+ - "\2\u01db\u01d9\3\2\2\2\u01dc\u01de\5<\37\2\u01dd\u01df\5\64\33\2\u01de"+ - "\u01dd\3\2\2\2\u01de\u01df\3\2\2\2\u01df\63\3\2\2\2\u01e0\u01e2\7\62\2"+ - "\2\u01e1\u01e0\3\2\2\2\u01e1\u01e2\3\2\2\2\u01e2\u01e3\3\2\2\2\u01e3\u01e4"+ - "\7\16\2\2\u01e4\u01e5\5<\37\2\u01e5\u01e6\7\n\2\2\u01e6\u01e7\5<\37\2"+ - "\u01e7\u020f\3\2\2\2\u01e8\u01ea\7\62\2\2\u01e9\u01e8\3\2\2\2\u01e9\u01ea"+ - "\3\2\2\2\u01ea\u01eb\3\2\2\2\u01eb\u01ec\7\'\2\2\u01ec\u01ed\7\3\2\2\u01ed"+ - "\u01f2\5,\27\2\u01ee\u01ef\7\5\2\2\u01ef\u01f1\5,\27\2\u01f0\u01ee\3\2"+ - "\2\2\u01f1\u01f4\3\2\2\2\u01f2\u01f0\3\2\2\2\u01f2\u01f3\3\2\2\2\u01f3"+ - "\u01f5\3\2\2\2\u01f4\u01f2\3\2\2\2\u01f5\u01f6\7\4\2\2\u01f6\u020f\3\2"+ - "\2\2\u01f7\u01f9\7\62\2\2\u01f8\u01f7\3\2\2\2\u01f8\u01f9\3\2\2\2\u01f9"+ - "\u01fa\3\2\2\2\u01fa\u01fb\7\'\2\2\u01fb\u01fc\7\3\2\2\u01fc\u01fd\5\b"+ - "\5\2\u01fd\u01fe\7\4\2\2\u01fe\u020f\3\2\2\2\u01ff\u0201\7\62\2\2\u0200"+ - "\u01ff\3\2\2\2\u0200\u0201\3\2\2\2\u0201\u0202\3\2\2\2\u0202\u0203\7-"+ - "\2\2\u0203\u020f\58\35\2\u0204\u0206\7\62\2\2\u0205\u0204\3\2\2\2\u0205"+ - "\u0206\3\2\2\2\u0206\u0207\3\2\2\2\u0207\u0208\7>\2\2\u0208\u020f\5d\63"+ - "\2\u0209\u020b\7)\2\2\u020a\u020c\7\62\2\2\u020b\u020a\3\2\2\2\u020b\u020c"+ - "\3\2\2\2\u020c\u020d\3\2\2\2\u020d\u020f\7\63\2\2\u020e\u01e1\3\2\2\2"+ - "\u020e\u01e9\3\2\2\2\u020e\u01f8\3\2\2\2\u020e\u0200\3\2\2\2\u020e\u0205"+ - "\3\2\2\2\u020e\u0209\3\2\2\2\u020f\65\3\2\2\2\u0210\u0211\7-\2\2\u0211"+ - "\u0212\58\35\2\u0212\67\3\2\2\2\u0213\u0215\5d\63\2\u0214\u0216\5:\36"+ - "\2\u0215\u0214\3\2\2\2\u0215\u0216\3\2\2\2\u02169\3\2\2\2\u0217\u0218"+ - "\7\31\2\2\u0218\u021e\5d\63\2\u0219\u021a\7N\2\2\u021a\u021b\5d\63\2\u021b"+ - "\u021c\7U\2\2\u021c\u021e\3\2\2\2\u021d\u0217\3\2\2\2\u021d\u0219\3\2"+ - "\2\2\u021e;\3\2\2\2\u021f\u0220\b\37\1\2\u0220\u0224\5> \2\u0221\u0222"+ - "\t\13\2\2\u0222\u0224\5<\37\6\u0223\u021f\3\2\2\2\u0223\u0221\3\2\2\2"+ - "\u0224\u0231\3\2\2\2\u0225\u0226\f\5\2\2\u0226\u0227\t\f\2\2\u0227\u0230"+ - "\5<\37\6\u0228\u0229\f\4\2\2\u0229\u022a\t\13\2\2\u022a\u0230\5<\37\5"+ - "\u022b\u022c\f\3\2\2\u022c\u022d\5R*\2\u022d\u022e\5<\37\4\u022e\u0230"+ - "\3\2\2\2\u022f\u0225\3\2\2\2\u022f\u0228\3\2\2\2\u022f\u022b\3\2\2\2\u0230"+ - "\u0233\3\2\2\2\u0231\u022f\3\2\2\2\u0231\u0232\3\2\2\2\u0232=\3\2\2\2"+ - "\u0233\u0231\3\2\2\2\u0234\u0248\5@!\2\u0235\u0248\5F$\2\u0236\u0248\5"+ - "P)\2\u0237\u0238\5X-\2\u0238\u0239\7b\2\2\u0239\u023b\3\2\2\2\u023a\u0237"+ - "\3\2\2\2\u023a\u023b\3\2\2\2\u023b\u023c\3\2\2\2\u023c\u0248\7^\2\2\u023d"+ - "\u0248\5J&\2\u023e\u023f\7\3\2\2\u023f\u0240\5\b\5\2\u0240\u0241\7\4\2"+ - "\2\u0241\u0248\3\2\2\2\u0242\u0248\5X-\2\u0243\u0244\7\3\2\2\u0244\u0245"+ - "\5,\27\2\u0245\u0246\7\4\2\2\u0246\u0248\3\2\2\2\u0247\u0234\3\2\2\2\u0247"+ - "\u0235\3\2\2\2\u0247\u0236\3\2\2\2\u0247\u023a\3\2\2\2\u0247\u023d\3\2"+ - "\2\2\u0247\u023e\3\2\2\2\u0247\u0242\3\2\2\2\u0247\u0243\3\2\2\2\u0248"+ - "?\3\2\2\2\u0249\u0254\5B\"\2\u024a\u024b\7O\2\2\u024b\u024c\5B\"\2\u024c"+ - "\u024d\7U\2\2\u024d\u0254\3\2\2\2\u024e\u0254\5D#\2\u024f\u0250\7O\2\2"+ - "\u0250\u0251\5D#\2\u0251\u0252\7U\2\2\u0252\u0254\3\2\2\2\u0253\u0249"+ - "\3\2\2\2\u0253\u024a\3\2\2\2\u0253\u024e\3\2\2\2\u0253\u024f\3\2\2\2\u0254"+ - "A\3\2\2\2\u0255\u0256\7\20\2\2\u0256\u0257\7\3\2\2\u0257\u0258\5,\27\2"+ - "\u0258\u0259\7\f\2\2\u0259\u025a\5V,\2\u025a\u025b\7\4\2\2\u025bC\3\2"+ - "\2\2\u025c\u025d\7\24\2\2\u025d\u025e\7\3\2\2\u025e\u025f\5,\27\2\u025f"+ - "\u0260\7\5\2\2\u0260\u0261\5V,\2\u0261\u0262\7\4\2\2\u0262E\3\2\2\2\u0263"+ - "\u0269\5H%\2\u0264\u0265\7O\2\2\u0265\u0266\5H%\2\u0266\u0267\7U\2\2\u0267"+ - "\u0269\3\2\2\2\u0268\u0263\3\2\2\2\u0268\u0264\3\2\2\2\u0269G\3\2\2\2"+ - "\u026a\u026b\7\35\2\2\u026b\u026c\7\3\2\2\u026c\u026d\5Z.\2\u026d\u026e"+ - "\7!\2\2\u026e\u026f\5<\37\2\u026f\u0270\7\4\2\2\u0270I\3\2\2\2\u0271\u0277"+ - "\5L\'\2\u0272\u0273\7O\2\2\u0273\u0274\5L\'\2\u0274\u0275\7U\2\2\u0275"+ - "\u0277\3\2\2\2\u0276\u0271\3\2\2\2\u0276\u0272\3\2\2\2\u0277K\3\2\2\2"+ - "\u0278\u0279\5N(\2\u0279\u0285\7\3\2\2\u027a\u027c\5\36\20\2\u027b\u027a"+ - "\3\2\2\2\u027b\u027c\3\2\2\2\u027c\u027d\3\2\2\2\u027d\u0282\5,\27\2\u027e"+ - "\u027f\7\5\2\2\u027f\u0281\5,\27\2\u0280\u027e\3\2\2\2\u0281\u0284\3\2"+ - "\2\2\u0282\u0280\3\2\2\2\u0282\u0283\3\2\2\2\u0283\u0286\3\2\2\2\u0284"+ - "\u0282\3\2\2\2\u0285\u027b\3\2\2\2\u0285\u0286\3\2\2\2\u0286\u0287\3\2"+ - "\2\2\u0287\u0288\7\4\2\2\u0288M\3\2\2\2\u0289\u028d\7,\2\2\u028a\u028d"+ - "\7=\2\2\u028b\u028d\5Z.\2\u028c\u0289\3\2\2\2\u028c\u028a\3\2\2\2\u028c"+ - "\u028b\3\2\2\2\u028dO\3\2\2\2\u028e\u02a8\7\63\2\2\u028f\u02a8\5b\62\2"+ - "\u0290\u02a8\5T+\2\u0291\u0293\7d\2\2\u0292\u0291\3\2\2\2\u0293\u0294"+ - "\3\2\2\2\u0294\u0292\3\2\2\2\u0294\u0295\3\2\2\2\u0295\u02a8\3\2\2\2\u0296"+ - "\u02a8\7c\2\2\u0297\u0298\7Q\2\2\u0298\u0299\5d\63\2\u0299\u029a\7U\2"+ - "\2\u029a\u02a8\3\2\2\2\u029b\u029c\7R\2\2\u029c\u029d\5d\63\2\u029d\u029e"+ - "\7U\2\2\u029e\u02a8\3\2\2\2\u029f\u02a0\7S\2\2\u02a0\u02a1\5d\63\2\u02a1"+ - "\u02a2\7U\2\2\u02a2\u02a8\3\2\2\2\u02a3\u02a4\7T\2\2\u02a4\u02a5\5d\63"+ - "\2\u02a5\u02a6\7U\2\2\u02a6\u02a8\3\2\2\2\u02a7\u028e\3\2\2\2\u02a7\u028f"+ - "\3\2\2\2\u02a7\u0290\3\2\2\2\u02a7\u0292\3\2\2\2\u02a7\u0296\3\2\2\2\u02a7"+ - "\u0297\3\2\2\2\u02a7\u029b\3\2\2\2\u02a7\u029f\3\2\2\2\u02a7\u02a3\3\2"+ - "\2\2\u02a8Q\3\2\2\2\u02a9\u02aa\t\r\2\2\u02aaS\3\2\2\2\u02ab\u02ac\t\16"+ - "\2\2\u02acU\3\2\2\2\u02ad\u02ae\5Z.\2\u02aeW\3\2\2\2\u02af\u02b0\5Z.\2"+ - "\u02b0\u02b1\7b\2\2\u02b1\u02b3\3\2\2\2\u02b2\u02af\3\2\2\2\u02b3\u02b6"+ - "\3\2\2\2\u02b4\u02b2\3\2\2\2\u02b4\u02b5\3\2\2\2\u02b5\u02b7\3\2\2\2\u02b6"+ - "\u02b4\3\2\2\2\u02b7\u02b8\5Z.\2\u02b8Y\3\2\2\2\u02b9\u02bc\5^\60\2\u02ba"+ - "\u02bc\5`\61\2\u02bb\u02b9\3\2\2\2\u02bb\u02ba\3\2\2\2\u02bc[\3\2\2\2"+ - "\u02bd\u02be\5Z.\2\u02be\u02bf\7\6\2\2\u02bf\u02c1\3\2\2\2\u02c0\u02bd"+ - "\3\2\2\2\u02c0\u02c1\3\2\2\2\u02c1\u02c2\3\2\2\2\u02c2\u02ca\7i\2\2\u02c3"+ - "\u02c4\5Z.\2\u02c4\u02c5\7\6\2\2\u02c5\u02c7\3\2\2\2\u02c6\u02c3\3\2\2"+ - "\2\u02c6\u02c7\3\2\2\2\u02c7\u02c8\3\2\2\2\u02c8\u02ca\5Z.\2\u02c9\u02c0"+ - "\3\2\2\2\u02c9\u02c6\3\2\2\2\u02ca]\3\2\2\2\u02cb\u02ce\7j\2\2\u02cc\u02ce"+ - "\7k\2\2\u02cd\u02cb\3\2\2\2\u02cd\u02cc\3\2\2\2\u02ce_\3\2\2\2\u02cf\u02d3"+ - "\7g\2\2\u02d0\u02d3\5f\64\2\u02d1\u02d3\7h\2\2\u02d2\u02cf\3\2\2\2\u02d2"+ - "\u02d0\3\2\2\2\u02d2\u02d1\3\2\2\2\u02d3a\3\2\2\2\u02d4\u02d7\7f\2\2\u02d5"+ - "\u02d7\7e\2\2\u02d6\u02d4\3\2\2\2\u02d6\u02d5\3\2\2\2\u02d7c\3\2\2\2\u02d8"+ - "\u02d9\t\17\2\2\u02d9e\3\2\2\2\u02da\u02db\t\20\2\2\u02dbg\3\2\2\2awy"+ - "}\u0086\u0088\u008c\u0093\u009a\u009f\u00a4\u00ae\u00b2\u00ba\u00bd\u00c3"+ - "\u00c8\u00cb\u00d2\u00da\u00dd\u00e9\u00ec\u00ef\u00f6\u00fd\u0101\u0105"+ - "\u0109\u0110\u0114\u0118\u011d\u0121\u0129\u012d\u0134\u013f\u0142\u0146"+ - "\u0152\u0155\u015b\u0162\u0169\u016c\u0170\u0174\u0178\u017a\u0185\u018a"+ - "\u018e\u0191\u0197\u019a\u01a0\u01a3\u01a5\u01c8\u01d0\u01d2\u01d9\u01de"+ - "\u01e1\u01e9\u01f2\u01f8\u0200\u0205\u020b\u020e\u0215\u021d\u0223\u022f"+ - "\u0231\u023a\u0247\u0253\u0268\u0276\u027b\u0282\u0285\u028c\u0294\u02a7"+ - "\u02b4\u02bb\u02c0\u02c6\u02c9\u02cd\u02d2\u02d6"; + "\3\30\3\30\3\30\3\30\3\30\3\30\3\30\3\30\3\30\3\30\3\30\3\30\5\30\u01cf"+ + "\n\30\3\30\3\30\3\30\3\30\3\30\3\30\7\30\u01d7\n\30\f\30\16\30\u01da\13"+ + "\30\3\31\3\31\7\31\u01de\n\31\f\31\16\31\u01e1\13\31\3\32\3\32\5\32\u01e5"+ + "\n\32\3\33\5\33\u01e8\n\33\3\33\3\33\3\33\3\33\3\33\3\33\5\33\u01f0\n"+ + "\33\3\33\3\33\3\33\3\33\3\33\7\33\u01f7\n\33\f\33\16\33\u01fa\13\33\3"+ + "\33\3\33\3\33\5\33\u01ff\n\33\3\33\3\33\3\33\3\33\3\33\3\33\5\33\u0207"+ + "\n\33\3\33\3\33\3\33\5\33\u020c\n\33\3\33\3\33\3\33\3\33\5\33\u0212\n"+ + "\33\3\33\5\33\u0215\n\33\3\34\3\34\3\34\3\35\3\35\5\35\u021c\n\35\3\36"+ + "\3\36\3\36\3\36\3\36\3\36\5\36\u0224\n\36\3\37\3\37\3\37\3\37\5\37\u022a"+ + "\n\37\3\37\3\37\3\37\3\37\3\37\3\37\3\37\3\37\3\37\3\37\7\37\u0236\n\37"+ + "\f\37\16\37\u0239\13\37\3 \3 \3 \3 \3 \3 \5 \u0241\n \3 \3 \3 \3 \3 \3"+ + " \3 \3 \3 \3 \3 \5 \u024e\n \3!\3!\3!\3!\3!\3!\3!\3!\3!\3!\5!\u025a\n"+ + "!\3\"\3\"\3\"\3\"\3\"\3\"\3\"\3#\3#\3#\3#\3#\3#\3#\3$\3$\3$\3$\3$\5$\u026f"+ + "\n$\3%\3%\3%\3%\3%\3%\3%\3&\3&\3&\3&\3&\5&\u027d\n&\3\'\3\'\3\'\5\'\u0282"+ + "\n\'\3\'\3\'\3\'\7\'\u0287\n\'\f\'\16\'\u028a\13\'\5\'\u028c\n\'\3\'\3"+ + "\'\3(\3(\3(\5(\u0293\n(\3)\3)\3)\3)\3)\6)\u029a\n)\r)\16)\u029b\3)\3)"+ + "\3)\3)\3)\3)\3)\3)\3)\3)\3)\3)\3)\3)\3)\3)\3)\5)\u02af\n)\3*\3*\3+\3+"+ + "\3,\3,\5,\u02b7\n,\3,\3,\5,\u02bb\n,\3,\3,\3,\5,\u02c0\n,\3-\3-\5-\u02c4"+ + "\n-\3.\3.\3/\3/\3\60\3\60\3\60\7\60\u02cd\n\60\f\60\16\60\u02d0\13\60"+ + "\3\60\3\60\3\61\3\61\5\61\u02d6\n\61\3\62\3\62\3\62\5\62\u02db\n\62\3"+ + "\62\3\62\3\62\3\62\5\62\u02e1\n\62\3\62\5\62\u02e4\n\62\3\63\3\63\5\63"+ + "\u02e8\n\63\3\64\3\64\3\64\5\64\u02ed\n\64\3\65\3\65\5\65\u02f1\n\65\3"+ + "\66\3\66\3\67\3\67\3\67\2\4.<8\2\4\6\b\n\f\16\20\22\24\26\30\32\34\36"+ + " \"$&(*,.\60\62\64\668:<>@BDFHJLNPRTVXZ\\^`bdfhjl\2\22\b\2\7\7\t\t\34"+ + "\34\64\64??CC\4\2&&QQ\4\2\t\t??\4\2##++\3\2\30\31\4\2\7\7ss\4\2\r\r\30"+ + "\30\4\2!!\60\60\4\2\7\7\32\32\3\2jk\3\2ln\3\2di\4\2 RR\7\2\25\26)*\66"+ + "9JKZ[\3\2qr\30\2\b\t\22\23\25\25\27\27\34\34\36\36!\"%&))--\60\60\63\64"+ + "\66\6688??CEGJMNPQTUWWZZ\u0350\2n\3\2\2\2\4q\3\2\2\2\6\u00d8\3\2\2\2\b"+ + "\u00e3\3\2\2\2\n\u00e7\3\2\2\2\f\u00fc\3\2\2\2\16\u0103\3\2\2\2\20\u0105"+ + "\3\2\2\2\22\u010d\3\2\2\2\24\u0129\3\2\2\2\26\u0133\3\2\2\2\30\u013d\3"+ + "\2\2\2\32\u014c\3\2\2\2\34\u014e\3\2\2\2\36\u0154\3\2\2\2 \u0156\3\2\2"+ + "\2\"\u015d\3\2\2\2$\u016f\3\2\2\2&\u0180\3\2\2\2(\u0190\3\2\2\2*\u01ab"+ + "\3\2\2\2,\u01ad\3\2\2\2.\u01ce\3\2\2\2\60\u01df\3\2\2\2\62\u01e2\3\2\2"+ + "\2\64\u0214\3\2\2\2\66\u0216\3\2\2\28\u0219\3\2\2\2:\u0223\3\2\2\2<\u0229"+ + "\3\2\2\2>\u024d\3\2\2\2@\u0259\3\2\2\2B\u025b\3\2\2\2D\u0262\3\2\2\2F"+ + "\u026e\3\2\2\2H\u0270\3\2\2\2J\u027c\3\2\2\2L\u027e\3\2\2\2N\u0292\3\2"+ + "\2\2P\u02ae\3\2\2\2R\u02b0\3\2\2\2T\u02b2\3\2\2\2V\u02b4\3\2\2\2X\u02c3"+ + "\3\2\2\2Z\u02c5\3\2\2\2\\\u02c7\3\2\2\2^\u02ce\3\2\2\2`\u02d5\3\2\2\2"+ + "b\u02e3\3\2\2\2d\u02e7\3\2\2\2f\u02ec\3\2\2\2h\u02f0\3\2\2\2j\u02f2\3"+ + "\2\2\2l\u02f4\3\2\2\2no\5\6\4\2op\7\2\2\3p\3\3\2\2\2qr\5,\27\2rs\7\2\2"+ + "\3s\5\3\2\2\2t\u00d9\5\b\5\2u\u0083\7\36\2\2v\177\7\3\2\2wx\7E\2\2x~\t"+ + "\2\2\2yz\7\"\2\2z~\t\3\2\2{|\7W\2\2|~\5T+\2}w\3\2\2\2}y\3\2\2\2}{\3\2"+ + "\2\2~\u0081\3\2\2\2\177}\3\2\2\2\177\u0080\3\2\2\2\u0080\u0082\3\2\2\2"+ + "\u0081\177\3\2\2\2\u0082\u0084\7\4\2\2\u0083v\3\2\2\2\u0083\u0084\3\2"+ + "\2\2\u0084\u0085\3\2\2\2\u0085\u00d9\5\6\4\2\u0086\u0092\7\27\2\2\u0087"+ + "\u008e\7\3\2\2\u0088\u0089\7E\2\2\u0089\u008d\t\4\2\2\u008a\u008b\7\""+ + "\2\2\u008b\u008d\t\3\2\2\u008c\u0088\3\2\2\2\u008c\u008a\3\2\2\2\u008d"+ + "\u0090\3\2\2\2\u008e\u008c\3\2\2\2\u008e\u008f\3\2\2\2\u008f\u0091\3\2"+ + "\2\2\u0090\u008e\3\2\2\2\u0091\u0093\7\4\2\2\u0092\u0087\3\2\2\2\u0092"+ + "\u0093\3\2\2\2\u0093\u0094\3\2\2\2\u0094\u00d9\5\6\4\2\u0095\u0096\7M"+ + "\2\2\u0096\u0099\7P\2\2\u0097\u009a\5\66\34\2\u0098\u009a\5b\62\2\u0099"+ + "\u0097\3\2\2\2\u0099\u0098\3\2\2\2\u0099\u009a\3\2\2\2\u009a\u00d9\3\2"+ + "\2\2\u009b\u009c\7M\2\2\u009c\u009d\7\23\2\2\u009d\u00a0\t\5\2\2\u009e"+ + "\u00a1\5\66\34\2\u009f\u00a1\5b\62\2\u00a0\u009e\3\2\2\2\u00a0\u009f\3"+ + "\2\2\2\u00a1\u00d9\3\2\2\2\u00a2\u00a5\t\6\2\2\u00a3\u00a6\5\66\34\2\u00a4"+ + "\u00a6\5b\62\2\u00a5\u00a3\3\2\2\2\u00a5\u00a4\3\2\2\2\u00a6\u00d9\3\2"+ + "\2\2\u00a7\u00a8\7M\2\2\u00a8\u00aa\7%\2\2\u00a9\u00ab\5\66\34\2\u00aa"+ + "\u00a9\3\2\2\2\u00aa\u00ab\3\2\2\2\u00ab\u00d9\3\2\2\2\u00ac\u00ad\7M"+ + "\2\2\u00ad\u00d9\7I\2\2\u00ae\u00af\7N\2\2\u00af\u00d9\7\22\2\2\u00b0"+ + "\u00b1\7N\2\2\u00b1\u00b4\7P\2\2\u00b2\u00b3\7\21\2\2\u00b3\u00b5\5\66"+ + "\34\2\u00b4\u00b2\3\2\2\2\u00b4\u00b5\3\2\2\2\u00b5\u00b8\3\2\2\2\u00b6"+ + "\u00b9\5\66\34\2\u00b7\u00b9\5b\62\2\u00b8\u00b6\3\2\2\2\u00b8\u00b7\3"+ + "\2\2\2\u00b8\u00b9\3\2\2\2\u00b9\u00c3\3\2\2\2\u00ba\u00bb\7T\2\2\u00bb"+ + "\u00c0\5j\66\2\u00bc\u00bd\7\5\2\2\u00bd\u00bf\5j\66\2\u00be\u00bc\3\2"+ + "\2\2\u00bf\u00c2\3\2\2\2\u00c0\u00be\3\2\2\2\u00c0\u00c1\3\2\2\2\u00c1"+ + "\u00c4\3\2\2\2\u00c2\u00c0\3\2\2\2\u00c3\u00ba\3\2\2\2\u00c3\u00c4\3\2"+ + "\2\2\u00c4\u00d9\3\2\2\2\u00c5\u00c6\7N\2\2\u00c6\u00c9\7\23\2\2\u00c7"+ + "\u00c8\7\21\2\2\u00c8\u00ca\5j\66\2\u00c9\u00c7\3\2\2\2\u00c9\u00ca\3"+ + "\2\2\2\u00ca\u00ce\3\2\2\2\u00cb\u00cc\7O\2\2\u00cc\u00cf\5\66\34\2\u00cd"+ + "\u00cf\5b\62\2\u00ce\u00cb\3\2\2\2\u00ce\u00cd\3\2\2\2\u00ce\u00cf\3\2"+ + "\2\2\u00cf\u00d1\3\2\2\2\u00d0\u00d2\5\66\34\2\u00d1\u00d0\3\2\2\2\u00d1"+ + "\u00d2\3\2\2\2\u00d2\u00d9\3\2\2\2\u00d3\u00d4\7N\2\2\u00d4\u00d9\7U\2"+ + "\2\u00d5\u00d6\7N\2\2\u00d6\u00d7\7O\2\2\u00d7\u00d9\7U\2\2\u00d8t\3\2"+ + "\2\2\u00d8u\3\2\2\2\u00d8\u0086\3\2\2\2\u00d8\u0095\3\2\2\2\u00d8\u009b"+ + "\3\2\2\2\u00d8\u00a2\3\2\2\2\u00d8\u00a7\3\2\2\2\u00d8\u00ac\3\2\2\2\u00d8"+ + "\u00ae\3\2\2\2\u00d8\u00b0\3\2\2\2\u00d8\u00c5\3\2\2\2\u00d8\u00d3\3\2"+ + "\2\2\u00d8\u00d5\3\2\2\2\u00d9\7\3\2\2\2\u00da\u00db\7Y\2\2\u00db\u00e0"+ + "\5\34\17\2\u00dc\u00dd\7\5\2\2\u00dd\u00df\5\34\17\2\u00de\u00dc\3\2\2"+ + "\2\u00df\u00e2\3\2\2\2\u00e0\u00de\3\2\2\2\u00e0\u00e1\3\2\2\2\u00e1\u00e4"+ + "\3\2\2\2\u00e2\u00e0\3\2\2\2\u00e3\u00da\3\2\2\2\u00e3\u00e4\3\2\2\2\u00e4"+ + "\u00e5\3\2\2\2\u00e5\u00e6\5\n\6\2\u00e6\t\3\2\2\2\u00e7\u00f2\5\16\b"+ + "\2\u00e8\u00e9\7A\2\2\u00e9\u00ea\7\17\2\2\u00ea\u00ef\5\20\t\2\u00eb"+ + "\u00ec\7\5\2\2\u00ec\u00ee\5\20\t\2\u00ed\u00eb\3\2\2\2\u00ee\u00f1\3"+ + "\2\2\2\u00ef\u00ed\3\2\2\2\u00ef\u00f0\3\2\2\2\u00f0\u00f3\3\2\2\2\u00f1"+ + "\u00ef\3\2\2\2\u00f2\u00e8\3\2\2\2\u00f2\u00f3\3\2\2\2\u00f3\u00f5\3\2"+ + "\2\2\u00f4\u00f6\5\f\7\2\u00f5\u00f4\3\2\2\2\u00f5\u00f6\3\2\2\2\u00f6"+ + "\13\3\2\2\2\u00f7\u00f8\7\63\2\2\u00f8\u00fd\t\7\2\2\u00f9\u00fa\7^\2"+ + "\2\u00fa\u00fb\t\7\2\2\u00fb\u00fd\7c\2\2\u00fc\u00f7\3\2\2\2\u00fc\u00f9"+ + "\3\2\2\2\u00fd\r\3\2\2\2\u00fe\u0104\5\22\n\2\u00ff\u0100\7\3\2\2\u0100"+ + "\u0101\5\n\6\2\u0101\u0102\7\4\2\2\u0102\u0104\3\2\2\2\u0103\u00fe\3\2"+ + "\2\2\u0103\u00ff\3\2\2\2\u0104\17\3\2\2\2\u0105\u0107\5,\27\2\u0106\u0108"+ + "\t\b\2\2\u0107\u0106\3\2\2\2\u0107\u0108\3\2\2\2\u0108\u010b\3\2\2\2\u0109"+ + "\u010a\7=\2\2\u010a\u010c\t\t\2\2\u010b\u0109\3\2\2\2\u010b\u010c\3\2"+ + "\2\2\u010c\21\3\2\2\2\u010d\u010f\7L\2\2\u010e\u0110\5\36\20\2\u010f\u010e"+ + "\3\2\2\2\u010f\u0110\3\2\2\2\u0110\u0111\3\2\2\2\u0111\u0116\5 \21\2\u0112"+ + "\u0113\7\5\2\2\u0113\u0115\5 \21\2\u0114\u0112\3\2\2\2\u0115\u0118\3\2"+ + "\2\2\u0116\u0114\3\2\2\2\u0116\u0117\3\2\2\2\u0117\u011a\3\2\2\2\u0118"+ + "\u0116\3\2\2\2\u0119\u011b\5\24\13\2\u011a\u0119\3\2\2\2\u011a\u011b\3"+ + "\2\2\2\u011b\u011e\3\2\2\2\u011c\u011d\7X\2\2\u011d\u011f\5.\30\2\u011e"+ + "\u011c\3\2\2\2\u011e\u011f\3\2\2\2\u011f\u0123\3\2\2\2\u0120\u0121\7\'"+ + "\2\2\u0121\u0122\7\17\2\2\u0122\u0124\5\26\f\2\u0123\u0120\3\2\2\2\u0123"+ + "\u0124\3\2\2\2\u0124\u0127\3\2\2\2\u0125\u0126\7(\2\2\u0126\u0128\5.\30"+ + "\2\u0127\u0125\3\2\2\2\u0127\u0128\3\2\2\2\u0128\23\3\2\2\2\u0129\u012a"+ + "\7#\2\2\u012a\u012f\5\"\22\2\u012b\u012c\7\5\2\2\u012c\u012e\5\"\22\2"+ + "\u012d\u012b\3\2\2\2\u012e\u0131\3\2\2\2\u012f\u012d\3\2\2\2\u012f\u0130"+ + "\3\2\2\2\u0130\25\3\2\2\2\u0131\u012f\3\2\2\2\u0132\u0134\5\36\20\2\u0133"+ + "\u0132\3\2\2\2\u0133\u0134\3\2\2\2\u0134\u0135\3\2\2\2\u0135\u013a\5\30"+ + "\r\2\u0136\u0137\7\5\2\2\u0137\u0139\5\30\r\2\u0138\u0136\3\2\2\2\u0139"+ + "\u013c\3\2\2\2\u013a\u0138\3\2\2\2\u013a\u013b\3\2\2\2\u013b\27\3\2\2"+ + "\2\u013c\u013a\3\2\2\2\u013d\u013e\5\32\16\2\u013e\31\3\2\2\2\u013f\u0148"+ + "\7\3\2\2\u0140\u0145\5,\27\2\u0141\u0142\7\5\2\2\u0142\u0144\5,\27\2\u0143"+ + "\u0141\3\2\2\2\u0144\u0147\3\2\2\2\u0145\u0143\3\2\2\2\u0145\u0146\3\2"+ + "\2\2\u0146\u0149\3\2\2\2\u0147\u0145\3\2\2\2\u0148\u0140\3\2\2\2\u0148"+ + "\u0149\3\2\2\2\u0149\u014a\3\2\2\2\u014a\u014d\7\4\2\2\u014b\u014d\5,"+ + "\27\2\u014c\u013f\3\2\2\2\u014c\u014b\3\2\2\2\u014d\33\3\2\2\2\u014e\u014f"+ + "\5`\61\2\u014f\u0150\7\f\2\2\u0150\u0151\7\3\2\2\u0151\u0152\5\n\6\2\u0152"+ + "\u0153\7\4\2\2\u0153\35\3\2\2\2\u0154\u0155\t\n\2\2\u0155\37\3\2\2\2\u0156"+ + "\u015b\5,\27\2\u0157\u0159\7\f\2\2\u0158\u0157\3\2\2\2\u0158\u0159\3\2"+ + "\2\2\u0159\u015a\3\2\2\2\u015a\u015c\5`\61\2\u015b\u0158\3\2\2\2\u015b"+ + "\u015c\3\2\2\2\u015c!\3\2\2\2\u015d\u0161\5*\26\2\u015e\u0160\5$\23\2"+ + "\u015f\u015e\3\2\2\2\u0160\u0163\3\2\2\2\u0161\u015f\3\2\2\2\u0161\u0162"+ + "\3\2\2\2\u0162#\3\2\2\2\u0163\u0161\3\2\2\2\u0164\u0165\5&\24\2\u0165"+ + "\u0166\7/\2\2\u0166\u0168\5*\26\2\u0167\u0169\5(\25\2\u0168\u0167\3\2"+ + "\2\2\u0168\u0169\3\2\2\2\u0169\u0170\3\2\2\2\u016a\u016b\7:\2\2\u016b"+ + "\u016c\5&\24\2\u016c\u016d\7/\2\2\u016d\u016e\5*\26\2\u016e\u0170\3\2"+ + "\2\2\u016f\u0164\3\2\2\2\u016f\u016a\3\2\2\2\u0170%\3\2\2\2\u0171\u0173"+ + "\7,\2\2\u0172\u0171\3\2\2\2\u0172\u0173\3\2\2\2\u0173\u0181\3\2\2\2\u0174"+ + "\u0176\7\61\2\2\u0175\u0177\7B\2\2\u0176\u0175\3\2\2\2\u0176\u0177\3\2"+ + "\2\2\u0177\u0181\3\2\2\2\u0178\u017a\7F\2\2\u0179\u017b\7B\2\2\u017a\u0179"+ + "\3\2\2\2\u017a\u017b\3\2\2\2\u017b\u0181\3\2\2\2\u017c\u017e\7$\2\2\u017d"+ + "\u017f\7B\2\2\u017e\u017d\3\2\2\2\u017e\u017f\3\2\2\2\u017f\u0181\3\2"+ + "\2\2\u0180\u0172\3\2\2\2\u0180\u0174\3\2\2\2\u0180\u0178\3\2\2\2\u0180"+ + "\u017c\3\2\2\2\u0181\'\3\2\2\2\u0182\u0183\7>\2\2\u0183\u0191\5.\30\2"+ + "\u0184\u0185\7V\2\2\u0185\u0186\7\3\2\2\u0186\u018b\5`\61\2\u0187\u0188"+ + "\7\5\2\2\u0188\u018a\5`\61\2\u0189\u0187\3\2\2\2\u018a\u018d\3\2\2\2\u018b"+ + "\u0189\3\2\2\2\u018b\u018c\3\2\2\2\u018c\u018e\3\2\2\2\u018d\u018b\3\2"+ + "\2\2\u018e\u018f\7\4\2\2\u018f\u0191\3\2\2\2\u0190\u0182\3\2\2\2\u0190"+ + "\u0184\3\2\2\2\u0191)\3\2\2\2\u0192\u0197\5b\62\2\u0193\u0195\7\f\2\2"+ + "\u0194\u0193\3\2\2\2\u0194\u0195\3\2\2\2\u0195\u0196\3\2\2\2\u0196\u0198"+ + "\5^\60\2\u0197\u0194\3\2\2\2\u0197\u0198\3\2\2\2\u0198\u01ac\3\2\2\2\u0199"+ + "\u019a\7\3\2\2\u019a\u019b\5\n\6\2\u019b\u01a0\7\4\2\2\u019c\u019e\7\f"+ + "\2\2\u019d\u019c\3\2\2\2\u019d\u019e\3\2\2\2\u019e\u019f\3\2\2\2\u019f"+ + "\u01a1\5^\60\2\u01a0\u019d\3\2\2\2\u01a0\u01a1\3\2\2\2\u01a1\u01ac\3\2"+ + "\2\2\u01a2\u01a3\7\3\2\2\u01a3\u01a4\5\"\22\2\u01a4\u01a9\7\4\2\2\u01a5"+ + "\u01a7\7\f\2\2\u01a6\u01a5\3\2\2\2\u01a6\u01a7\3\2\2\2\u01a7\u01a8\3\2"+ + "\2\2\u01a8\u01aa\5^\60\2\u01a9\u01a6\3\2\2\2\u01a9\u01aa\3\2\2\2\u01aa"+ + "\u01ac\3\2\2\2\u01ab\u0192\3\2\2\2\u01ab\u0199\3\2\2\2\u01ab\u01a2\3\2"+ + "\2\2\u01ac+\3\2\2\2\u01ad\u01ae\5.\30\2\u01ae-\3\2\2\2\u01af\u01b0\b\30"+ + "\1\2\u01b0\u01b1\7;\2\2\u01b1\u01cf\5.\30\n\u01b2\u01b3\7\35\2\2\u01b3"+ + "\u01b4\7\3\2\2\u01b4\u01b5\5\b\5\2\u01b5\u01b6\7\4\2\2\u01b6\u01cf\3\2"+ + "\2\2\u01b7\u01b8\7H\2\2\u01b8\u01b9\7\3\2\2\u01b9\u01ba\5j\66\2\u01ba"+ + "\u01bb\5\60\31\2\u01bb\u01bc\7\4\2\2\u01bc\u01cf\3\2\2\2\u01bd\u01be\7"+ + "\65\2\2\u01be\u01bf\7\3\2\2\u01bf\u01c0\5^\60\2\u01c0\u01c1\7\5\2\2\u01c1"+ + "\u01c2\5j\66\2\u01c2\u01c3\5\60\31\2\u01c3\u01c4\7\4\2\2\u01c4\u01cf\3"+ + "\2\2\2\u01c5\u01c6\7\65\2\2\u01c6\u01c7\7\3\2\2\u01c7\u01c8\5j\66\2\u01c8"+ + "\u01c9\7\5\2\2\u01c9\u01ca\5j\66\2\u01ca\u01cb\5\60\31\2\u01cb\u01cc\7"+ + "\4\2\2\u01cc\u01cf\3\2\2\2\u01cd\u01cf\5\62\32\2\u01ce\u01af\3\2\2\2\u01ce"+ + "\u01b2\3\2\2\2\u01ce\u01b7\3\2\2\2\u01ce\u01bd\3\2\2\2\u01ce\u01c5\3\2"+ + "\2\2\u01ce\u01cd\3\2\2\2\u01cf\u01d8\3\2\2\2\u01d0\u01d1\f\4\2\2\u01d1"+ + "\u01d2\7\n\2\2\u01d2\u01d7\5.\30\5\u01d3\u01d4\f\3\2\2\u01d4\u01d5\7@"+ + "\2\2\u01d5\u01d7\5.\30\4\u01d6\u01d0\3\2\2\2\u01d6\u01d3\3\2\2\2\u01d7"+ + "\u01da\3\2\2\2\u01d8\u01d6\3\2\2\2\u01d8\u01d9\3\2\2\2\u01d9/\3\2\2\2"+ + "\u01da\u01d8\3\2\2\2\u01db\u01dc\7\5\2\2\u01dc\u01de\5j\66\2\u01dd\u01db"+ + "\3\2\2\2\u01de\u01e1\3\2\2\2\u01df\u01dd\3\2\2\2\u01df\u01e0\3\2\2\2\u01e0"+ + "\61\3\2\2\2\u01e1\u01df\3\2\2\2\u01e2\u01e4\5<\37\2\u01e3\u01e5\5\64\33"+ + "\2\u01e4\u01e3\3\2\2\2\u01e4\u01e5\3\2\2\2\u01e5\63\3\2\2\2\u01e6\u01e8"+ + "\7;\2\2\u01e7\u01e6\3\2\2\2\u01e7\u01e8\3\2\2\2\u01e8\u01e9\3\2\2\2\u01e9"+ + "\u01ea\7\16\2\2\u01ea\u01eb\5<\37\2\u01eb\u01ec\7\n\2\2\u01ec\u01ed\5"+ + "<\37\2\u01ed\u0215\3\2\2\2\u01ee\u01f0\7;\2\2\u01ef\u01ee\3\2\2\2\u01ef"+ + "\u01f0\3\2\2\2\u01f0\u01f1\3\2\2\2\u01f1\u01f2\7+\2\2\u01f2\u01f3\7\3"+ + "\2\2\u01f3\u01f8\5,\27\2\u01f4\u01f5\7\5\2\2\u01f5\u01f7\5,\27\2\u01f6"+ + "\u01f4\3\2\2\2\u01f7\u01fa\3\2\2\2\u01f8\u01f6\3\2\2\2\u01f8\u01f9\3\2"+ + "\2\2\u01f9\u01fb\3\2\2\2\u01fa\u01f8\3\2\2\2\u01fb\u01fc\7\4\2\2\u01fc"+ + "\u0215\3\2\2\2\u01fd\u01ff\7;\2\2\u01fe\u01fd\3\2\2\2\u01fe\u01ff\3\2"+ + "\2\2\u01ff\u0200\3\2\2\2\u0200\u0201\7+\2\2\u0201\u0202\7\3\2\2\u0202"+ + "\u0203\5\b\5\2\u0203\u0204\7\4\2\2\u0204\u0215\3\2\2\2\u0205\u0207\7;"+ + "\2\2\u0206\u0205\3\2\2\2\u0206\u0207\3\2\2\2\u0207\u0208\3\2\2\2\u0208"+ + "\u0209\7\62\2\2\u0209\u0215\58\35\2\u020a\u020c\7;\2\2\u020b\u020a\3\2"+ + "\2\2\u020b\u020c\3\2\2\2\u020c\u020d\3\2\2\2\u020d\u020e\7G\2\2\u020e"+ + "\u0215\5j\66\2\u020f\u0211\7.\2\2\u0210\u0212\7;\2\2\u0211\u0210\3\2\2"+ + "\2\u0211\u0212\3\2\2\2\u0212\u0213\3\2\2\2\u0213\u0215\7<\2\2\u0214\u01e7"+ + "\3\2\2\2\u0214\u01ef\3\2\2\2\u0214\u01fe\3\2\2\2\u0214\u0206\3\2\2\2\u0214"+ + "\u020b\3\2\2\2\u0214\u020f\3\2\2\2\u0215\65\3\2\2\2\u0216\u0217\7\62\2"+ + "\2\u0217\u0218\58\35\2\u0218\67\3\2\2\2\u0219\u021b\5j\66\2\u021a\u021c"+ + "\5:\36\2\u021b\u021a\3\2\2\2\u021b\u021c\3\2\2\2\u021c9\3\2\2\2\u021d"+ + "\u021e\7\33\2\2\u021e\u0224\5j\66\2\u021f\u0220\7\\\2\2\u0220\u0221\5"+ + "j\66\2\u0221\u0222\7c\2\2\u0222\u0224\3\2\2\2\u0223\u021d\3\2\2\2\u0223"+ + "\u021f\3\2\2\2\u0224;\3\2\2\2\u0225\u0226\b\37\1\2\u0226\u022a\5> \2\u0227"+ + "\u0228\t\13\2\2\u0228\u022a\5<\37\6\u0229\u0225\3\2\2\2\u0229\u0227\3"+ + "\2\2\2\u022a\u0237\3\2\2\2\u022b\u022c\f\5\2\2\u022c\u022d\t\f\2\2\u022d"+ + "\u0236\5<\37\6\u022e\u022f\f\4\2\2\u022f\u0230\t\13\2\2\u0230\u0236\5"+ + "<\37\5\u0231\u0232\f\3\2\2\u0232\u0233\5R*\2\u0233\u0234\5<\37\4\u0234"+ + "\u0236\3\2\2\2\u0235\u022b\3\2\2\2\u0235\u022e\3\2\2\2\u0235\u0231\3\2"+ + "\2\2\u0236\u0239\3\2\2\2\u0237\u0235\3\2\2\2\u0237\u0238\3\2\2\2\u0238"+ + "=\3\2\2\2\u0239\u0237\3\2\2\2\u023a\u024e\5@!\2\u023b\u024e\5F$\2\u023c"+ + "\u024e\5P)\2\u023d\u023e\5^\60\2\u023e\u023f\7p\2\2\u023f\u0241\3\2\2"+ + "\2\u0240\u023d\3\2\2\2\u0240\u0241\3\2\2\2\u0241\u0242\3\2\2\2\u0242\u024e"+ + "\7l\2\2\u0243\u024e\5J&\2\u0244\u0245\7\3\2\2\u0245\u0246\5\b\5\2\u0246"+ + "\u0247\7\4\2\2\u0247\u024e\3\2\2\2\u0248\u024e\5^\60\2\u0249\u024a\7\3"+ + "\2\2\u024a\u024b\5,\27\2\u024b\u024c\7\4\2\2\u024c\u024e\3\2\2\2\u024d"+ + "\u023a\3\2\2\2\u024d\u023b\3\2\2\2\u024d\u023c\3\2\2\2\u024d\u0240\3\2"+ + "\2\2\u024d\u0243\3\2\2\2\u024d\u0244\3\2\2\2\u024d\u0248\3\2\2\2\u024d"+ + "\u0249\3\2\2\2\u024e?\3\2\2\2\u024f\u025a\5B\"\2\u0250\u0251\7]\2\2\u0251"+ + "\u0252\5B\"\2\u0252\u0253\7c\2\2\u0253\u025a\3\2\2\2\u0254\u025a\5D#\2"+ + "\u0255\u0256\7]\2\2\u0256\u0257\5D#\2\u0257\u0258\7c\2\2\u0258\u025a\3"+ + "\2\2\2\u0259\u024f\3\2\2\2\u0259\u0250\3\2\2\2\u0259\u0254\3\2\2\2\u0259"+ + "\u0255\3\2\2\2\u025aA\3\2\2\2\u025b\u025c\7\20\2\2\u025c\u025d\7\3\2\2"+ + "\u025d\u025e\5,\27\2\u025e\u025f\7\f\2\2\u025f\u0260\5\\/\2\u0260\u0261"+ + "\7\4\2\2\u0261C\3\2\2\2\u0262\u0263\7\24\2\2\u0263\u0264\7\3\2\2\u0264"+ + "\u0265\5,\27\2\u0265\u0266\7\5\2\2\u0266\u0267\5\\/\2\u0267\u0268\7\4"+ + "\2\2\u0268E\3\2\2\2\u0269\u026f\5H%\2\u026a\u026b\7]\2\2\u026b\u026c\5"+ + "H%\2\u026c\u026d\7c\2\2\u026d\u026f\3\2\2\2\u026e\u0269\3\2\2\2\u026e"+ + "\u026a\3\2\2\2\u026fG\3\2\2\2\u0270\u0271\7\37\2\2\u0271\u0272\7\3\2\2"+ + "\u0272\u0273\5`\61\2\u0273\u0274\7#\2\2\u0274\u0275\5<\37\2\u0275\u0276"+ + "\7\4\2\2\u0276I\3\2\2\2\u0277\u027d\5L\'\2\u0278\u0279\7]\2\2\u0279\u027a"+ + "\5L\'\2\u027a\u027b\7c\2\2\u027b\u027d\3\2\2\2\u027c\u0277\3\2\2\2\u027c"+ + "\u0278\3\2\2\2\u027dK\3\2\2\2\u027e\u027f\5N(\2\u027f\u028b\7\3\2\2\u0280"+ + "\u0282\5\36\20\2\u0281\u0280\3\2\2\2\u0281\u0282\3\2\2\2\u0282\u0283\3"+ + "\2\2\2\u0283\u0288\5,\27\2\u0284\u0285\7\5\2\2\u0285\u0287\5,\27\2\u0286"+ + "\u0284\3\2\2\2\u0287\u028a\3\2\2\2\u0288\u0286\3\2\2\2\u0288\u0289\3\2"+ + "\2\2\u0289\u028c\3\2\2\2\u028a\u0288\3\2\2\2\u028b\u0281\3\2\2\2\u028b"+ + "\u028c\3\2\2\2\u028c\u028d\3\2\2\2\u028d\u028e\7\4\2\2\u028eM\3\2\2\2"+ + "\u028f\u0293\7\61\2\2\u0290\u0293\7F\2\2\u0291\u0293\5`\61\2\u0292\u028f"+ + "\3\2\2\2\u0292\u0290\3\2\2\2\u0292\u0291\3\2\2\2\u0293O\3\2\2\2\u0294"+ + "\u02af\7<\2\2\u0295\u02af\5V,\2\u0296\u02af\5h\65\2\u0297\u02af\5T+\2"+ + "\u0298\u029a\7r\2\2\u0299\u0298\3\2\2\2\u029a\u029b\3\2\2\2\u029b\u0299"+ + "\3\2\2\2\u029b\u029c\3\2\2\2\u029c\u02af\3\2\2\2\u029d\u02af\7q\2\2\u029e"+ + "\u029f\7_\2\2\u029f\u02a0\5j\66\2\u02a0\u02a1\7c\2\2\u02a1\u02af\3\2\2"+ + "\2\u02a2\u02a3\7`\2\2\u02a3\u02a4\5j\66\2\u02a4\u02a5\7c\2\2\u02a5\u02af"+ + "\3\2\2\2\u02a6\u02a7\7a\2\2\u02a7\u02a8\5j\66\2\u02a8\u02a9\7c\2\2\u02a9"+ + "\u02af\3\2\2\2\u02aa\u02ab\7b\2\2\u02ab\u02ac\5j\66\2\u02ac\u02ad\7c\2"+ + "\2\u02ad\u02af\3\2\2\2\u02ae\u0294\3\2\2\2\u02ae\u0295\3\2\2\2\u02ae\u0296"+ + "\3\2\2\2\u02ae\u0297\3\2\2\2\u02ae\u0299\3\2\2\2\u02ae\u029d\3\2\2\2\u02ae"+ + "\u029e\3\2\2\2\u02ae\u02a2\3\2\2\2\u02ae\u02a6\3\2\2\2\u02ae\u02aa\3\2"+ + "\2\2\u02afQ\3\2\2\2\u02b0\u02b1\t\r\2\2\u02b1S\3\2\2\2\u02b2\u02b3\t\16"+ + "\2\2\u02b3U\3\2\2\2\u02b4\u02b6\7-\2\2\u02b5\u02b7\t\13\2\2\u02b6\u02b5"+ + "\3\2\2\2\u02b6\u02b7\3\2\2\2\u02b7\u02ba\3\2\2\2\u02b8\u02bb\5h\65\2\u02b9"+ + "\u02bb\5j\66\2\u02ba\u02b8\3\2\2\2\u02ba\u02b9\3\2\2\2\u02bb\u02bc\3\2"+ + "\2\2\u02bc\u02bf\5Z.\2\u02bd\u02be\7S\2\2\u02be\u02c0\5Z.\2\u02bf\u02bd"+ + "\3\2\2\2\u02bf\u02c0\3\2\2\2\u02c0W\3\2\2\2\u02c1\u02c4\5h\65\2\u02c2"+ + "\u02c4\5j\66\2\u02c3\u02c1\3\2\2\2\u02c3\u02c2\3\2\2\2\u02c4Y\3\2\2\2"+ + "\u02c5\u02c6\t\17\2\2\u02c6[\3\2\2\2\u02c7\u02c8\5`\61\2\u02c8]\3\2\2"+ + "\2\u02c9\u02ca\5`\61\2\u02ca\u02cb\7p\2\2\u02cb\u02cd\3\2\2\2\u02cc\u02c9"+ + "\3\2\2\2\u02cd\u02d0\3\2\2\2\u02ce\u02cc\3\2\2\2\u02ce\u02cf\3\2\2\2\u02cf"+ + "\u02d1\3\2\2\2\u02d0\u02ce\3\2\2\2\u02d1\u02d2\5`\61\2\u02d2_\3\2\2\2"+ + "\u02d3\u02d6\5d\63\2\u02d4\u02d6\5f\64\2\u02d5\u02d3\3\2\2\2\u02d5\u02d4"+ + "\3\2\2\2\u02d6a\3\2\2\2\u02d7\u02d8\5`\61\2\u02d8\u02d9\7\6\2\2\u02d9"+ + "\u02db\3\2\2\2\u02da\u02d7\3\2\2\2\u02da\u02db\3\2\2\2\u02db\u02dc\3\2"+ + "\2\2\u02dc\u02e4\7w\2\2\u02dd\u02de\5`\61\2\u02de\u02df\7\6\2\2\u02df"+ + "\u02e1\3\2\2\2\u02e0\u02dd\3\2\2\2\u02e0\u02e1\3\2\2\2\u02e1\u02e2\3\2"+ + "\2\2\u02e2\u02e4\5`\61\2\u02e3\u02da\3\2\2\2\u02e3\u02e0\3\2\2\2\u02e4"+ + "c\3\2\2\2\u02e5\u02e8\7x\2\2\u02e6\u02e8\7y\2\2\u02e7\u02e5\3\2\2\2\u02e7"+ + "\u02e6\3\2\2\2\u02e8e\3\2\2\2\u02e9\u02ed\7u\2\2\u02ea\u02ed\5l\67\2\u02eb"+ + "\u02ed\7v\2\2\u02ec\u02e9\3\2\2\2\u02ec\u02ea\3\2\2\2\u02ec\u02eb\3\2"+ + "\2\2\u02edg\3\2\2\2\u02ee\u02f1\7t\2\2\u02ef\u02f1\7s\2\2\u02f0\u02ee"+ + "\3\2\2\2\u02f0\u02ef\3\2\2\2\u02f1i\3\2\2\2\u02f2\u02f3\t\20\2\2\u02f3"+ + "k\3\2\2\2\u02f4\u02f5\t\21\2\2\u02f5m\3\2\2\2e}\177\u0083\u008c\u008e"+ + "\u0092\u0099\u00a0\u00a5\u00aa\u00b4\u00b8\u00c0\u00c3\u00c9\u00ce\u00d1"+ + "\u00d8\u00e0\u00e3\u00ef\u00f2\u00f5\u00fc\u0103\u0107\u010b\u010f\u0116"+ + "\u011a\u011e\u0123\u0127\u012f\u0133\u013a\u0145\u0148\u014c\u0158\u015b"+ + "\u0161\u0168\u016f\u0172\u0176\u017a\u017e\u0180\u018b\u0190\u0194\u0197"+ + "\u019d\u01a0\u01a6\u01a9\u01ab\u01ce\u01d6\u01d8\u01df\u01e4\u01e7\u01ef"+ + "\u01f8\u01fe\u0206\u020b\u0211\u0214\u021b\u0223\u0229\u0235\u0237\u0240"+ + "\u024d\u0259\u026e\u027c\u0281\u0288\u028b\u0292\u029b\u02ae\u02b6\u02ba"+ + "\u02bf\u02c3\u02ce\u02d5\u02da\u02e0\u02e3\u02e7\u02ec\u02f0"; public static final ATN _ATN = new ATNDeserializer().deserialize(_serializedATN.toCharArray()); static { diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/parser/SqlBaseVisitor.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/parser/SqlBaseVisitor.java index f4420a66adb07..53c3b4533d25f 100644 --- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/parser/SqlBaseVisitor.java +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/parser/SqlBaseVisitor.java @@ -469,6 +469,13 @@ interface SqlBaseVisitor extends ParseTreeVisitor { * @return the visitor result */ T visitNullLiteral(SqlBaseParser.NullLiteralContext ctx); + /** + * Visit a parse tree produced by the {@code intervalLiteral} + * labeled alternative in {@link SqlBaseParser#constant}. + * @param ctx the parse tree + * @return the visitor result + */ + T visitIntervalLiteral(SqlBaseParser.IntervalLiteralContext ctx); /** * Visit a parse tree produced by the {@code numericLiteral} * labeled alternative in {@link SqlBaseParser#constant}. @@ -537,6 +544,24 @@ interface SqlBaseVisitor extends ParseTreeVisitor { * @return the visitor result */ T visitBooleanValue(SqlBaseParser.BooleanValueContext ctx); + /** + * Visit a parse tree produced by {@link SqlBaseParser#interval}. + * @param ctx the parse tree + * @return the visitor result + */ + T visitInterval(SqlBaseParser.IntervalContext ctx); + /** + * Visit a parse tree produced by {@link SqlBaseParser#intervalValue}. + * @param ctx the parse tree + * @return the visitor result + */ + T visitIntervalValue(SqlBaseParser.IntervalValueContext ctx); + /** + * Visit a parse tree produced by {@link SqlBaseParser#intervalField}. + * @param ctx the parse tree + * @return the visitor result + */ + T visitIntervalField(SqlBaseParser.IntervalFieldContext ctx); /** * Visit a parse tree produced by the {@code primitiveDataType} * labeled alternative in {@link SqlBaseParser#dataType}. diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/plan/logical/command/sys/SysColumns.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/plan/logical/command/sys/SysColumns.java index 40475f4fe57e6..48682b2e134f6 100644 --- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/plan/logical/command/sys/SysColumns.java +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/plan/logical/command/sys/SysColumns.java @@ -128,7 +128,7 @@ static void fillInRows(String clusterName, String indexName, Map output() { public final void execute(SqlSession session, ActionListener listener) { List> rows = Stream.of(DataType.values()) // sort by SQL int type (that's what the JDBC/ODBC specs want) followed by name - .sorted(Comparator.comparing((DataType t) -> t.jdbcType.getVendorTypeNumber()).thenComparing(DataType::sqlName)) + .sorted(Comparator.comparing((DataType t) -> t.sqlType.getVendorTypeNumber()).thenComparing(DataType::sqlName)) .map(t -> asList(t.esType.toUpperCase(Locale.ROOT), - t.jdbcType.getVendorTypeNumber(), + t.sqlType.getVendorTypeNumber(), //https://docs.microsoft.com/en-us/sql/odbc/reference/appendixes/column-size?view=sql-server-2017 t.defaultPrecision, "'", diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/plugin/TransportSqlQueryAction.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/plugin/TransportSqlQueryAction.java index 9e5954af737ee..9a0f101941404 100644 --- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/plugin/TransportSqlQueryAction.java +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/plugin/TransportSqlQueryAction.java @@ -72,7 +72,7 @@ static SqlQueryResponse createResponse(SqlQueryRequest request, SchemaRowSet row List columns = new ArrayList<>(rowSet.columnCount()); for (Schema.Entry entry : rowSet.schema()) { if (Mode.isDriver(request.mode())) { - columns.add(new ColumnInfo("", entry.name(), entry.type().esType, entry.type().jdbcType, + columns.add(new ColumnInfo("", entry.name(), entry.type().esType, entry.type().sqlType.getVendorTypeNumber(), entry.type().displaySize)); } else { columns.add(new ColumnInfo("", entry.name(), entry.type().esType)); diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/querydsl/agg/GroupByScriptKey.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/querydsl/agg/GroupByScriptKey.java index 99bd4e27671bf..910f2a2a76347 100644 --- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/querydsl/agg/GroupByScriptKey.java +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/querydsl/agg/GroupByScriptKey.java @@ -39,9 +39,9 @@ public TermsValuesSourceBuilder asValueSource() { .order(direction().asOrder()) .missingBucket(true); - if (script.outputType().isInteger) { + if (script.outputType().isInteger()) { builder.valueType(ValueType.LONG); - } else if (script.outputType().isRational) { + } else if (script.outputType().isRational()) { builder.valueType(ValueType.DOUBLE); } else if (script.outputType().isString()) { builder.valueType(ValueType.STRING); diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/type/DataType.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/type/DataType.java new file mode 100644 index 0000000000000..20071f84e9812 --- /dev/null +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/type/DataType.java @@ -0,0 +1,249 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +package org.elasticsearch.xpack.sql.type; + +import java.sql.JDBCType; +import java.sql.SQLType; +import java.sql.Types; +import java.util.HashMap; +import java.util.Locale; +import java.util.Map; + +/** + * Elasticsearch SQL data types. + * This class also implements JDBC {@link SQLType} for properly receiving and setting values. + * Where possible, please use the build-in, JDBC {@link Types} and {@link JDBCType} to avoid coupling + * to the API. + */ +public enum DataType { + + // @formatter:off + // jdbc type, size, defPrecision, dispSize, int, rat, docvals + NULL( JDBCType.NULL, 0, 0, 0), + UNSUPPORTED( JDBCType.OTHER, 0, 0, 0), + BOOLEAN( JDBCType.BOOLEAN, 1, 1, 1), + BYTE( JDBCType.TINYINT, Byte.BYTES, 3, 5, true, false, true), + SHORT( JDBCType.SMALLINT, Short.BYTES, 5, 6, true, false, true), + INTEGER( JDBCType.INTEGER, Integer.BYTES, 10, 11, true, false, true), + LONG( JDBCType.BIGINT, Long.BYTES, 19, 20, true, false, true), + // 53 bits defaultPrecision ~ 15(15.95) decimal digits (53log10(2)), + DOUBLE( JDBCType.DOUBLE, Double.BYTES, 15, 25, false, true, true), + // 24 bits defaultPrecision - 24*log10(2) =~ 7 (7.22) + FLOAT( JDBCType.REAL, Float.BYTES, 7, 15, false, true, true), + HALF_FLOAT( JDBCType.FLOAT, Double.BYTES, 16, 25, false, true, true), + // precision is based on long + SCALED_FLOAT( JDBCType.FLOAT, Double.BYTES, 19, 25, false, true, true), + KEYWORD( JDBCType.VARCHAR, Integer.MAX_VALUE, 256, 0), + TEXT( JDBCType.VARCHAR, Integer.MAX_VALUE, Integer.MAX_VALUE, 0, false, false, false), + OBJECT( JDBCType.STRUCT, -1, 0, 0, false, false, false), + NESTED( JDBCType.STRUCT, -1, 0, 0, false, false, false), + BINARY( JDBCType.VARBINARY, -1, Integer.MAX_VALUE, 0), + // since ODBC and JDBC interpret precision for Date as display size, + // the precision is 23 (number of chars in ISO8601 with millis) + Z (the UTC timezone) + // see https://github.com/elastic/elasticsearch/issues/30386#issuecomment-386807288 + DATE( JDBCType.TIMESTAMP, Long.BYTES, 24, 24), + // + // specialized types + // + // IP can be v4 or v6. The latter has 2^128 addresses or 340,282,366,920,938,463,463,374,607,431,768,211,456 + // aka 39 chars + IP( JDBCType.VARCHAR, 39, 39, 0,false, false, true), + // + // INTERVALS + // the list is long as there are a lot of variations and that's what clients (ODBC) expect + INTERVAL_YEAR( ExtTypes.INTERVAL_YEAR, Integer.BYTES, 7, 7), + INTERVAL_MONTH( ExtTypes.INTERVAL_MONTH, Integer.BYTES, 7, 7), + INTERVAL_YEAR_TO_MONTH( ExtTypes.INTERVAL_YEAR_TO_MONTH, Integer.BYTES, 7, 7), + INTERVAL_DAY( ExtTypes.INTERVAL_DAY, Long.BYTES, 23, 23), + INTERVAL_HOUR( ExtTypes.INTERVAL_HOUR, Long.BYTES, 23, 23), + INTERVAL_MINUTE( ExtTypes.INTERVAL_MINUTE, Long.BYTES, 23, 23), + INTERVAL_SECOND( ExtTypes.INTERVAL_SECOND, Long.BYTES, 23, 23), + INTERVAL_DAY_TO_HOUR( ExtTypes.INTERVAL_DAY_TO_HOUR, Long.BYTES, 23, 23), + INTERVAL_DAY_TO_MINUTE( ExtTypes.INTERVAL_DAY_TO_MINUTE, Long.BYTES, 23, 23), + INTERVAL_DAY_TO_SECOND( ExtTypes.INTERVAL_DAY_TO_SECOND, Long.BYTES, 23, 23), + INTERVAL_HOUR_TO_MINUTE( ExtTypes.INTERVAL_HOUR_TO_MINUTE, Long.BYTES, 23, 23), + INTERVAL_HOUR_TO_SECOND( ExtTypes.INTERVAL_HOUR_TO_SECOND, Long.BYTES, 23, 23), + INTERVAL_MINUTE_TO_SECOND(ExtTypes.INTERVAL_MINUTE_TO_SECOND,Long.BYTES, 23, 23); + // @formatter:on + + private static final Map odbcToEs; + + static { + odbcToEs = new HashMap<>(36); + + // Numeric + odbcToEs.put("SQL_BIT", BOOLEAN); + odbcToEs.put("SQL_TINYINT", BYTE); + odbcToEs.put("SQL_SMALLINT", SHORT); + odbcToEs.put("SQL_INTEGER", INTEGER); + odbcToEs.put("SQL_BIGINT", LONG); + odbcToEs.put("SQL_FLOAT", FLOAT); + odbcToEs.put("SQL_REAL", FLOAT); + odbcToEs.put("SQL_DOUBLE", DOUBLE); + odbcToEs.put("SQL_DECIMAL", DOUBLE); + odbcToEs.put("SQL_NUMERIC", DOUBLE); + + // String + odbcToEs.put("SQL_GUID", KEYWORD); + odbcToEs.put("SQL_CHAR", KEYWORD); + odbcToEs.put("SQL_WCHAR", KEYWORD); + odbcToEs.put("SQL_VARCHAR", TEXT); + odbcToEs.put("SQL_WVARCHAR", TEXT); + odbcToEs.put("SQL_LONGVARCHAR", TEXT); + odbcToEs.put("SQL_WLONGVARCHAR", TEXT); + + // Binary + odbcToEs.put("SQL_BINARY", BINARY); + odbcToEs.put("SQL_VARBINARY", BINARY); + odbcToEs.put("SQL_LONGVARBINARY", BINARY); + + // Date + odbcToEs.put("SQL_DATE", DATE); + odbcToEs.put("SQL_TIME", DATE); + odbcToEs.put("SQL_TIMESTAMP", DATE); + + // Intervals + odbcToEs.put("SQL_INTERVAL_HOUR_TO_MINUTE", INTERVAL_HOUR_TO_MINUTE); + odbcToEs.put("SQL_INTERVAL_HOUR_TO_SECOND", INTERVAL_HOUR_TO_SECOND); + odbcToEs.put("SQL_INTERVAL_MINUTE_TO_SECOND", INTERVAL_MINUTE_TO_SECOND); + odbcToEs.put("SQL_INTERVAL_MONTH", INTERVAL_MONTH); + odbcToEs.put("SQL_INTERVAL_YEAR", INTERVAL_YEAR); + odbcToEs.put("SQL_INTERVAL_YEAR_TO_MONTH", INTERVAL_YEAR_TO_MONTH); + odbcToEs.put("SQL_INTERVAL_DAY", INTERVAL_DAY); + odbcToEs.put("SQL_INTERVAL_HOUR", INTERVAL_HOUR); + odbcToEs.put("SQL_INTERVAL_MINUTE", INTERVAL_MINUTE); + odbcToEs.put("SQL_INTERVAL_SECOND", INTERVAL_SECOND); + odbcToEs.put("SQL_INTERVAL_DAY_TO_HOUR", INTERVAL_DAY_TO_HOUR); + odbcToEs.put("SQL_INTERVAL_DAY_TO_MINUTE", INTERVAL_DAY_TO_MINUTE); + odbcToEs.put("SQL_INTERVAL_DAY_TO_SECOND", INTERVAL_DAY_TO_SECOND); + } + + /** + * Elasticsearch type name + */ + public final String esType; + + /** + * Compatible JDBC type + */ + public final SQLType sqlType; + + /** + * Size of the type in bytes + *

+ * -1 if the size can vary + */ + public final int size; + + /** + * Precision + *

+ * Specified column size. For numeric data, this is the maximum precision. For character + * data, this is the length in characters. For datetime datatypes, this is the length in characters of the + * String representation (assuming the maximum allowed defaultPrecision of the fractional milliseconds component). + */ + public final int defaultPrecision; + + + /** + * Display Size + *

+ * Normal maximum width in characters. + */ + public final int displaySize; + + /** + * True if the type represents an integer number + */ + private final boolean isInteger; + + /** + * True if the type represents a rational number + */ + private final boolean isRational; + + /** + * True if the type supports doc values by default + */ + public final boolean defaultDocValues; + + DataType(SQLType sqlType, int size, int defaultPrecision, int displaySize, boolean isInteger, + boolean isRational, boolean defaultDocValues) { + this.esType = name().toLowerCase(Locale.ROOT); + this.sqlType = sqlType; + this.size = size; + this.defaultPrecision = defaultPrecision; + this.displaySize = displaySize; + this.isInteger = isInteger; + this.isRational = isRational; + this.defaultDocValues = defaultDocValues; + } + + DataType(SQLType sqlType, int size, int defaultPrecision, int displaySize) { + this(sqlType, size, defaultPrecision, displaySize, false, false, true); + } + + public String sqlName() { + return sqlType.getName(); + } + + public boolean isInteger() { + return isInteger; + } + + public boolean isRational() { + return isRational; + } + + public boolean isNumeric() { + return isInteger || isRational; + } + + + /** + * Returns true if value is signed, false otherwise (including if the type is not numeric) + */ + public boolean isSigned() { + // For now all numeric values that es supports are signed + return isNumeric(); + } + + public boolean isString() { + return this == KEYWORD || this == TEXT; + } + + public boolean isPrimitive() { + return this != OBJECT && this != NESTED; + } + + public boolean isCompatibleWith(DataType other) { + if (this == other) { + return true; + } else { + return + (this == NULL || other == NULL) || + (isString() && other.isString()) || + (isNumeric() && other.isNumeric()); + } + } + + public static DataType fromOdbcType(String odbcType) { + return odbcToEs.get(odbcType); + } + + /** + * Creates returns DataType enum corresponding to the specified es type + *

+ * For any dataType DataType.fromEsType(dataType.esType) == dataType + */ + public static DataType fromTypeName(String esType) { + try { + return DataType.valueOf(esType.toUpperCase(Locale.ROOT)); + } catch (IllegalArgumentException ex) { + return DataType.UNSUPPORTED; + } + } +} \ No newline at end of file diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/type/DataTypeConversion.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/type/DataTypeConversion.java index 26436c614f565..0c722fdb5ac7e 100644 --- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/type/DataTypeConversion.java +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/type/DataTypeConversion.java @@ -53,16 +53,16 @@ public static DataType commonType(DataType left, DataType right) { } if (left.isNumeric() && right.isNumeric()) { // if one is int - if (left.isInteger) { + if (left.isInteger()) { // promote the highest int - if (right.isInteger) { + if (right.isInteger()) { return left.size > right.size ? left : right; } // promote the rational return right; } // try the other side - if (right.isInteger) { + if (right.isInteger()) { return left; } // promote the highest rational @@ -160,10 +160,10 @@ private static Conversion conversionToIp(DataType from) { } private static Conversion conversionToLong(DataType from) { - if (from.isRational) { + if (from.isRational()) { return Conversion.RATIONAL_TO_LONG; } - if (from.isInteger) { + if (from.isInteger()) { return Conversion.INTEGER_TO_LONG; } if (from == BOOLEAN) { @@ -179,10 +179,10 @@ private static Conversion conversionToLong(DataType from) { } private static Conversion conversionToInt(DataType from) { - if (from.isRational) { + if (from.isRational()) { return Conversion.RATIONAL_TO_INT; } - if (from.isInteger) { + if (from.isInteger()) { return Conversion.INTEGER_TO_INT; } if (from == BOOLEAN) { @@ -198,10 +198,10 @@ private static Conversion conversionToInt(DataType from) { } private static Conversion conversionToShort(DataType from) { - if (from.isRational) { + if (from.isRational()) { return Conversion.RATIONAL_TO_SHORT; } - if (from.isInteger) { + if (from.isInteger()) { return Conversion.INTEGER_TO_SHORT; } if (from == BOOLEAN) { @@ -217,10 +217,10 @@ private static Conversion conversionToShort(DataType from) { } private static Conversion conversionToByte(DataType from) { - if (from.isRational) { + if (from.isRational()) { return Conversion.RATIONAL_TO_BYTE; } - if (from.isInteger) { + if (from.isInteger()) { return Conversion.INTEGER_TO_BYTE; } if (from == BOOLEAN) { @@ -236,10 +236,10 @@ private static Conversion conversionToByte(DataType from) { } private static Conversion conversionToFloat(DataType from) { - if (from.isRational) { + if (from.isRational()) { return Conversion.RATIONAL_TO_FLOAT; } - if (from.isInteger) { + if (from.isInteger()) { return Conversion.INTEGER_TO_FLOAT; } if (from == BOOLEAN) { @@ -255,10 +255,10 @@ private static Conversion conversionToFloat(DataType from) { } private static Conversion conversionToDouble(DataType from) { - if (from.isRational) { + if (from.isRational()) { return Conversion.RATIONAL_TO_DOUBLE; } - if (from.isInteger) { + if (from.isInteger()) { return Conversion.INTEGER_TO_DOUBLE; } if (from == BOOLEAN) { @@ -274,10 +274,10 @@ private static Conversion conversionToDouble(DataType from) { } private static Conversion conversionToDate(DataType from) { - if (from.isRational) { + if (from.isRational()) { return Conversion.RATIONAL_TO_DATE; } - if (from.isInteger) { + if (from.isInteger()) { return Conversion.INTEGER_TO_DATE; } if (from == BOOLEAN) { @@ -482,6 +482,6 @@ public static DataType asInteger(DataType dataType) { return dataType; } - return dataType.isInteger ? dataType : LONG; + return dataType.isInteger() ? dataType : LONG; } } diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/type/DataTypes.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/type/DataTypes.java index 92bc6f33a5de5..beb8ece94bf5d 100644 --- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/type/DataTypes.java +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/type/DataTypes.java @@ -6,6 +6,7 @@ package org.elasticsearch.xpack.sql.type; import org.elasticsearch.xpack.sql.SqlIllegalArgumentException; +import org.elasticsearch.xpack.sql.expression.literal.Interval; import org.joda.time.DateTime; public final class DataTypes { @@ -51,6 +52,9 @@ public static DataType fromJava(Object value) { if (value instanceof String || value instanceof Character) { return DataType.KEYWORD; } + if (value instanceof Interval) { + return ((Interval) value).dataType(); + } throw new SqlIllegalArgumentException("No idea what's the DataType for {}", value.getClass()); } @@ -68,7 +72,7 @@ public static Integer metaSqlDataType(DataType t) { return Integer.valueOf(9); } // this is safe since the vendor SQL types are short despite the return value - return t.jdbcType.getVendorTypeNumber(); + return t.sqlType.getVendorTypeNumber(); } // https://github.com/elastic/elasticsearch/issues/30386 @@ -88,11 +92,11 @@ public static Short metaSqlMinimumScale(DataType t) { if (t == DataType.DATE) { return Short.valueOf((short) 3); } - if (t.isInteger) { + if (t.isInteger()) { return Short.valueOf((short) 0); } // minimum scale? - if (t.isRational) { + if (t.isRational()) { return Short.valueOf((short) 0); } return null; @@ -103,10 +107,10 @@ public static Short metaSqlMaximumScale(DataType t) { if (t == DataType.DATE) { return Short.valueOf((short) 3); } - if (t.isInteger) { + if (t.isInteger()) { return Short.valueOf((short) 0); } - if (t.isRational) { + if (t.isRational()) { return Short.valueOf((short) t.defaultPrecision); } return null; @@ -118,6 +122,6 @@ public static Integer metaSqlRadix(DataType t) { // 10 means they represent the number of decimal digits allowed for the column. // 2 means they represent the number of bits allowed for the column. // null means radix is not applicable for the given type. - return t.isInteger ? Integer.valueOf(10) : (t.isRational ? Integer.valueOf(2) : null); + return t.isInteger() ? Integer.valueOf(10) : (t.isRational() ? Integer.valueOf(2) : null); } } diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/type/ExtTypes.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/type/ExtTypes.java new file mode 100644 index 0000000000000..1ad9dd92abfec --- /dev/null +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/type/ExtTypes.java @@ -0,0 +1,52 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +package org.elasticsearch.xpack.sql.type; + +import java.sql.JDBCType; +import java.sql.SQLType; +import java.sql.Types; + +/** + * Provides ODBC-based codes for the missing SQL data types from {@link Types}/{@link JDBCType}. + */ +enum ExtTypes implements SQLType { + + INTERVAL_YEAR(101), + INTERVAL_MONTH(102), + INTERVAL_DAY(103), + INTERVAL_HOUR(104), + INTERVAL_MINUTE(105), + INTERVAL_SECOND(106), + INTERVAL_YEAR_TO_MONTH(107), + INTERVAL_DAY_TO_HOUR(108), + INTERVAL_DAY_TO_MINUTE(109), + INTERVAL_DAY_TO_SECOND(110), + INTERVAL_HOUR_TO_MINUTE(111), + INTERVAL_HOUR_TO_SECOND(112), + INTERVAL_MINUTE_TO_SECOND(113); + + private final Integer type; + + ExtTypes(Integer type) { + this.type = type; + } + + @Override + public String getName() { + return name(); + } + + @Override + public String getVendor() { + return "org.elasticsearch"; + } + + @Override + public Integer getVendorTypeNumber() { + return type; + } +} diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/type/Types.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/type/Types.java index 63fc16c6eeaa3..f367f39530dae 100644 --- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/type/Types.java +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/type/Types.java @@ -42,7 +42,7 @@ private static Map startWalking(Map mapping) { private static DataType getType(Map content) { if (content.containsKey("type")) { try { - return DataType.fromEsType(content.get("type").toString()); + return DataType.fromTypeName(content.get("type").toString()); } catch (IllegalArgumentException ex) { return DataType.UNSUPPORTED; } diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/util/StringUtils.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/util/StringUtils.java index 0f00822e3f445..10066e7764966 100644 --- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/util/StringUtils.java +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/util/StringUtils.java @@ -16,6 +16,7 @@ import org.elasticsearch.xpack.sql.SqlIllegalArgumentException; import java.io.IOException; +import java.math.BigInteger; import java.util.ArrayList; import java.util.List; import java.util.Locale; @@ -261,4 +262,39 @@ public static List findSimilar(String match, Iterable potentialM .map(a -> a.v2()) .collect(toList()); } + + public static double parseDouble(String string) throws SqlIllegalArgumentException { + double value; + try { + value = Double.parseDouble(string); + } catch (NumberFormatException nfe) { + throw new SqlIllegalArgumentException("Cannot parse number [{}]", string); + } + + if (Double.isInfinite(value)) { + throw new SqlIllegalArgumentException("Number [{}] is too large", string); + } + if (Double.isNaN(value)) { + throw new SqlIllegalArgumentException("[{}] cannot be parsed as a number (NaN)", string); + } + return value; + } + + public static long parseLong(String string) throws SqlIllegalArgumentException { + try { + return Long.parseLong(string); + } catch (NumberFormatException nfe) { + try { + BigInteger bi = new BigInteger(string); + try { + bi.longValueExact(); + } catch (ArithmeticException ae) { + throw new SqlIllegalArgumentException("Number [{}] is too large", string); + } + } catch (NumberFormatException ex) { + // parsing fails, go through + } + throw new SqlIllegalArgumentException("Cannot parse number [{}]", string); + } + } } \ No newline at end of file diff --git a/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/action/CliFormatterTests.java b/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/action/CliFormatterTests.java index 400a90712d6cf..29f371e007a98 100644 --- a/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/action/CliFormatterTests.java +++ b/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/action/CliFormatterTests.java @@ -6,11 +6,9 @@ package org.elasticsearch.xpack.sql.action; import org.elasticsearch.test.ESTestCase; -import org.elasticsearch.xpack.sql.action.CliFormatter; -import org.elasticsearch.xpack.sql.action.SqlQueryResponse; import org.elasticsearch.xpack.sql.proto.ColumnInfo; -import java.sql.JDBCType; +import java.sql.Types; import java.util.Arrays; import static org.hamcrest.Matchers.arrayWithSize; @@ -18,11 +16,11 @@ public class CliFormatterTests extends ESTestCase { private final SqlQueryResponse firstResponse = new SqlQueryResponse("", Arrays.asList( - new ColumnInfo("", "foo", "string", JDBCType.VARCHAR, 0), - new ColumnInfo("", "bar", "long", JDBCType.BIGINT, 15), - new ColumnInfo("", "15charwidename!", "double", JDBCType.DOUBLE, 25), - new ColumnInfo("", "superduperwidename!!!", "double", JDBCType.DOUBLE, 25), - new ColumnInfo("", "baz", "keyword", JDBCType.VARCHAR, 0)), + new ColumnInfo("", "foo", "string", Types.VARCHAR, 0), + new ColumnInfo("", "bar", "long", Types.BIGINT, 15), + new ColumnInfo("", "15charwidename!", "double", Types.DOUBLE, 25), + new ColumnInfo("", "superduperwidename!!!", "double", Types.DOUBLE, 25), + new ColumnInfo("", "baz", "keyword", Types.VARCHAR, 0)), Arrays.asList( Arrays.asList("15charwidedata!", 1, 6.888, 12, "rabbit"), Arrays.asList("dog", 1.7976931348623157E308, 123124.888, 9912, "goat"))); diff --git a/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/action/SqlActionIT.java b/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/action/SqlActionIT.java index cbdf0e1ac9811..09e106b17bf6c 100644 --- a/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/action/SqlActionIT.java +++ b/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/action/SqlActionIT.java @@ -10,7 +10,7 @@ import org.elasticsearch.xpack.sql.proto.ColumnInfo; import org.elasticsearch.xpack.sql.proto.Mode; -import java.sql.JDBCType; +import java.sql.Types; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked; import static org.hamcrest.Matchers.equalTo; @@ -35,8 +35,8 @@ public void testSqlAction() throws Exception { assertThat(response.columns(), hasSize(2)); int dataIndex = dataBeforeCount ? 0 : 1; int countIndex = dataBeforeCount ? 1 : 0; - assertEquals(new ColumnInfo("", "data", "text", JDBCType.VARCHAR, 0), response.columns().get(dataIndex)); - assertEquals(new ColumnInfo("", "count", "long", JDBCType.BIGINT, 20), response.columns().get(countIndex)); + assertEquals(new ColumnInfo("", "data", "text", Types.VARCHAR, 0), response.columns().get(dataIndex)); + assertEquals(new ColumnInfo("", "count", "long", Types.BIGINT, 20), response.columns().get(countIndex)); assertThat(response.rows(), hasSize(2)); assertEquals("bar", response.rows().get(0).get(dataIndex)); diff --git a/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/execution/search/CursorTests.java b/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/execution/search/CursorTests.java index 7eb4e5402cd36..2c1ef04c863ba 100644 --- a/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/execution/search/CursorTests.java +++ b/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/execution/search/CursorTests.java @@ -13,15 +13,14 @@ import org.elasticsearch.test.ESTestCase; import org.elasticsearch.xpack.sql.SqlException; import org.elasticsearch.xpack.sql.action.CliFormatter; +import org.elasticsearch.xpack.sql.action.SqlQueryResponse; import org.elasticsearch.xpack.sql.plugin.CliFormatterCursor; import org.elasticsearch.xpack.sql.proto.ColumnInfo; -import org.elasticsearch.xpack.sql.action.SqlQueryResponse; import org.elasticsearch.xpack.sql.session.Configuration; import org.elasticsearch.xpack.sql.session.Cursor; import org.elasticsearch.xpack.sql.session.Cursors; import org.mockito.ArgumentCaptor; -import java.sql.JDBCType; import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -67,7 +66,7 @@ private static SqlQueryResponse createRandomSqlResponse() { columns = new ArrayList<>(columnCount); for (int i = 0; i < columnCount; i++) { columns.add(new ColumnInfo(randomAlphaOfLength(10), randomAlphaOfLength(10), randomAlphaOfLength(10), - randomFrom(JDBCType.values()), randomInt(25))); + randomInt(), randomInt(25))); } } return new SqlQueryResponse("", columns, Collections.emptyList()); diff --git a/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/expression/ParameterTests.java b/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/expression/ParameterTests.java index 33185df465e31..52f14550b1449 100644 --- a/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/expression/ParameterTests.java +++ b/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/expression/ParameterTests.java @@ -28,7 +28,7 @@ public class ParameterTests extends ESTestCase { public void testSingleParameter() { Expression expression = new SqlParser().createExpression("a = \n?", Collections.singletonList( - new SqlTypedParamValue(DataType.KEYWORD, "foo") + new SqlTypedParamValue(DataType.KEYWORD.esType, "foo") )); logger.info(expression); assertThat(expression, instanceOf(Equals.class)); @@ -42,10 +42,10 @@ public void testSingleParameter() { public void testMultipleParameters() { Expression expression = new SqlParser().createExpression("(? + ? * ?) - ?", Arrays.asList( - new SqlTypedParamValue(DataType.LONG, 1L), - new SqlTypedParamValue(DataType.LONG, 2L), - new SqlTypedParamValue(DataType.LONG, 3L), - new SqlTypedParamValue(DataType.LONG, 4L) + new SqlTypedParamValue(DataType.LONG.esType, 1L), + new SqlTypedParamValue(DataType.LONG.esType, 2L), + new SqlTypedParamValue(DataType.LONG.esType, 3L), + new SqlTypedParamValue(DataType.LONG.esType, 4L) )); assertThat(expression, instanceOf(Sub.class)); Sub sub = (Sub) expression; @@ -62,9 +62,9 @@ public void testMultipleParameters() { public void testNotEnoughParameters() { ParsingException ex = expectThrows(ParsingException.class, () -> new SqlParser().createExpression("(? + ? * ?) - ?", Arrays.asList( - new SqlTypedParamValue(DataType.LONG, 1L), - new SqlTypedParamValue(DataType.LONG, 2L), - new SqlTypedParamValue(DataType.LONG, 3L) + new SqlTypedParamValue(DataType.LONG.esType, 1L), + new SqlTypedParamValue(DataType.LONG.esType, 2L), + new SqlTypedParamValue(DataType.LONG.esType, 3L) ))); assertThat(ex.getMessage(), containsString("Not enough actual parameters")); } diff --git a/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/expression/literal/IntervalUtilsTests.java b/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/expression/literal/IntervalUtilsTests.java new file mode 100644 index 0000000000000..96ebf401c4cb6 --- /dev/null +++ b/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/expression/literal/IntervalUtilsTests.java @@ -0,0 +1,211 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +package org.elasticsearch.xpack.sql.expression.literal; + +import org.elasticsearch.test.ESTestCase; +import org.elasticsearch.xpack.sql.parser.ParsingException; +import org.elasticsearch.xpack.sql.type.DataType; + +import java.time.Duration; +import java.time.Period; +import java.time.temporal.TemporalAmount; +import java.util.Locale; + +import static java.lang.String.format; +import static org.elasticsearch.xpack.sql.expression.literal.IntervalUtils.parseInterval; +import static org.elasticsearch.xpack.sql.tree.Location.EMPTY; + +public class IntervalUtilsTests extends ESTestCase { + + public void testYearInterval() throws Exception { + String randomSign = randomSign(); + int random = randomNonNegativeInt(); + TemporalAmount amount = parseInterval(EMPTY, randomSign + random, DataType.INTERVAL_YEAR); + assertEquals(maybeNegate(randomSign, Period.ofYears(random)), amount); + } + + public void testMonthInterval() throws Exception { + String randomSign = randomSign(); + int random = randomNonNegativeInt(); + TemporalAmount amount = parseInterval(EMPTY, randomSign + random, DataType.INTERVAL_MONTH); + assertEquals(maybeNegate(randomSign, Period.ofMonths(random)), amount); + } + + public void testDayInterval() throws Exception { + String randomSign = randomSign(); + int random = randomNonNegativeInt(); + TemporalAmount amount = parseInterval(EMPTY, randomSign + random, DataType.INTERVAL_DAY); + assertEquals(maybeNegate(randomSign, Duration.ofDays(random)), amount); + } + + public void testHourInterval() throws Exception { + String randomSign = randomSign(); + int random = randomNonNegativeInt(); + TemporalAmount amount = parseInterval(EMPTY, randomSign + random, DataType.INTERVAL_HOUR); + assertEquals(maybeNegate(randomSign, Duration.ofHours(random)), amount); + } + + public void testMinuteInterval() throws Exception { + String randomSign = randomSign(); + int random = randomNonNegativeInt(); + TemporalAmount amount = parseInterval(EMPTY, randomSign + random, DataType.INTERVAL_MINUTE); + assertEquals(maybeNegate(randomSign, Duration.ofMinutes(random)), amount); + } + + public void testSecondInterval() throws Exception { + String randomSign = randomSign(); + int randomSeconds = randomNonNegativeInt(); + int randomMillis = randomInt(999999999); + String value = format(Locale.ROOT, "%s%d.%d", randomSign, randomSeconds, randomMillis); + TemporalAmount amount = parseInterval(EMPTY, value, DataType.INTERVAL_SECOND); + assertEquals(maybeNegate(randomSign, Duration.ofSeconds(randomSeconds).plusMillis(randomMillis)), amount); + } + + public void testYearToMonth() throws Exception { + String randomSign = randomSign(); + int randomYear = randomNonNegativeInt(); + int randomMonth = randomInt(11); + String value = format(Locale.ROOT, "%s%d-%d", randomSign, randomYear, randomMonth); + TemporalAmount amount = parseInterval(EMPTY, value, DataType.INTERVAL_YEAR_TO_MONTH); + assertEquals(maybeNegate(randomSign, Period.ofYears(randomYear).plusMonths(randomMonth)), amount); + } + + public void testDayToHour() throws Exception { + String randomSign = randomSign(); + int randomDay = randomNonNegativeInt(); + int randomHour = randomInt(23); + String value = format(Locale.ROOT, "%s%d %d", randomSign, randomDay, randomHour); + TemporalAmount amount = parseInterval(EMPTY, value, DataType.INTERVAL_DAY_TO_HOUR); + assertEquals(maybeNegate(randomSign, Duration.ofDays(randomDay).plusHours(randomHour)), amount); + } + + public void testDayToMinute() throws Exception { + String randomSign = randomSign(); + int randomDay = randomNonNegativeInt(); + int randomHour = randomInt(23); + int randomMinute = randomInt(59); + String value = format(Locale.ROOT, "%s%d %d:%d", randomSign, randomDay, randomHour, randomMinute); + TemporalAmount amount = parseInterval(EMPTY, value, DataType.INTERVAL_DAY_TO_MINUTE); + assertEquals(maybeNegate(randomSign, Duration.ofDays(randomDay).plusHours(randomHour).plusMinutes(randomMinute)), amount); + } + + public void testDayToSecond() throws Exception { + String randomSign = randomSign(); + int randomDay = randomNonNegativeInt(); + int randomHour = randomInt(23); + int randomMinute = randomInt(59); + int randomSecond = randomInt(59); + int randomMilli = randomInt(999999999); + + String value = format(Locale.ROOT, "%s%d %d:%d:%d.%d", randomSign, randomDay, randomHour, randomMinute, randomSecond, + randomMilli); + TemporalAmount amount = parseInterval(EMPTY, value, DataType.INTERVAL_DAY_TO_SECOND); + assertEquals(maybeNegate(randomSign, Duration.ofDays(randomDay).plusHours(randomHour).plusMinutes(randomMinute) + .plusSeconds(randomSecond).plusMillis(randomMilli)), amount); + } + + public void testHourToMinute() throws Exception { + String randomSign = randomSign(); + int randomHour = randomNonNegativeInt(); + int randomMinute = randomInt(59); + String value = format(Locale.ROOT, "%s%d:%d", randomSign, randomHour, randomMinute); + TemporalAmount amount = parseInterval(EMPTY, value, DataType.INTERVAL_HOUR_TO_MINUTE); + assertEquals(maybeNegate(randomSign, Duration.ofHours(randomHour).plusMinutes(randomMinute)), amount); + } + + public void testHourToSecond() throws Exception { + String randomSign = randomSign(); + int randomHour = randomNonNegativeInt(); + int randomMinute = randomInt(59); + int randomSecond = randomInt(59); + int randomMilli = randomInt(999999999); + + String value = format(Locale.ROOT, "%s%d:%d:%d.%d", randomSign, randomHour, randomMinute, randomSecond, randomMilli); + TemporalAmount amount = parseInterval(EMPTY, value, DataType.INTERVAL_HOUR_TO_SECOND); + assertEquals(maybeNegate(randomSign, + Duration.ofHours(randomHour).plusMinutes(randomMinute).plusSeconds(randomSecond).plusMillis(randomMilli)), amount); + } + + public void testMinuteToSecond() throws Exception { + String randomSign = randomSign(); + int randomMinute = randomNonNegativeInt(); + int randomSecond = randomInt(59); + int randomMilli = randomInt(999999999); + + String value = format(Locale.ROOT, "%s%d:%d.%d", randomSign, randomMinute, randomSecond, randomMilli); + TemporalAmount amount = parseInterval(EMPTY, value, DataType.INTERVAL_MINUTE_TO_SECOND); + assertEquals(maybeNegate(randomSign, Duration.ofMinutes(randomMinute).plusSeconds(randomSecond).plusMillis(randomMilli)), amount); + } + + + // validation + public void testYearToMonthTooBig() throws Exception { + String randomSign = randomSign(); + int randomYear = randomNonNegativeInt(); + int randomTooBig = randomIntBetween(12, 9999); + String value = format(Locale.ROOT, "%s%d-%d", randomSign, randomYear, randomTooBig); + ParsingException pe = expectThrows(ParsingException.class, + () -> parseInterval(EMPTY, value, DataType.INTERVAL_YEAR_TO_MONTH)); + assertEquals("line -1:0: Invalid [INTERVAL YEAR TO MONTH] value [" + value + "]: [MONTH] unit has illegal value [" + randomTooBig + + "], expected a positive number up to [11]", pe.getMessage()); + } + + public void testMillisTooBig() throws Exception { + String randomSign = randomSign(); + int randomSeconds = randomNonNegativeInt(); + int millisTooLarge = 1234567890; + String value = format(Locale.ROOT, "%s%d.%d", randomSign, randomSeconds, millisTooLarge); + ParsingException pe = expectThrows(ParsingException.class, + () -> parseInterval(EMPTY, value, DataType.INTERVAL_SECOND)); + assertEquals("line -1:0: Invalid [INTERVAL SECOND] value [" + value + "]: [MILLISECOND] unit has illegal value [" + millisTooLarge + + "], expected a positive number up to [999999999]", pe.getMessage()); + } + + public void testDayToMinuteTooBig() throws Exception { + String randomSign = randomSign(); + int randomDay = randomNonNegativeInt(); + int randomHour = randomIntBetween(24, 9999); + int randomMinute = randomInt(59); + String value = format(Locale.ROOT, "%s%d %d:%d", randomSign, randomDay, randomHour, randomMinute); + ParsingException pe = expectThrows(ParsingException.class, () -> parseInterval(EMPTY, value, DataType.INTERVAL_DAY_TO_MINUTE)); + assertEquals("line -1:0: Invalid [INTERVAL DAY TO MINUTE] value [" + value + "]: [HOUR] unit has illegal value [" + randomHour + + "], expected a positive number up to [23]", pe.getMessage()); + } + + public void testExtraCharLeading() throws Exception { + String value = "a123"; + ParsingException pe = expectThrows(ParsingException.class, () -> parseInterval(EMPTY, value, DataType.INTERVAL_YEAR)); + assertEquals("line -1:0: Invalid [INTERVAL YEAR] value [a123]: expected digit (at [0]) but found [a]", pe.getMessage()); + } + + public void testExtraCharTrailing() throws Exception { + String value = "123x"; + ParsingException pe = expectThrows(ParsingException.class, + () -> parseInterval(EMPTY, value, DataType.INTERVAL_YEAR)); + assertEquals("line -1:0: Invalid [INTERVAL YEAR] value [123x]: unexpected trailing characters found [x]", pe.getMessage()); + } + + public void testIncorrectSeparator() throws Exception { + String value = "123^456"; + ParsingException pe = expectThrows(ParsingException.class, () -> parseInterval(EMPTY, value, DataType.INTERVAL_SECOND)); + assertEquals("line -1:0: Invalid [INTERVAL SECOND] value [123^456]: expected [.] (at [3]) but found [^]", pe.getMessage()); + } + + private static int randomNonNegativeInt() { + int random = randomInt(); + return random == Integer.MIN_VALUE ? 0 : Math.abs(random); + } + + //Maybe returns a sign, which might be + or -. + private static String randomSign() { + return randomBoolean() ? (randomBoolean() ? "+" : "-") : ""; + } + + private Object maybeNegate(String randomSign, TemporalAmount interval) { + return "-".equals(randomSign) ? IntervalUtils.negate(interval) : interval; + } +} \ No newline at end of file diff --git a/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/parser/EscapedFunctionsTests.java b/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/parser/EscapedFunctionsTests.java index 0df368252d187..9492d63b7d323 100644 --- a/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/parser/EscapedFunctionsTests.java +++ b/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/parser/EscapedFunctionsTests.java @@ -142,9 +142,9 @@ public void testFunctionWithFunctionWithArg() { public void testFunctionWithFunctionWithArgAndParams() { Function f = (Function) parser.createExpression("POWER(?, {fn POWER({fn ABS(?)}, {fN ABS(?)})})", - asList(new SqlTypedParamValue(DataType.LONG, 1), - new SqlTypedParamValue(DataType.LONG, 1), - new SqlTypedParamValue(DataType.LONG, 1))); + asList(new SqlTypedParamValue(DataType.LONG.esType, 1), + new SqlTypedParamValue(DataType.LONG.esType, 1), + new SqlTypedParamValue(DataType.LONG.esType, 1))); assertEquals("POWER", f.functionName()); assertEquals(2, f.arguments().size()); diff --git a/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/parser/ExpressionTests.java b/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/parser/ExpressionTests.java index 70ff5ac8c0e7d..842c23926e094 100644 --- a/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/parser/ExpressionTests.java +++ b/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/parser/ExpressionTests.java @@ -10,6 +10,7 @@ import org.elasticsearch.xpack.sql.expression.Literal; import org.elasticsearch.xpack.sql.expression.function.UnresolvedFunction; import org.elasticsearch.xpack.sql.expression.function.scalar.Cast; +import org.elasticsearch.xpack.sql.expression.literal.Interval; import org.elasticsearch.xpack.sql.expression.predicate.operator.arithmetic.Add; import org.elasticsearch.xpack.sql.expression.predicate.operator.arithmetic.Mul; import org.elasticsearch.xpack.sql.expression.predicate.operator.arithmetic.Neg; @@ -18,6 +19,12 @@ import org.elasticsearch.xpack.sql.expression.predicate.operator.comparison.NotEquals; import org.elasticsearch.xpack.sql.type.DataType; +import java.time.Duration; +import java.time.Period; +import java.time.temporal.TemporalAmount; +import java.util.Locale; + +import static java.lang.String.format; import static org.hamcrest.core.StringStartsWith.startsWith; public class ExpressionTests extends ESTestCase { @@ -72,9 +79,9 @@ public void testLiteralLong() { } public void testLiteralLongNegative() { - Expression lt = parser.createExpression(String.valueOf(Long.MIN_VALUE)); + Expression lt = parser.createExpression(String.valueOf(-Long.MAX_VALUE)); assertTrue(lt.foldable()); - assertEquals(Long.MIN_VALUE, lt.fold()); + assertEquals(-Long.MAX_VALUE, lt.fold()); assertEquals(DataType.LONG, lt.dataType()); } @@ -120,6 +127,59 @@ public void testLiteralDecimalTooBig() { assertEquals("Number [1.9976931348623157e+308] is too large", ex.getErrorMessage()); } + public void testExactDayTimeInterval() throws Exception { + int number = randomIntBetween(-100, 100); + assertEquals(Duration.ofDays(number), intervalOf("INTERVAL " + number + " DAY")); + number = randomIntBetween(-100, 100); + assertEquals(Duration.ofHours(number), intervalOf("INTERVAL " + number + " HOUR")); + number = randomIntBetween(-100, 100); + assertEquals(Duration.ofMinutes(number), intervalOf("INTERVAL " + number + " MINUTE")); + number = randomIntBetween(-100, 100); + assertEquals(Duration.ofSeconds(number), intervalOf("INTERVAL " + number + " SECOND")); + } + + public void testExactYearMonthInterval() throws Exception { + int number = randomIntBetween(-100, 100); + assertEquals(Period.ofYears(number), intervalOf("INTERVAL " + number + " YEAR")); + number = randomIntBetween(-100, 100); + assertEquals(Period.ofMonths(number), intervalOf("INTERVAL " + number + " MONTH")); + } + + public void testStringInterval() throws Exception { + int randomDay = randomInt(1024); + int randomHour = randomInt(23); + int randomMinute = randomInt(59); + int randomSecond = randomInt(59); + int randomMilli = randomInt(999999999); + + String value = format(Locale.ROOT, "INTERVAL '%d %d:%d:%d.%d' DAY TO SECOND", randomDay, randomHour, randomMinute, randomSecond, + randomMilli); + assertEquals(Duration.ofDays(randomDay).plusHours(randomHour).plusMinutes(randomMinute).plusSeconds(randomSecond) + .plusMillis(randomMilli), intervalOf(value)); + } + + public void testNegativeStringInterval() throws Exception { + int randomDay = randomInt(1024); + int randomHour = randomInt(23); + int randomMinute = randomInt(59); + int randomSecond = randomInt(59); + int randomMilli = randomInt(999999999); + + String value = format(Locale.ROOT, "INTERVAL -'%d %d:%d:%d.%d' DAY TO SECOND", randomDay, randomHour, randomMinute, randomSecond, + randomMilli); + assertEquals(Duration.ofDays(randomDay).plusHours(randomHour).plusMinutes(randomMinute).plusSeconds(randomSecond) + .plusMillis(randomMilli).negated(), intervalOf(value)); + } + + private TemporalAmount intervalOf(String query) { + Expression lt = parser.createExpression(query); + assertEquals(Literal.class, lt.getClass()); + Literal l = (Literal) lt; + Object value = l.value(); + assertTrue(Interval.class.isAssignableFrom(value.getClass())); + return ((Interval) value).interval(); + } + public void testLiteralTimesLiteral() { Expression expr = parser.createExpression("10*2"); assertEquals(Mul.class, expr.getClass()); diff --git a/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/parser/LikeEscapingParsingTests.java b/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/parser/LikeEscapingParsingTests.java index 0a20202a67dbd..9f8cae9e52918 100644 --- a/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/parser/LikeEscapingParsingTests.java +++ b/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/parser/LikeEscapingParsingTests.java @@ -33,7 +33,7 @@ private LikePattern like(String pattern) { Expression exp = null; boolean parameterized = randomBoolean(); if (parameterized) { - exp = parser.createExpression("exp LIKE ?", singletonList(new SqlTypedParamValue(DataType.KEYWORD, pattern))); + exp = parser.createExpression("exp LIKE ?", singletonList(new SqlTypedParamValue(DataType.KEYWORD.esType, pattern))); } else { exp = parser.createExpression(String.format(Locale.ROOT, "exp LIKE '%s'", pattern)); } diff --git a/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/plan/logical/command/sys/SysParserTests.java b/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/plan/logical/command/sys/SysParserTests.java index 7e73374aab025..974bdd2de8f97 100644 --- a/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/plan/logical/command/sys/SysParserTests.java +++ b/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/plan/logical/command/sys/SysParserTests.java @@ -58,7 +58,11 @@ public void testSysTypes() throws Exception { Command cmd = sql("SYS TYPES").v1(); List names = asList("BYTE", "LONG", "BINARY", "NULL", "INTEGER", "SHORT", "HALF_FLOAT", "SCALED_FLOAT", "FLOAT", "DOUBLE", - "KEYWORD", "TEXT", "IP", "BOOLEAN", "DATE", "UNSUPPORTED", "OBJECT", "NESTED"); + "KEYWORD", "TEXT", "IP", "BOOLEAN", "DATE", + "INTERVAL_YEAR", "INTERVAL_MONTH", "INTERVAL_DAY", "INTERVAL_HOUR", "INTERVAL_MINUTE", "INTERVAL_SECOND", + "INTERVAL_YEAR_TO_MONTH", "INTERVAL_DAY_TO_HOUR", "INTERVAL_DAY_TO_MINUTE", "INTERVAL_DAY_TO_SECOND", + "INTERVAL_HOUR_TO_MINUTE", "INTERVAL_HOUR_TO_SECOND", "INTERVAL_MINUTE_TO_SECOND", + "UNSUPPORTED", "OBJECT", "NESTED"); cmd.execute(null, ActionListener.wrap(r -> { assertEquals(19, r.columnCount()); diff --git a/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/plan/logical/command/sys/SysTablesTests.java b/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/plan/logical/command/sys/SysTablesTests.java index c6e1c389bb199..55f7994534fca 100644 --- a/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/plan/logical/command/sys/SysTablesTests.java +++ b/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/plan/logical/command/sys/SysTablesTests.java @@ -229,7 +229,7 @@ public void testSysTablesTypesEnumerationWoString() throws Exception { } private SqlTypedParamValue param(Object value) { - return new SqlTypedParamValue(DataTypes.fromJava(value), value); + return new SqlTypedParamValue(DataTypes.fromJava(value).esType, value); } private Tuple sql(String sql, List params) { diff --git a/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/type/DataTypeConversionTests.java b/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/type/DataTypeConversionTests.java index 7a04139430e33..5b959c6130038 100644 --- a/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/type/DataTypeConversionTests.java +++ b/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/type/DataTypeConversionTests.java @@ -252,7 +252,7 @@ public void testCommonType() { public void testEsDataTypes() { for (DataType type : DataType.values()) { - assertEquals(type, DataType.fromEsType(type.esType)); + assertEquals(type, DataType.fromTypeName(type.esType)); } } diff --git a/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/type/DataTypesTests.java b/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/type/DataTypesTests.java index 0a34c697bdf64..0d61edf860ce7 100644 --- a/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/type/DataTypesTests.java +++ b/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/type/DataTypesTests.java @@ -22,7 +22,7 @@ public class DataTypesTests extends ESTestCase { public void testMetaDataType() { assertEquals(Integer.valueOf(9), metaSqlDataType(DATE)); DataType t = randomDataTypeNoDate(); - assertEquals(t.jdbcType.getVendorTypeNumber(), metaSqlDataType(t)); + assertEquals(t.sqlType.getVendorTypeNumber(), metaSqlDataType(t)); } public void testMetaDateTypeSub() { From beae06981f512ed9871710c74f01a549b333ae57 Mon Sep 17 00:00:00 2001 From: Costin Leau Date: Wed, 14 Nov 2018 00:02:21 +0200 Subject: [PATCH 2/6] Loosen type compatibility --- .../xpack/sql/analysis/analyzer/Verifier.java | 15 +- .../arithmetic/ArithmeticOperation.java | 45 +++++- .../xpack/sql/type/DataType.java | 75 ++++------ .../xpack/sql/type/DataTypeConversion.java | 29 +++- .../xpack/sql/type/DataTypes.java | 137 ++++++++++++++--- .../literal/IntervalUtilsTests.java | 52 ++++--- .../sql/type/DataTypeConversionTests.java | 138 ++++++++++++------ .../xpack/sql/type/DataTypesTests.java | 59 +++++++- 8 files changed, 412 insertions(+), 138 deletions(-) diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/analysis/analyzer/Verifier.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/analysis/analyzer/Verifier.java index 5394625d88272..e2309a134a22f 100644 --- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/analysis/analyzer/Verifier.java +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/analysis/analyzer/Verifier.java @@ -525,7 +525,7 @@ private static void validateInExpression(LogicalPlan p, Set localFailur e.forEachUp((In in) -> { DataType dt = in.value().dataType(); for (Expression value : in.list()) { - if (!in.value().dataType().isCompatibleWith(value.dataType())) { + if (areTypesCompatible(in.value().dataType(), value.dataType()) == false) { localFailures.add(fail(value, "expected data type [%s], value provided is of type [%s]", dt, value.dataType())); return; @@ -534,4 +534,15 @@ private static void validateInExpression(LogicalPlan p, Set localFailur }, In.class)); } -} + + private static boolean areTypesCompatible(DataType left, DataType right) { + if (left == right) { + return true; + } else { + return + (left == DataType.NULL || right == DataType.NULL) || + (left.isString() && right.isString()) || + (left.isNumeric() && right.isNumeric()); + } + } +} \ No newline at end of file diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/predicate/operator/arithmetic/ArithmeticOperation.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/predicate/operator/arithmetic/ArithmeticOperation.java index cdc3cb5415fae..1209a24d86589 100644 --- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/predicate/operator/arithmetic/ArithmeticOperation.java +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/predicate/operator/arithmetic/ArithmeticOperation.java @@ -13,13 +13,51 @@ import org.elasticsearch.xpack.sql.tree.Location; import org.elasticsearch.xpack.sql.type.DataType; import org.elasticsearch.xpack.sql.type.DataTypeConversion; +import org.elasticsearch.xpack.sql.type.DataTypes; + +import static org.elasticsearch.common.logging.LoggerMessageFormat.format; public abstract class ArithmeticOperation extends BinaryOperator { + private DataType dataType; + protected ArithmeticOperation(Location location, Expression left, Expression right, BinaryArithmeticOperation operation) { super(location, left, right, operation); } + @Override + protected TypeResolution resolveType() { + if (!childrenResolved()) { + return new TypeResolution("Unresolved children"); + } + + // arithmetic operation can work on: + // 1. numbers + // 2. intervals (of compatible types) + // 3. dates and intervals + // 4. single unit intervals and numbers + + DataType l = left().dataType(); + DataType r = right().dataType(); + + // 1. both are numbers + if (l.isNumeric() && r.isNumeric()) { + return TypeResolution.TYPE_RESOLVED; + } + // 2. 3. 4. intervals + if ((DataTypes.isInterval(l) || DataTypes.isInterval(r))) { + dataType = DataTypeConversion.commonType(l, r); + if (dataType == null) { + return new TypeResolution(format("[{}] has arguments with incompatible types [{}] and [{}]", symbol(), l, r)); + } else { + return TypeResolution.TYPE_RESOLVED; + } + } + + // fall-back to default checks + return super.resolveType(); + } + @Override protected TypeResolution resolveInputType(Expression e, Expressions.ParamOrdinal paramOrdinal) { return Expressions.typeMustBeNumeric(e, symbol(), paramOrdinal); @@ -32,11 +70,14 @@ public ArithmeticOperation swapLeftAndRight() { @Override public DataType dataType() { - return DataTypeConversion.commonType(left().dataType(), right().dataType()); + if (dataType == null) { + dataType = DataTypeConversion.commonType(left().dataType(), right().dataType()); + } + return dataType; } @Override protected Pipe makePipe() { return new BinaryArithmeticPipe(location(), this, Expressions.pipe(left()), Expressions.pipe(right()), function()); } -} +} \ No newline at end of file diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/type/DataType.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/type/DataType.java index 20071f84e9812..c704e3f63a278 100644 --- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/type/DataType.java +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/type/DataType.java @@ -21,52 +21,53 @@ public enum DataType { // @formatter:off - // jdbc type, size, defPrecision, dispSize, int, rat, docvals - NULL( JDBCType.NULL, 0, 0, 0), - UNSUPPORTED( JDBCType.OTHER, 0, 0, 0), - BOOLEAN( JDBCType.BOOLEAN, 1, 1, 1), - BYTE( JDBCType.TINYINT, Byte.BYTES, 3, 5, true, false, true), - SHORT( JDBCType.SMALLINT, Short.BYTES, 5, 6, true, false, true), - INTEGER( JDBCType.INTEGER, Integer.BYTES, 10, 11, true, false, true), - LONG( JDBCType.BIGINT, Long.BYTES, 19, 20, true, false, true), + // jdbc type, size, defPrecision,dispSize, int, rat, docvals + NULL( JDBCType.NULL, 0, 0, 0, false, false, false), + UNSUPPORTED( JDBCType.OTHER, 0, 0, 0, false, false, false), + BOOLEAN( JDBCType.BOOLEAN, 1, 1, 1, false, false, false), + BYTE( JDBCType.TINYINT, Byte.BYTES, 3, 5, true, false, true), + SHORT( JDBCType.SMALLINT, Short.BYTES, 5, 6, true, false, true), + INTEGER( JDBCType.INTEGER, Integer.BYTES, 10, 11, true, false, true), + LONG( JDBCType.BIGINT, Long.BYTES, 19, 20, true, false, true), // 53 bits defaultPrecision ~ 15(15.95) decimal digits (53log10(2)), - DOUBLE( JDBCType.DOUBLE, Double.BYTES, 15, 25, false, true, true), + DOUBLE( JDBCType.DOUBLE, Double.BYTES, 15, 25, false, true, true), // 24 bits defaultPrecision - 24*log10(2) =~ 7 (7.22) - FLOAT( JDBCType.REAL, Float.BYTES, 7, 15, false, true, true), - HALF_FLOAT( JDBCType.FLOAT, Double.BYTES, 16, 25, false, true, true), + FLOAT( JDBCType.REAL, Float.BYTES, 7, 15, false, true, true), + HALF_FLOAT( JDBCType.FLOAT, Double.BYTES, 16, 25, false, true, true), // precision is based on long - SCALED_FLOAT( JDBCType.FLOAT, Double.BYTES, 19, 25, false, true, true), - KEYWORD( JDBCType.VARCHAR, Integer.MAX_VALUE, 256, 0), + SCALED_FLOAT( JDBCType.FLOAT, Double.BYTES, 19, 25, false, true, true), + KEYWORD( JDBCType.VARCHAR, Integer.MAX_VALUE, 256, 0, false, false, true), TEXT( JDBCType.VARCHAR, Integer.MAX_VALUE, Integer.MAX_VALUE, 0, false, false, false), OBJECT( JDBCType.STRUCT, -1, 0, 0, false, false, false), NESTED( JDBCType.STRUCT, -1, 0, 0, false, false, false), - BINARY( JDBCType.VARBINARY, -1, Integer.MAX_VALUE, 0), + BINARY( JDBCType.VARBINARY, -1, Integer.MAX_VALUE, 0, false, false, false), // since ODBC and JDBC interpret precision for Date as display size, // the precision is 23 (number of chars in ISO8601 with millis) + Z (the UTC timezone) // see https://github.com/elastic/elasticsearch/issues/30386#issuecomment-386807288 - DATE( JDBCType.TIMESTAMP, Long.BYTES, 24, 24), + DATE( JDBCType.TIMESTAMP, Long.BYTES, 24, 24, false, false, true), // // specialized types // // IP can be v4 or v6. The latter has 2^128 addresses or 340,282,366,920,938,463,463,374,607,431,768,211,456 // aka 39 chars - IP( JDBCType.VARCHAR, 39, 39, 0,false, false, true), + IP( JDBCType.VARCHAR, 39, 39, 0, false, false, true), // // INTERVALS // the list is long as there are a lot of variations and that's what clients (ODBC) expect - INTERVAL_YEAR( ExtTypes.INTERVAL_YEAR, Integer.BYTES, 7, 7), - INTERVAL_MONTH( ExtTypes.INTERVAL_MONTH, Integer.BYTES, 7, 7), - INTERVAL_YEAR_TO_MONTH( ExtTypes.INTERVAL_YEAR_TO_MONTH, Integer.BYTES, 7, 7), - INTERVAL_DAY( ExtTypes.INTERVAL_DAY, Long.BYTES, 23, 23), - INTERVAL_HOUR( ExtTypes.INTERVAL_HOUR, Long.BYTES, 23, 23), - INTERVAL_MINUTE( ExtTypes.INTERVAL_MINUTE, Long.BYTES, 23, 23), - INTERVAL_SECOND( ExtTypes.INTERVAL_SECOND, Long.BYTES, 23, 23), - INTERVAL_DAY_TO_HOUR( ExtTypes.INTERVAL_DAY_TO_HOUR, Long.BYTES, 23, 23), - INTERVAL_DAY_TO_MINUTE( ExtTypes.INTERVAL_DAY_TO_MINUTE, Long.BYTES, 23, 23), - INTERVAL_DAY_TO_SECOND( ExtTypes.INTERVAL_DAY_TO_SECOND, Long.BYTES, 23, 23), - INTERVAL_HOUR_TO_MINUTE( ExtTypes.INTERVAL_HOUR_TO_MINUTE, Long.BYTES, 23, 23), - INTERVAL_HOUR_TO_SECOND( ExtTypes.INTERVAL_HOUR_TO_SECOND, Long.BYTES, 23, 23), - INTERVAL_MINUTE_TO_SECOND(ExtTypes.INTERVAL_MINUTE_TO_SECOND,Long.BYTES, 23, 23); + // jdbc type, size, prec,disp, int, rat, docvals + INTERVAL_YEAR( ExtTypes.INTERVAL_YEAR, Integer.BYTES, 7, 7, false, false, false), + INTERVAL_MONTH( ExtTypes.INTERVAL_MONTH, Integer.BYTES, 7, 7, false, false, false), + INTERVAL_DAY( ExtTypes.INTERVAL_DAY, Long.BYTES, 23, 23, false, false, false), + INTERVAL_HOUR( ExtTypes.INTERVAL_HOUR, Long.BYTES, 23, 23, false, false, false), + INTERVAL_MINUTE( ExtTypes.INTERVAL_MINUTE, Long.BYTES, 23, 23, false, false, false), + INTERVAL_SECOND( ExtTypes.INTERVAL_SECOND, Long.BYTES, 23, 23, false, false, false), + INTERVAL_YEAR_TO_MONTH( ExtTypes.INTERVAL_YEAR_TO_MONTH, Integer.BYTES, 7, 7, false, false, false), + INTERVAL_DAY_TO_HOUR( ExtTypes.INTERVAL_DAY_TO_HOUR, Long.BYTES, 23, 23, false, false, false), + INTERVAL_DAY_TO_MINUTE( ExtTypes.INTERVAL_DAY_TO_MINUTE, Long.BYTES, 23, 23, false, false, false), + INTERVAL_DAY_TO_SECOND( ExtTypes.INTERVAL_DAY_TO_SECOND, Long.BYTES, 23, 23, false, false, false), + INTERVAL_HOUR_TO_MINUTE( ExtTypes.INTERVAL_HOUR_TO_MINUTE, Long.BYTES, 23, 23, false, false, false), + INTERVAL_HOUR_TO_SECOND( ExtTypes.INTERVAL_HOUR_TO_SECOND, Long.BYTES, 23, 23, false, false, false), + INTERVAL_MINUTE_TO_SECOND(ExtTypes.INTERVAL_MINUTE_TO_SECOND,Long.BYTES, 23, 23, false, false, false); // @formatter:on private static final Map odbcToEs; @@ -182,10 +183,6 @@ public enum DataType { this.defaultDocValues = defaultDocValues; } - DataType(SQLType sqlType, int size, int defaultPrecision, int displaySize) { - this(sqlType, size, defaultPrecision, displaySize, false, false, true); - } - public String sqlName() { return sqlType.getName(); } @@ -202,7 +199,6 @@ public boolean isNumeric() { return isInteger || isRational; } - /** * Returns true if value is signed, false otherwise (including if the type is not numeric) */ @@ -218,17 +214,6 @@ public boolean isString() { public boolean isPrimitive() { return this != OBJECT && this != NESTED; } - - public boolean isCompatibleWith(DataType other) { - if (this == other) { - return true; - } else { - return - (this == NULL || other == NULL) || - (isString() && other.isString()) || - (isNumeric() && other.isNumeric()); - } - } public static DataType fromOdbcType(String odbcType) { return odbcToEs.get(odbcType); diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/type/DataTypeConversion.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/type/DataTypeConversion.java index 0c722fdb5ac7e..ecd4ae72f4908 100644 --- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/type/DataTypeConversion.java +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/type/DataTypeConversion.java @@ -78,6 +78,31 @@ public static DataType commonType(DataType left, DataType right) { return left; } } + // interval and dates + if (DataTypes.isInterval(left)) { + // intervals widening + if (DataTypes.isInterval(right)) { + // null returned for incompatible intervals + return DataTypes.compatibleInterval(left, right); + } + if (right == DATE) { + return right; + } + // single unit intervals and numbers + if (DataTypes.isSingularInterval(left) && right.isInteger()) { + return left; + } + } + + if (DataTypes.isInterval(right)) { + if (left == DATE) { + return left; + } + if (DataTypes.isSingularInterval(right) && left.isInteger()) { + return right; + } + } + // none found return null; } @@ -102,10 +127,10 @@ public static Conversion conversionFor(DataType from, DataType to) { if (from == to) { return Conversion.IDENTITY; } - if (to == DataType.NULL || from == DataType.NULL) { + if (to == NULL || from == NULL) { return Conversion.NULL; } - if (from == DataType.NULL) { + if (from == NULL) { return Conversion.NULL; } diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/type/DataTypes.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/type/DataTypes.java index beb8ece94bf5d..89bf15c7b60b0 100644 --- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/type/DataTypes.java +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/type/DataTypes.java @@ -9,48 +9,68 @@ import org.elasticsearch.xpack.sql.expression.literal.Interval; import org.joda.time.DateTime; +import static org.elasticsearch.xpack.sql.type.DataType.BOOLEAN; +import static org.elasticsearch.xpack.sql.type.DataType.BYTE; +import static org.elasticsearch.xpack.sql.type.DataType.DATE; +import static org.elasticsearch.xpack.sql.type.DataType.DOUBLE; +import static org.elasticsearch.xpack.sql.type.DataType.FLOAT; +import static org.elasticsearch.xpack.sql.type.DataType.INTEGER; +import static org.elasticsearch.xpack.sql.type.DataType.INTERVAL_DAY; +import static org.elasticsearch.xpack.sql.type.DataType.INTERVAL_DAY_TO_HOUR; +import static org.elasticsearch.xpack.sql.type.DataType.INTERVAL_MINUTE_TO_SECOND; +import static org.elasticsearch.xpack.sql.type.DataType.INTERVAL_MONTH; +import static org.elasticsearch.xpack.sql.type.DataType.INTERVAL_SECOND; +import static org.elasticsearch.xpack.sql.type.DataType.INTERVAL_YEAR; +import static org.elasticsearch.xpack.sql.type.DataType.INTERVAL_YEAR_TO_MONTH; +import static org.elasticsearch.xpack.sql.type.DataType.KEYWORD; +import static org.elasticsearch.xpack.sql.type.DataType.LONG; +import static org.elasticsearch.xpack.sql.type.DataType.NULL; +import static org.elasticsearch.xpack.sql.type.DataType.SHORT; +import static org.elasticsearch.xpack.sql.type.DataType.UNSUPPORTED; +import static org.elasticsearch.xpack.sql.type.DataType.fromTypeName; + public final class DataTypes { private DataTypes() {} public static boolean isNull(DataType from) { - return from == DataType.NULL; + return from == NULL; } public static boolean isUnsupported(DataType from) { - return from == DataType.UNSUPPORTED; + return from == UNSUPPORTED; } public static DataType fromJava(Object value) { if (value == null) { - return DataType.NULL; + return NULL; } if (value instanceof Integer) { - return DataType.INTEGER; + return INTEGER; } if (value instanceof Long) { - return DataType.LONG; + return LONG; } if (value instanceof Boolean) { - return DataType.BOOLEAN; + return BOOLEAN; } if (value instanceof Double) { - return DataType.DOUBLE; + return DOUBLE; } if (value instanceof Float) { - return DataType.FLOAT; + return FLOAT; } if (value instanceof Byte) { - return DataType.BYTE; + return BYTE; } if (value instanceof Short) { - return DataType.SHORT; + return SHORT; } if (value instanceof DateTime) { - return DataType.DATE; + return DATE; } if (value instanceof String || value instanceof Character) { - return DataType.KEYWORD; + return KEYWORD; } if (value instanceof Interval) { return ((Interval) value).dataType(); @@ -58,6 +78,89 @@ public static DataType fromJava(Object value) { throw new SqlIllegalArgumentException("No idea what's the DataType for {}", value.getClass()); } + + // + // Interval utilities + // + // some of the methods below could have used an EnumSet however isDayTime would have required a large initialization block + // for this reason, these use the ordinal directly (and thus avoid the type check in EnumSet) + + public static boolean isInterval(DataType type) { + int ordinal = type.ordinal(); + return ordinal >= INTERVAL_YEAR.ordinal() && ordinal <= INTERVAL_MINUTE_TO_SECOND.ordinal(); + } + + public static boolean isSingularInterval(DataType type) { + int ordinal = type.ordinal(); + return ordinal >= INTERVAL_YEAR.ordinal() && ordinal <= INTERVAL_SECOND.ordinal(); + } + + // return the compatible interval between the two - it is assumed the types are intervals + // YEAR and MONTH -> YEAR_TO_MONTH + // DAY... SECOND -> DAY_TIME + // YEAR_MONTH and DAY_SECOND are NOT compatible + public static DataType compatibleInterval(DataType left, DataType right) { + if (left == right) { + return left; + } + if (isYearMonthInterval(left) && isYearMonthInterval(right)) { + // no need to look at YEAR/YEAR or MONTH/MONTH as these are equal and already handled + return INTERVAL_YEAR_TO_MONTH; + } + if (isDayTimeInterval(left) && isDayTimeInterval(right)) { + // to avoid specifying the combinations, extract the leading and trailing unit from the name + // D > H > S > M which is also the alphabetical order + String lName = left.name().substring(9); + String rName = right.name().substring(9); + + char leading = lName.charAt(0); + if (rName.charAt(0) < leading) { + leading = rName.charAt(0); + } + // look at the trailing unit + if (lName.length() > 6) { + int indexOf = lName.indexOf("_TO_"); + lName = lName.substring(indexOf + 4); + } + if (rName.length() > 6) { + int indexOf = rName.indexOf("_TO_"); + rName = rName.substring(indexOf + 4); + } + char trailing = lName.charAt(0); + if (rName.charAt(0) > trailing) { + trailing = rName.charAt(0); + } + + return fromTypeName("INTERVAL_" + intervalUnit(leading) + "_TO_" + intervalUnit(trailing)); + } + return null; + } + + private static boolean isYearMonthInterval(DataType type) { + return type == INTERVAL_YEAR || type == INTERVAL_MONTH || type == INTERVAL_YEAR_TO_MONTH; + } + + private static boolean isDayTimeInterval(DataType type) { + int ordinal = type.ordinal(); + return (ordinal >= INTERVAL_DAY.ordinal() && ordinal <= INTERVAL_SECOND.ordinal()) + || (ordinal >= INTERVAL_DAY_TO_HOUR.ordinal() && ordinal <= INTERVAL_MINUTE_TO_SECOND.ordinal()); + } + + private static String intervalUnit(char unitChar) { + switch (unitChar) { + case 'D': + return "DAY"; + case 'H': + return "HOUR"; + case 'M': + return "MINUTE"; + case 'S': + return "SECOND"; + default: + throw new SqlIllegalArgumentException("Unknown unit {}", unitChar); + } + } + // // Metadata methods, mainly for ODBC. // As these are fairly obscure and limited in use, there is no point to promote them as a full type methods @@ -67,7 +170,7 @@ public static DataType fromJava(Object value) { // https://docs.microsoft.com/en-us/sql/relational-databases/native-client-odbc-date-time/metadata-catalog // https://github.com/elastic/elasticsearch/issues/30386 public static Integer metaSqlDataType(DataType t) { - if (t == DataType.DATE) { + if (t == DATE) { // ODBC SQL_DATETME return Integer.valueOf(9); } @@ -78,7 +181,7 @@ public static Integer metaSqlDataType(DataType t) { // https://github.com/elastic/elasticsearch/issues/30386 // https://docs.microsoft.com/en-us/sql/odbc/reference/syntax/sqlgettypeinfo-function?view=sql-server-2017 public static Integer metaSqlDateTimeSub(DataType t) { - if (t == DataType.DATE) { + if (t == DATE) { // ODBC SQL_CODE_TIMESTAMP return Integer.valueOf(3); } @@ -89,7 +192,7 @@ public static Integer metaSqlDateTimeSub(DataType t) { // https://docs.microsoft.com/en-us/sql/odbc/reference/appendixes/decimal-digits?view=sql-server-2017 public static Short metaSqlMinimumScale(DataType t) { // TODO: return info for HALF/SCALED_FLOATS (should be based on field not type) - if (t == DataType.DATE) { + if (t == DATE) { return Short.valueOf((short) 3); } if (t.isInteger()) { @@ -104,7 +207,7 @@ public static Short metaSqlMinimumScale(DataType t) { public static Short metaSqlMaximumScale(DataType t) { // TODO: return info for HALF/SCALED_FLOATS (should be based on field not type) - if (t == DataType.DATE) { + if (t == DATE) { return Short.valueOf((short) 3); } if (t.isInteger()) { @@ -124,4 +227,4 @@ public static Integer metaSqlRadix(DataType t) { // null means radix is not applicable for the given type. return t.isInteger() ? Integer.valueOf(10) : (t.isRational() ? Integer.valueOf(2) : null); } -} +} \ No newline at end of file diff --git a/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/expression/literal/IntervalUtilsTests.java b/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/expression/literal/IntervalUtilsTests.java index 96ebf401c4cb6..b982f9d1d1449 100644 --- a/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/expression/literal/IntervalUtilsTests.java +++ b/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/expression/literal/IntervalUtilsTests.java @@ -8,7 +8,6 @@ import org.elasticsearch.test.ESTestCase; import org.elasticsearch.xpack.sql.parser.ParsingException; -import org.elasticsearch.xpack.sql.type.DataType; import java.time.Duration; import java.time.Period; @@ -18,41 +17,54 @@ import static java.lang.String.format; import static org.elasticsearch.xpack.sql.expression.literal.IntervalUtils.parseInterval; import static org.elasticsearch.xpack.sql.tree.Location.EMPTY; +import static org.elasticsearch.xpack.sql.type.DataType.INTERVAL_DAY; +import static org.elasticsearch.xpack.sql.type.DataType.INTERVAL_DAY_TO_HOUR; +import static org.elasticsearch.xpack.sql.type.DataType.INTERVAL_DAY_TO_MINUTE; +import static org.elasticsearch.xpack.sql.type.DataType.INTERVAL_DAY_TO_SECOND; +import static org.elasticsearch.xpack.sql.type.DataType.INTERVAL_HOUR; +import static org.elasticsearch.xpack.sql.type.DataType.INTERVAL_HOUR_TO_MINUTE; +import static org.elasticsearch.xpack.sql.type.DataType.INTERVAL_HOUR_TO_SECOND; +import static org.elasticsearch.xpack.sql.type.DataType.INTERVAL_MINUTE; +import static org.elasticsearch.xpack.sql.type.DataType.INTERVAL_MINUTE_TO_SECOND; +import static org.elasticsearch.xpack.sql.type.DataType.INTERVAL_MONTH; +import static org.elasticsearch.xpack.sql.type.DataType.INTERVAL_SECOND; +import static org.elasticsearch.xpack.sql.type.DataType.INTERVAL_YEAR; +import static org.elasticsearch.xpack.sql.type.DataType.INTERVAL_YEAR_TO_MONTH; public class IntervalUtilsTests extends ESTestCase { public void testYearInterval() throws Exception { String randomSign = randomSign(); int random = randomNonNegativeInt(); - TemporalAmount amount = parseInterval(EMPTY, randomSign + random, DataType.INTERVAL_YEAR); + TemporalAmount amount = parseInterval(EMPTY, randomSign + random, INTERVAL_YEAR); assertEquals(maybeNegate(randomSign, Period.ofYears(random)), amount); } public void testMonthInterval() throws Exception { String randomSign = randomSign(); int random = randomNonNegativeInt(); - TemporalAmount amount = parseInterval(EMPTY, randomSign + random, DataType.INTERVAL_MONTH); + TemporalAmount amount = parseInterval(EMPTY, randomSign + random, INTERVAL_MONTH); assertEquals(maybeNegate(randomSign, Period.ofMonths(random)), amount); } public void testDayInterval() throws Exception { String randomSign = randomSign(); int random = randomNonNegativeInt(); - TemporalAmount amount = parseInterval(EMPTY, randomSign + random, DataType.INTERVAL_DAY); + TemporalAmount amount = parseInterval(EMPTY, randomSign + random, INTERVAL_DAY); assertEquals(maybeNegate(randomSign, Duration.ofDays(random)), amount); } public void testHourInterval() throws Exception { String randomSign = randomSign(); int random = randomNonNegativeInt(); - TemporalAmount amount = parseInterval(EMPTY, randomSign + random, DataType.INTERVAL_HOUR); + TemporalAmount amount = parseInterval(EMPTY, randomSign + random, INTERVAL_HOUR); assertEquals(maybeNegate(randomSign, Duration.ofHours(random)), amount); } public void testMinuteInterval() throws Exception { String randomSign = randomSign(); int random = randomNonNegativeInt(); - TemporalAmount amount = parseInterval(EMPTY, randomSign + random, DataType.INTERVAL_MINUTE); + TemporalAmount amount = parseInterval(EMPTY, randomSign + random, INTERVAL_MINUTE); assertEquals(maybeNegate(randomSign, Duration.ofMinutes(random)), amount); } @@ -61,7 +73,7 @@ public void testSecondInterval() throws Exception { int randomSeconds = randomNonNegativeInt(); int randomMillis = randomInt(999999999); String value = format(Locale.ROOT, "%s%d.%d", randomSign, randomSeconds, randomMillis); - TemporalAmount amount = parseInterval(EMPTY, value, DataType.INTERVAL_SECOND); + TemporalAmount amount = parseInterval(EMPTY, value, INTERVAL_SECOND); assertEquals(maybeNegate(randomSign, Duration.ofSeconds(randomSeconds).plusMillis(randomMillis)), amount); } @@ -70,7 +82,7 @@ public void testYearToMonth() throws Exception { int randomYear = randomNonNegativeInt(); int randomMonth = randomInt(11); String value = format(Locale.ROOT, "%s%d-%d", randomSign, randomYear, randomMonth); - TemporalAmount amount = parseInterval(EMPTY, value, DataType.INTERVAL_YEAR_TO_MONTH); + TemporalAmount amount = parseInterval(EMPTY, value, INTERVAL_YEAR_TO_MONTH); assertEquals(maybeNegate(randomSign, Period.ofYears(randomYear).plusMonths(randomMonth)), amount); } @@ -79,7 +91,7 @@ public void testDayToHour() throws Exception { int randomDay = randomNonNegativeInt(); int randomHour = randomInt(23); String value = format(Locale.ROOT, "%s%d %d", randomSign, randomDay, randomHour); - TemporalAmount amount = parseInterval(EMPTY, value, DataType.INTERVAL_DAY_TO_HOUR); + TemporalAmount amount = parseInterval(EMPTY, value, INTERVAL_DAY_TO_HOUR); assertEquals(maybeNegate(randomSign, Duration.ofDays(randomDay).plusHours(randomHour)), amount); } @@ -89,7 +101,7 @@ public void testDayToMinute() throws Exception { int randomHour = randomInt(23); int randomMinute = randomInt(59); String value = format(Locale.ROOT, "%s%d %d:%d", randomSign, randomDay, randomHour, randomMinute); - TemporalAmount amount = parseInterval(EMPTY, value, DataType.INTERVAL_DAY_TO_MINUTE); + TemporalAmount amount = parseInterval(EMPTY, value, INTERVAL_DAY_TO_MINUTE); assertEquals(maybeNegate(randomSign, Duration.ofDays(randomDay).plusHours(randomHour).plusMinutes(randomMinute)), amount); } @@ -103,7 +115,7 @@ public void testDayToSecond() throws Exception { String value = format(Locale.ROOT, "%s%d %d:%d:%d.%d", randomSign, randomDay, randomHour, randomMinute, randomSecond, randomMilli); - TemporalAmount amount = parseInterval(EMPTY, value, DataType.INTERVAL_DAY_TO_SECOND); + TemporalAmount amount = parseInterval(EMPTY, value, INTERVAL_DAY_TO_SECOND); assertEquals(maybeNegate(randomSign, Duration.ofDays(randomDay).plusHours(randomHour).plusMinutes(randomMinute) .plusSeconds(randomSecond).plusMillis(randomMilli)), amount); } @@ -113,7 +125,7 @@ public void testHourToMinute() throws Exception { int randomHour = randomNonNegativeInt(); int randomMinute = randomInt(59); String value = format(Locale.ROOT, "%s%d:%d", randomSign, randomHour, randomMinute); - TemporalAmount amount = parseInterval(EMPTY, value, DataType.INTERVAL_HOUR_TO_MINUTE); + TemporalAmount amount = parseInterval(EMPTY, value, INTERVAL_HOUR_TO_MINUTE); assertEquals(maybeNegate(randomSign, Duration.ofHours(randomHour).plusMinutes(randomMinute)), amount); } @@ -125,7 +137,7 @@ public void testHourToSecond() throws Exception { int randomMilli = randomInt(999999999); String value = format(Locale.ROOT, "%s%d:%d:%d.%d", randomSign, randomHour, randomMinute, randomSecond, randomMilli); - TemporalAmount amount = parseInterval(EMPTY, value, DataType.INTERVAL_HOUR_TO_SECOND); + TemporalAmount amount = parseInterval(EMPTY, value, INTERVAL_HOUR_TO_SECOND); assertEquals(maybeNegate(randomSign, Duration.ofHours(randomHour).plusMinutes(randomMinute).plusSeconds(randomSecond).plusMillis(randomMilli)), amount); } @@ -137,7 +149,7 @@ public void testMinuteToSecond() throws Exception { int randomMilli = randomInt(999999999); String value = format(Locale.ROOT, "%s%d:%d.%d", randomSign, randomMinute, randomSecond, randomMilli); - TemporalAmount amount = parseInterval(EMPTY, value, DataType.INTERVAL_MINUTE_TO_SECOND); + TemporalAmount amount = parseInterval(EMPTY, value, INTERVAL_MINUTE_TO_SECOND); assertEquals(maybeNegate(randomSign, Duration.ofMinutes(randomMinute).plusSeconds(randomSecond).plusMillis(randomMilli)), amount); } @@ -149,7 +161,7 @@ public void testYearToMonthTooBig() throws Exception { int randomTooBig = randomIntBetween(12, 9999); String value = format(Locale.ROOT, "%s%d-%d", randomSign, randomYear, randomTooBig); ParsingException pe = expectThrows(ParsingException.class, - () -> parseInterval(EMPTY, value, DataType.INTERVAL_YEAR_TO_MONTH)); + () -> parseInterval(EMPTY, value, INTERVAL_YEAR_TO_MONTH)); assertEquals("line -1:0: Invalid [INTERVAL YEAR TO MONTH] value [" + value + "]: [MONTH] unit has illegal value [" + randomTooBig + "], expected a positive number up to [11]", pe.getMessage()); } @@ -160,7 +172,7 @@ public void testMillisTooBig() throws Exception { int millisTooLarge = 1234567890; String value = format(Locale.ROOT, "%s%d.%d", randomSign, randomSeconds, millisTooLarge); ParsingException pe = expectThrows(ParsingException.class, - () -> parseInterval(EMPTY, value, DataType.INTERVAL_SECOND)); + () -> parseInterval(EMPTY, value, INTERVAL_SECOND)); assertEquals("line -1:0: Invalid [INTERVAL SECOND] value [" + value + "]: [MILLISECOND] unit has illegal value [" + millisTooLarge + "], expected a positive number up to [999999999]", pe.getMessage()); } @@ -171,27 +183,27 @@ public void testDayToMinuteTooBig() throws Exception { int randomHour = randomIntBetween(24, 9999); int randomMinute = randomInt(59); String value = format(Locale.ROOT, "%s%d %d:%d", randomSign, randomDay, randomHour, randomMinute); - ParsingException pe = expectThrows(ParsingException.class, () -> parseInterval(EMPTY, value, DataType.INTERVAL_DAY_TO_MINUTE)); + ParsingException pe = expectThrows(ParsingException.class, () -> parseInterval(EMPTY, value, INTERVAL_DAY_TO_MINUTE)); assertEquals("line -1:0: Invalid [INTERVAL DAY TO MINUTE] value [" + value + "]: [HOUR] unit has illegal value [" + randomHour + "], expected a positive number up to [23]", pe.getMessage()); } public void testExtraCharLeading() throws Exception { String value = "a123"; - ParsingException pe = expectThrows(ParsingException.class, () -> parseInterval(EMPTY, value, DataType.INTERVAL_YEAR)); + ParsingException pe = expectThrows(ParsingException.class, () -> parseInterval(EMPTY, value, INTERVAL_YEAR)); assertEquals("line -1:0: Invalid [INTERVAL YEAR] value [a123]: expected digit (at [0]) but found [a]", pe.getMessage()); } public void testExtraCharTrailing() throws Exception { String value = "123x"; ParsingException pe = expectThrows(ParsingException.class, - () -> parseInterval(EMPTY, value, DataType.INTERVAL_YEAR)); + () -> parseInterval(EMPTY, value, INTERVAL_YEAR)); assertEquals("line -1:0: Invalid [INTERVAL YEAR] value [123x]: unexpected trailing characters found [x]", pe.getMessage()); } public void testIncorrectSeparator() throws Exception { String value = "123^456"; - ParsingException pe = expectThrows(ParsingException.class, () -> parseInterval(EMPTY, value, DataType.INTERVAL_SECOND)); + ParsingException pe = expectThrows(ParsingException.class, () -> parseInterval(EMPTY, value, INTERVAL_SECOND)); assertEquals("line -1:0: Invalid [INTERVAL SECOND] value [123^456]: expected [.] (at [3]) but found [^]", pe.getMessage()); } diff --git a/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/type/DataTypeConversionTests.java b/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/type/DataTypeConversionTests.java index 5b959c6130038..abe6e128d98cc 100644 --- a/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/type/DataTypeConversionTests.java +++ b/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/type/DataTypeConversionTests.java @@ -13,14 +13,40 @@ import org.joda.time.DateTimeZone; import static org.elasticsearch.xpack.sql.tree.Location.EMPTY; +import static org.elasticsearch.xpack.sql.type.DataType.BOOLEAN; +import static org.elasticsearch.xpack.sql.type.DataType.BYTE; +import static org.elasticsearch.xpack.sql.type.DataType.DATE; +import static org.elasticsearch.xpack.sql.type.DataType.DOUBLE; +import static org.elasticsearch.xpack.sql.type.DataType.FLOAT; +import static org.elasticsearch.xpack.sql.type.DataType.HALF_FLOAT; +import static org.elasticsearch.xpack.sql.type.DataType.INTEGER; +import static org.elasticsearch.xpack.sql.type.DataType.INTERVAL_DAY; +import static org.elasticsearch.xpack.sql.type.DataType.INTERVAL_DAY_TO_MINUTE; +import static org.elasticsearch.xpack.sql.type.DataType.INTERVAL_HOUR_TO_SECOND; +import static org.elasticsearch.xpack.sql.type.DataType.INTERVAL_MINUTE; +import static org.elasticsearch.xpack.sql.type.DataType.INTERVAL_MINUTE_TO_SECOND; +import static org.elasticsearch.xpack.sql.type.DataType.INTERVAL_MONTH; +import static org.elasticsearch.xpack.sql.type.DataType.INTERVAL_YEAR; +import static org.elasticsearch.xpack.sql.type.DataType.INTERVAL_YEAR_TO_MONTH; +import static org.elasticsearch.xpack.sql.type.DataType.IP; +import static org.elasticsearch.xpack.sql.type.DataType.KEYWORD; +import static org.elasticsearch.xpack.sql.type.DataType.LONG; +import static org.elasticsearch.xpack.sql.type.DataType.NULL; +import static org.elasticsearch.xpack.sql.type.DataType.SHORT; +import static org.elasticsearch.xpack.sql.type.DataType.TEXT; +import static org.elasticsearch.xpack.sql.type.DataType.UNSUPPORTED; +import static org.elasticsearch.xpack.sql.type.DataType.fromTypeName; +import static org.elasticsearch.xpack.sql.type.DataType.values; +import static org.elasticsearch.xpack.sql.type.DataTypeConversion.commonType; +import static org.elasticsearch.xpack.sql.type.DataTypeConversion.conversionFor; public class DataTypeConversionTests extends ESTestCase { public void testConversionToString() { - Conversion conversion = DataTypeConversion.conversionFor(DataType.DOUBLE, DataType.KEYWORD); + Conversion conversion = conversionFor(DOUBLE, KEYWORD); assertNull(conversion.convert(null)); assertEquals("10.0", conversion.convert(10.0)); - conversion = DataTypeConversion.conversionFor(DataType.DATE, DataType.KEYWORD); + conversion = conversionFor(DATE, KEYWORD); assertNull(conversion.convert(null)); assertEquals("1970-01-01T00:00:00.000Z", conversion.convert(new DateTime(0, DateTimeZone.UTC))); } @@ -29,9 +55,9 @@ public void testConversionToString() { * Test conversion to long. */ public void testConversionToLong() { - DataType to = DataType.LONG; + DataType to = LONG; { - Conversion conversion = DataTypeConversion.conversionFor(DataType.DOUBLE, to); + Conversion conversion = conversionFor(DOUBLE, to); assertNull(conversion.convert(null)); assertEquals(10L, conversion.convert(10.0)); assertEquals(10L, conversion.convert(10.1)); @@ -40,18 +66,18 @@ public void testConversionToLong() { assertEquals("[" + Double.MAX_VALUE + "] out of [Long] range", e.getMessage()); } { - Conversion conversion = DataTypeConversion.conversionFor(DataType.INTEGER, to); + Conversion conversion = conversionFor(INTEGER, to); assertNull(conversion.convert(null)); assertEquals(10L, conversion.convert(10)); assertEquals(-134L, conversion.convert(-134)); } { - Conversion conversion = DataTypeConversion.conversionFor(DataType.BOOLEAN, to); + Conversion conversion = conversionFor(BOOLEAN, to); assertNull(conversion.convert(null)); assertEquals(1L, conversion.convert(true)); assertEquals(0L, conversion.convert(false)); } - Conversion conversion = DataTypeConversion.conversionFor(DataType.KEYWORD, to); + Conversion conversion = conversionFor(KEYWORD, to); assertNull(conversion.convert(null)); assertEquals(1L, conversion.convert("1")); assertEquals(0L, conversion.convert("-0")); @@ -60,9 +86,9 @@ public void testConversionToLong() { } public void testConversionToDate() { - DataType to = DataType.DATE; + DataType to = DATE; { - Conversion conversion = DataTypeConversion.conversionFor(DataType.DOUBLE, to); + Conversion conversion = conversionFor(DOUBLE, to); assertNull(conversion.convert(null)); assertEquals(new DateTime(10L, DateTimeZone.UTC), conversion.convert(10.0)); assertEquals(new DateTime(10L, DateTimeZone.UTC), conversion.convert(10.1)); @@ -71,18 +97,18 @@ public void testConversionToDate() { assertEquals("[" + Double.MAX_VALUE + "] out of [Long] range", e.getMessage()); } { - Conversion conversion = DataTypeConversion.conversionFor(DataType.INTEGER, to); + Conversion conversion = conversionFor(INTEGER, to); assertNull(conversion.convert(null)); assertEquals(new DateTime(10L, DateTimeZone.UTC), conversion.convert(10)); assertEquals(new DateTime(-134L, DateTimeZone.UTC), conversion.convert(-134)); } { - Conversion conversion = DataTypeConversion.conversionFor(DataType.BOOLEAN, to); + Conversion conversion = conversionFor(BOOLEAN, to); assertNull(conversion.convert(null)); assertEquals(new DateTime(1, DateTimeZone.UTC), conversion.convert(true)); assertEquals(new DateTime(0, DateTimeZone.UTC), conversion.convert(false)); } - Conversion conversion = DataTypeConversion.conversionFor(DataType.KEYWORD, to); + Conversion conversion = conversionFor(KEYWORD, to); assertNull(conversion.convert(null)); assertEquals(new DateTime(1000L, DateTimeZone.UTC), conversion.convert("1970-01-01T00:00:01Z")); @@ -91,8 +117,8 @@ public void testConversionToDate() { // double check back and forth conversion DateTime dt = DateTime.now(DateTimeZone.UTC); - Conversion forward = DataTypeConversion.conversionFor(DataType.DATE, DataType.KEYWORD); - Conversion back = DataTypeConversion.conversionFor(DataType.KEYWORD, DataType.DATE); + Conversion forward = conversionFor(DATE, KEYWORD); + Conversion back = conversionFor(KEYWORD, DATE); assertEquals(dt, back.convert(forward.convert(dt))); Exception e = expectThrows(SqlIllegalArgumentException.class, () -> conversion.convert("0xff")); assertEquals("cannot cast [0xff] to [Date]:Invalid format: \"0xff\" is malformed at \"xff\"", e.getMessage()); @@ -100,26 +126,26 @@ public void testConversionToDate() { public void testConversionToDouble() { { - Conversion conversion = DataTypeConversion.conversionFor(DataType.FLOAT, DataType.DOUBLE); + Conversion conversion = conversionFor(FLOAT, DOUBLE); assertNull(conversion.convert(null)); assertEquals(10.0, (double) conversion.convert(10.0f), 0.00001); assertEquals(10.1, (double) conversion.convert(10.1f), 0.00001); assertEquals(10.6, (double) conversion.convert(10.6f), 0.00001); } { - Conversion conversion = DataTypeConversion.conversionFor(DataType.INTEGER, DataType.DOUBLE); + Conversion conversion = conversionFor(INTEGER, DOUBLE); assertNull(conversion.convert(null)); assertEquals(10.0, (double) conversion.convert(10), 0.00001); assertEquals(-134.0, (double) conversion.convert(-134), 0.00001); } { - Conversion conversion = DataTypeConversion.conversionFor(DataType.BOOLEAN, DataType.DOUBLE); + Conversion conversion = conversionFor(BOOLEAN, DOUBLE); assertNull(conversion.convert(null)); assertEquals(1.0, (double) conversion.convert(true), 0); assertEquals(0.0, (double) conversion.convert(false), 0); } { - Conversion conversion = DataTypeConversion.conversionFor(DataType.KEYWORD, DataType.DOUBLE); + Conversion conversion = conversionFor(KEYWORD, DOUBLE); assertNull(conversion.convert(null)); assertEquals(1.0, (double) conversion.convert("1"), 0); assertEquals(0.0, (double) conversion.convert("-0"), 0); @@ -131,35 +157,35 @@ public void testConversionToDouble() { public void testConversionToBoolean() { { - Conversion conversion = DataTypeConversion.conversionFor(DataType.FLOAT, DataType.BOOLEAN); + Conversion conversion = conversionFor(FLOAT, BOOLEAN); assertNull(conversion.convert(null)); assertEquals(true, conversion.convert(10.0f)); assertEquals(true, conversion.convert(-10.0f)); assertEquals(false, conversion.convert(0.0f)); } { - Conversion conversion = DataTypeConversion.conversionFor(DataType.INTEGER, DataType.BOOLEAN); + Conversion conversion = conversionFor(INTEGER, BOOLEAN); assertNull(conversion.convert(null)); assertEquals(true, conversion.convert(10)); assertEquals(true, conversion.convert(-10)); assertEquals(false, conversion.convert(0)); } { - Conversion conversion = DataTypeConversion.conversionFor(DataType.LONG, DataType.BOOLEAN); + Conversion conversion = conversionFor(LONG, BOOLEAN); assertNull(conversion.convert(null)); assertEquals(true, conversion.convert(10L)); assertEquals(true, conversion.convert(-10L)); assertEquals(false, conversion.convert(0L)); } { - Conversion conversion = DataTypeConversion.conversionFor(DataType.DOUBLE, DataType.BOOLEAN); + Conversion conversion = conversionFor(DOUBLE, BOOLEAN); assertNull(conversion.convert(null)); assertEquals(true, conversion.convert(10.0d)); assertEquals(true, conversion.convert(-10.0d)); assertEquals(false, conversion.convert(0.0d)); } { - Conversion conversion = DataTypeConversion.conversionFor(DataType.KEYWORD, DataType.BOOLEAN); + Conversion conversion = conversionFor(KEYWORD, BOOLEAN); assertNull(conversion.convert(null)); // We only handled upper and lower case true and false assertEquals(true, conversion.convert("true")); @@ -184,7 +210,7 @@ public void testConversionToBoolean() { public void testConversionToInt() { { - Conversion conversion = DataTypeConversion.conversionFor(DataType.DOUBLE, DataType.INTEGER); + Conversion conversion = conversionFor(DOUBLE, INTEGER); assertNull(conversion.convert(null)); assertEquals(10, conversion.convert(10.0)); assertEquals(10, conversion.convert(10.1)); @@ -196,7 +222,7 @@ public void testConversionToInt() { public void testConversionToShort() { { - Conversion conversion = DataTypeConversion.conversionFor(DataType.DOUBLE, DataType.SHORT); + Conversion conversion = conversionFor(DOUBLE, SHORT); assertNull(conversion.convert(null)); assertEquals((short) 10, conversion.convert(10.0)); assertEquals((short) 10, conversion.convert(10.1)); @@ -208,7 +234,7 @@ public void testConversionToShort() { public void testConversionToByte() { { - Conversion conversion = DataTypeConversion.conversionFor(DataType.DOUBLE, DataType.BYTE); + Conversion conversion = conversionFor(DOUBLE, BYTE); assertNull(conversion.convert(null)); assertEquals((byte) 10, conversion.convert(10.0)); assertEquals((byte) 10, conversion.convert(10.1)); @@ -219,51 +245,67 @@ public void testConversionToByte() { } public void testConversionToNull() { - Conversion conversion = DataTypeConversion.conversionFor(DataType.DOUBLE, DataType.NULL); + Conversion conversion = conversionFor(DOUBLE, NULL); assertNull(conversion.convert(null)); assertNull(conversion.convert(10.0)); } public void testConversionFromNull() { - Conversion conversion = DataTypeConversion.conversionFor(DataType.NULL, DataType.INTEGER); + Conversion conversion = conversionFor(NULL, INTEGER); assertNull(conversion.convert(null)); assertNull(conversion.convert(10)); } public void testConversionToIdentity() { - Conversion conversion = DataTypeConversion.conversionFor(DataType.INTEGER, DataType.INTEGER); + Conversion conversion = conversionFor(INTEGER, INTEGER); assertNull(conversion.convert(null)); assertEquals(10, conversion.convert(10)); } public void testCommonType() { - assertEquals(DataType.BOOLEAN, DataTypeConversion.commonType(DataType.BOOLEAN, DataType.NULL)); - assertEquals(DataType.BOOLEAN, DataTypeConversion.commonType(DataType.NULL, DataType.BOOLEAN)); - assertEquals(DataType.BOOLEAN, DataTypeConversion.commonType(DataType.BOOLEAN, DataType.BOOLEAN)); - assertEquals(DataType.NULL, DataTypeConversion.commonType(DataType.NULL, DataType.NULL)); - assertEquals(DataType.INTEGER, DataTypeConversion.commonType(DataType.INTEGER, DataType.KEYWORD)); - assertEquals(DataType.LONG, DataTypeConversion.commonType(DataType.TEXT, DataType.LONG)); - assertEquals(null, DataTypeConversion.commonType(DataType.TEXT, DataType.KEYWORD)); - assertEquals(DataType.SHORT, DataTypeConversion.commonType(DataType.SHORT, DataType.BYTE)); - assertEquals(DataType.FLOAT, DataTypeConversion.commonType(DataType.BYTE, DataType.FLOAT)); - assertEquals(DataType.FLOAT, DataTypeConversion.commonType(DataType.FLOAT, DataType.INTEGER)); - assertEquals(DataType.DOUBLE, DataTypeConversion.commonType(DataType.DOUBLE, DataType.FLOAT)); + assertEquals(BOOLEAN, commonType(BOOLEAN, NULL)); + assertEquals(BOOLEAN, commonType(NULL, BOOLEAN)); + assertEquals(BOOLEAN, commonType(BOOLEAN, BOOLEAN)); + assertEquals(NULL, commonType(NULL, NULL)); + assertEquals(INTEGER, commonType(INTEGER, KEYWORD)); + assertEquals(LONG, commonType(TEXT, LONG)); + assertEquals(null, commonType(TEXT, KEYWORD)); + assertEquals(SHORT, commonType(SHORT, BYTE)); + assertEquals(FLOAT, commonType(BYTE, FLOAT)); + assertEquals(FLOAT, commonType(FLOAT, INTEGER)); + assertEquals(DOUBLE, commonType(DOUBLE, FLOAT)); + } + + public void testCommonTypeIntervals() { + assertEquals(INTERVAL_DAY_TO_MINUTE, commonType(INTERVAL_DAY, INTERVAL_MINUTE)); + + assertEquals(DATE, commonType(INTERVAL_YEAR_TO_MONTH, DATE)); + assertEquals(DATE, commonType(DATE, INTERVAL_MONTH)); + assertEquals(DATE, commonType(INTERVAL_DAY, DATE)); + assertEquals(DATE, commonType(DATE, INTERVAL_HOUR_TO_SECOND)); + assertEquals(null, commonType(INTERVAL_YEAR, INTERVAL_DAY)); + + assertEquals(INTERVAL_YEAR, commonType(INTERVAL_YEAR, LONG)); + assertEquals(INTERVAL_MINUTE, commonType(INTEGER, INTERVAL_MINUTE)); + assertEquals(null, commonType(SHORT, INTERVAL_DAY_TO_MINUTE)); + assertEquals(null, commonType(INTERVAL_MONTH, DOUBLE)); + assertEquals(null, commonType(HALF_FLOAT, INTERVAL_MINUTE_TO_SECOND)); } public void testEsDataTypes() { - for (DataType type : DataType.values()) { - assertEquals(type, DataType.fromTypeName(type.esType)); + for (DataType type : values()) { + assertEquals(type, fromTypeName(type.esType)); } } public void testConversionToUnsupported() { Exception e = expectThrows(SqlIllegalArgumentException.class, - () -> DataTypeConversion.conversionFor(DataType.INTEGER, DataType.UNSUPPORTED)); + () -> conversionFor(INTEGER, UNSUPPORTED)); assertEquals("cannot convert from [INTEGER] to [UNSUPPORTED]", e.getMessage()); } public void testStringToIp() { - Conversion conversion = DataTypeConversion.conversionFor(DataType.KEYWORD, DataType.IP); + Conversion conversion = conversionFor(KEYWORD, IP); assertNull(conversion.convert(null)); assertEquals("192.168.1.1", conversion.convert("192.168.1.1")); Exception e = expectThrows(SqlIllegalArgumentException.class, () -> conversion.convert("10.1.1.300")); @@ -271,9 +313,9 @@ public void testStringToIp() { } public void testIpToString() { - Conversion ipToString = DataTypeConversion.conversionFor(DataType.IP, DataType.KEYWORD); - assertEquals("10.0.0.1", ipToString.convert(new Literal(EMPTY, "10.0.0.1", DataType.IP))); - Conversion stringToIp = DataTypeConversion.conversionFor(DataType.KEYWORD, DataType.IP); + Conversion ipToString = conversionFor(IP, KEYWORD); + assertEquals("10.0.0.1", ipToString.convert(new Literal(EMPTY, "10.0.0.1", IP))); + Conversion stringToIp = conversionFor(KEYWORD, IP); assertEquals("10.0.0.1", ipToString.convert(stringToIp.convert(Literal.of(EMPTY, "10.0.0.1")))); } -} +} \ No newline at end of file diff --git a/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/type/DataTypesTests.java b/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/type/DataTypesTests.java index 0d61edf860ce7..a082f025b0c6a 100644 --- a/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/type/DataTypesTests.java +++ b/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/type/DataTypesTests.java @@ -7,10 +7,28 @@ import org.elasticsearch.test.ESTestCase; +import java.util.EnumSet; + import static org.elasticsearch.xpack.sql.type.DataType.DATE; import static org.elasticsearch.xpack.sql.type.DataType.FLOAT; +import static org.elasticsearch.xpack.sql.type.DataType.INTERVAL_DAY; +import static org.elasticsearch.xpack.sql.type.DataType.INTERVAL_DAY_TO_HOUR; +import static org.elasticsearch.xpack.sql.type.DataType.INTERVAL_DAY_TO_MINUTE; +import static org.elasticsearch.xpack.sql.type.DataType.INTERVAL_DAY_TO_SECOND; +import static org.elasticsearch.xpack.sql.type.DataType.INTERVAL_HOUR; +import static org.elasticsearch.xpack.sql.type.DataType.INTERVAL_HOUR_TO_MINUTE; +import static org.elasticsearch.xpack.sql.type.DataType.INTERVAL_HOUR_TO_SECOND; +import static org.elasticsearch.xpack.sql.type.DataType.INTERVAL_MINUTE; +import static org.elasticsearch.xpack.sql.type.DataType.INTERVAL_MINUTE_TO_SECOND; +import static org.elasticsearch.xpack.sql.type.DataType.INTERVAL_MONTH; +import static org.elasticsearch.xpack.sql.type.DataType.INTERVAL_SECOND; +import static org.elasticsearch.xpack.sql.type.DataType.INTERVAL_YEAR; +import static org.elasticsearch.xpack.sql.type.DataType.INTERVAL_YEAR_TO_MONTH; import static org.elasticsearch.xpack.sql.type.DataType.KEYWORD; import static org.elasticsearch.xpack.sql.type.DataType.LONG; +import static org.elasticsearch.xpack.sql.type.DataTypes.compatibleInterval; +import static org.elasticsearch.xpack.sql.type.DataTypes.isInterval; +import static org.elasticsearch.xpack.sql.type.DataTypes.isSingularInterval; import static org.elasticsearch.xpack.sql.type.DataTypes.metaSqlDataType; import static org.elasticsearch.xpack.sql.type.DataTypes.metaSqlDateTimeSub; import static org.elasticsearch.xpack.sql.type.DataTypes.metaSqlMaximumScale; @@ -51,8 +69,45 @@ public void testMetaRadix() { assertEquals(Integer.valueOf(2), metaSqlRadix(FLOAT)); } + + // type checks + public void testIsInterval() throws Exception { + assertTrue(isInterval(randomFrom(EnumSet.range(INTERVAL_YEAR, INTERVAL_MINUTE_TO_SECOND)))); + } + + public void testIsSingularInterval() throws Exception { + assertTrue( + isSingularInterval(randomFrom(EnumSet.of(INTERVAL_YEAR, INTERVAL_DAY, INTERVAL_HOUR, INTERVAL_MINUTE, INTERVAL_SECOND)))); + assertFalse(isSingularInterval(randomFrom(EnumSet.of(INTERVAL_YEAR_TO_MONTH, INTERVAL_DAY_TO_HOUR, INTERVAL_DAY_TO_MINUTE, + INTERVAL_DAY_TO_SECOND, INTERVAL_HOUR_TO_MINUTE, INTERVAL_HOUR_TO_SECOND, INTERVAL_MINUTE_TO_SECOND)))); + } + + public void testIntervalCompatibilityYearMonth() throws Exception { + assertEquals(INTERVAL_YEAR_TO_MONTH, compatibleInterval(INTERVAL_YEAR, INTERVAL_MONTH)); + assertEquals(INTERVAL_YEAR_TO_MONTH, compatibleInterval(INTERVAL_YEAR, INTERVAL_YEAR_TO_MONTH)); + assertEquals(INTERVAL_YEAR_TO_MONTH, compatibleInterval(INTERVAL_MONTH, INTERVAL_YEAR)); + assertEquals(INTERVAL_YEAR_TO_MONTH, compatibleInterval(INTERVAL_MONTH, INTERVAL_YEAR_TO_MONTH)); + } + + public void testIntervalCompatibilityDayTime() throws Exception { + assertEquals(INTERVAL_DAY_TO_HOUR, compatibleInterval(INTERVAL_DAY, INTERVAL_HOUR)); + assertEquals(INTERVAL_DAY_TO_HOUR, compatibleInterval(INTERVAL_DAY_TO_HOUR, INTERVAL_HOUR)); + assertEquals(INTERVAL_DAY_TO_MINUTE, compatibleInterval(INTERVAL_DAY, INTERVAL_MINUTE)); + assertEquals(INTERVAL_DAY_TO_MINUTE, compatibleInterval(INTERVAL_DAY_TO_HOUR, INTERVAL_HOUR_TO_MINUTE)); + assertEquals(INTERVAL_DAY_TO_MINUTE, compatibleInterval(INTERVAL_MINUTE, INTERVAL_DAY_TO_HOUR)); + assertEquals(INTERVAL_DAY_TO_MINUTE, compatibleInterval(INTERVAL_DAY, INTERVAL_DAY_TO_MINUTE)); + assertEquals(INTERVAL_DAY_TO_SECOND, compatibleInterval(INTERVAL_DAY, INTERVAL_SECOND)); + assertEquals(INTERVAL_DAY_TO_SECOND, compatibleInterval(INTERVAL_MINUTE, INTERVAL_DAY_TO_SECOND)); + + assertEquals(INTERVAL_HOUR_TO_MINUTE, compatibleInterval(INTERVAL_MINUTE, INTERVAL_HOUR)); + assertEquals(INTERVAL_HOUR_TO_SECOND, compatibleInterval(INTERVAL_SECOND, INTERVAL_HOUR)); + assertEquals(INTERVAL_HOUR_TO_SECOND, compatibleInterval(INTERVAL_SECOND, INTERVAL_HOUR_TO_MINUTE)); + assertEquals(INTERVAL_HOUR_TO_SECOND, compatibleInterval(INTERVAL_SECOND, INTERVAL_HOUR_TO_MINUTE)); + + assertEquals(INTERVAL_MINUTE_TO_SECOND, compatibleInterval(INTERVAL_SECOND, INTERVAL_MINUTE)); + } + private DataType randomDataTypeNoDate() { return randomValueOtherThan(DataType.DATE, () -> randomFrom(DataType.values())); } -} - +} \ No newline at end of file From d1d99ee13c1291462dcfa372f07cc642eafced5c Mon Sep 17 00:00:00 2001 From: Costin Leau Date: Thu, 15 Nov 2018 12:07:11 +0200 Subject: [PATCH 3/6] Polishing --- .../sql/jdbc/jdbc/JdbcDatabaseMetaData.java | 23 ++++++++++--------- .../sql/jdbc/net/client/JdbcHttpClient.java | 2 +- .../jdbc/net/protocol/ColumnInfoTests.java | 10 ++++---- .../xpack/sql/qa/jdbc/DataLoader.java | 2 +- .../sql/expression/literal/IntervalUtils.java | 6 ++--- .../literal/IntervalUtilsTests.java | 13 +++++++++++ 6 files changed, 36 insertions(+), 20 deletions(-) diff --git a/x-pack/plugin/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/jdbc/JdbcDatabaseMetaData.java b/x-pack/plugin/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/jdbc/JdbcDatabaseMetaData.java index 5a044bb35a6af..e37411af965c8 100644 --- a/x-pack/plugin/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/jdbc/JdbcDatabaseMetaData.java +++ b/x-pack/plugin/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/jdbc/JdbcDatabaseMetaData.java @@ -25,6 +25,7 @@ import static java.sql.JDBCType.INTEGER; import static java.sql.JDBCType.SMALLINT; +import static org.elasticsearch.xpack.sql.client.StringUtils.EMPTY; /** * Implementation of {@link DatabaseMetaData} for Elasticsearch. Draws inspiration @@ -175,7 +176,7 @@ public String getIdentifierQuoteString() throws SQLException { @Override public String getSQLKeywords() throws SQLException { // TODO: sync this with the grammar - return ""; + return EMPTY; } @Override @@ -212,7 +213,7 @@ public String getStringFunctions() throws SQLException { @Override public String getSystemFunctions() throws SQLException { // TODO: sync this with the grammar - return ""; + return EMPTY; } @Override @@ -235,7 +236,7 @@ public String getSearchStringEscape() throws SQLException { @Override public String getExtraNameCharacters() throws SQLException { - return ""; + return EMPTY; } @Override @@ -716,15 +717,15 @@ private String defaultCatalog() throws SQLException { private boolean isDefaultCatalog(String catalog) throws SQLException { // null means catalog info is irrelevant // % means return all catalogs - // "" means return those without a catalog - return catalog == null || catalog.equals("") || catalog.equals("%") || catalog.equals(defaultCatalog()); + // EMPTY means return those without a catalog + return catalog == null || catalog.equals(EMPTY) || catalog.equals("%") || catalog.equals(defaultCatalog()); } private boolean isDefaultSchema(String schema) { // null means schema info is irrelevant // % means return all schemas` - // "" means return those without a schema - return schema == null || schema.equals("") || schema.equals("%"); + // EMPTY means return those without a schema + return schema == null || schema.equals(EMPTY) || schema.equals("%"); } @Override @@ -756,7 +757,7 @@ public ResultSet getTables(String catalog, String schemaPattern, String tableNam @Override public ResultSet getSchemas() throws SQLException { - Object[][] data = { { "", defaultCatalog() } }; + Object[][] data = { { EMPTY, defaultCatalog() } }; return memorySet(con.cfg, columnInfo("SCHEMATA", "TABLE_SCHEM", "TABLE_CATALOG"), data); @@ -770,7 +771,7 @@ public ResultSet getSchemas(String catalog, String schemaPattern) throws SQLExce if (!isDefaultCatalog(catalog) || !isDefaultSchema(schemaPattern)) { return emptySet(con.cfg, info); } - Object[][] data = { { "", defaultCatalog() } }; + Object[][] data = { { EMPTY, defaultCatalog() } }; return memorySet(con.cfg, info, data); } @@ -789,7 +790,7 @@ public ResultSet getColumns(String catalog, String schemaPattern, String tableNa throws SQLException { PreparedStatement ps = con.prepareStatement("SYS COLUMNS CATALOG ? TABLE LIKE ? LIKE ?"); // TODO: until passing null works, pass an empty string - ps.setString(1, catalog != null ? catalog.trim() : ""); + ps.setString(1, catalog != null ? catalog.trim() : EMPTY); ps.setString(2, tableNamePattern != null ? tableNamePattern.trim() : "%"); ps.setString(3, columnNamePattern != null ? columnNamePattern.trim() : "%"); return ps.executeQuery(); @@ -1138,7 +1139,7 @@ private static List columnInfo(String tableName, Object... cols) } // it's not, use the default and move on } - columns.add(new JdbcColumnInfo(name, type, tableName, "INFORMATION_SCHEMA", "", "", 0)); + columns.add(new JdbcColumnInfo(name, type, tableName, "INFORMATION_SCHEMA", EMPTY, EMPTY, 0)); } else { throw new JdbcSQLException("Invalid metadata schema definition"); diff --git a/x-pack/plugin/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/net/client/JdbcHttpClient.java b/x-pack/plugin/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/net/client/JdbcHttpClient.java index 8b2d21524bc34..dfa606a8361ba 100644 --- a/x-pack/plugin/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/net/client/JdbcHttpClient.java +++ b/x-pack/plugin/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/net/client/JdbcHttpClient.java @@ -89,7 +89,7 @@ private InfoResponse fetchServerInfo() throws SQLException { /** * Converts REST column metadata into JDBC column metadata */ - private List toJdbcColumnInfo(List columns) throws SQLException { + private List toJdbcColumnInfo(List columns) throws SQLException { List cols = new ArrayList<>(columns.size()); for (ColumnInfo info : columns) { cols.add(new JdbcColumnInfo(info.name(), TypeUtils.of(info.esType()), EMPTY, EMPTY, EMPTY, EMPTY, info.displaySize())); diff --git a/x-pack/plugin/sql/jdbc/src/test/java/org/elasticsearch/xpack/sql/jdbc/net/protocol/ColumnInfoTests.java b/x-pack/plugin/sql/jdbc/src/test/java/org/elasticsearch/xpack/sql/jdbc/net/protocol/ColumnInfoTests.java index a58a4ffd3858e..8be18623ffa26 100644 --- a/x-pack/plugin/sql/jdbc/src/test/java/org/elasticsearch/xpack/sql/jdbc/net/protocol/ColumnInfoTests.java +++ b/x-pack/plugin/sql/jdbc/src/test/java/org/elasticsearch/xpack/sql/jdbc/net/protocol/ColumnInfoTests.java @@ -8,24 +8,26 @@ import org.elasticsearch.test.ESTestCase; import org.elasticsearch.xpack.sql.jdbc.type.DataType; +import static org.elasticsearch.xpack.sql.client.StringUtils.EMPTY; + public class ColumnInfoTests extends ESTestCase { static JdbcColumnInfo varcharInfo(String name) { - return new JdbcColumnInfo(name, DataType.KEYWORD, "", "", "", "", 0); + return new JdbcColumnInfo(name, DataType.KEYWORD, EMPTY, EMPTY, EMPTY, EMPTY, 0); } static JdbcColumnInfo intInfo(String name) { - return new JdbcColumnInfo(name, DataType.INTEGER, "", "", "", "", 11); + return new JdbcColumnInfo(name, DataType.INTEGER, EMPTY, EMPTY, EMPTY, EMPTY, 11); } static JdbcColumnInfo doubleInfo(String name) { - return new JdbcColumnInfo(name, DataType.DOUBLE, "", "", "", "", 25); + return new JdbcColumnInfo(name, DataType.DOUBLE, EMPTY, EMPTY, EMPTY, EMPTY, 25); } public void testToString() { assertEquals("test.doc.a", new JdbcColumnInfo("a", DataType.KEYWORD, "test.doc", "as", "ads", "lab", 0).toString()); assertEquals("test.doc.a", - new JdbcColumnInfo("a", DataType.KEYWORD, "test.doc", "", "", "", 0).toString()); + new JdbcColumnInfo("a", DataType.KEYWORD, "test.doc", EMPTY, EMPTY, EMPTY, 0).toString()); assertEquals("string", varcharInfo("string").toString()); assertEquals("int", intInfo("int").toString()); assertEquals("d", doubleInfo("d").toString()); diff --git a/x-pack/plugin/sql/qa/src/main/java/org/elasticsearch/xpack/sql/qa/jdbc/DataLoader.java b/x-pack/plugin/sql/qa/src/main/java/org/elasticsearch/xpack/sql/qa/jdbc/DataLoader.java index 3736453f94bc0..53669f9de0eb9 100644 --- a/x-pack/plugin/sql/qa/src/main/java/org/elasticsearch/xpack/sql/qa/jdbc/DataLoader.java +++ b/x-pack/plugin/sql/qa/src/main/java/org/elasticsearch/xpack/sql/qa/jdbc/DataLoader.java @@ -33,7 +33,7 @@ public class DataLoader { public static void main(String[] args) throws Exception { try (RestClient client = RestClient.builder(new HttpHost("localhost", 9200)).build()) { - //loadEmpDatasetIntoEs(client); + loadEmpDatasetIntoEs(client); loadDocsDatasetIntoEs(client); LogManager.getLogger(DataLoader.class).info("Data loaded"); } diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/literal/IntervalUtils.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/literal/IntervalUtils.java index c8604d7714615..590446230b7f3 100644 --- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/literal/IntervalUtils.java +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/literal/IntervalUtils.java @@ -63,7 +63,7 @@ public static TemporalAmount of(Location source, long duration, TimeUnit unit) { throw new ParsingException(source, "Cannot parse duration [{}]", unit); } } catch (ArithmeticException ae) { - throw new ParsingException(source, "Value [{}] cannot be used as it is too large to convert into " + unit.name() + "s"); + throw new ParsingException(source, "Value [{}] cannot be used as it is too large to convert into [{}]s", duration, unit); } } @@ -186,7 +186,7 @@ TemporalAmount parse(Location source, String string) { boolean negate = false; - // first take check if there's a sign + // first check if there's a sign char maybeSign = string.charAt(0); if (PLUS == maybeSign) { startToken = 1; @@ -195,7 +195,7 @@ TemporalAmount parse(Location source, String string) { negate = true; } - // take each token and use it consume a part of the string + // take each token and use it to consume a part of the string // validate each token and that the whole string is consumed for (Token token : tokens) { endToken = startToken; diff --git a/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/expression/literal/IntervalUtilsTests.java b/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/expression/literal/IntervalUtilsTests.java index b982f9d1d1449..eb9e2f832f53c 100644 --- a/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/expression/literal/IntervalUtilsTests.java +++ b/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/expression/literal/IntervalUtilsTests.java @@ -7,6 +7,7 @@ package org.elasticsearch.xpack.sql.expression.literal; import org.elasticsearch.test.ESTestCase; +import org.elasticsearch.xpack.sql.expression.literal.IntervalUtils.TimeUnit; import org.elasticsearch.xpack.sql.parser.ParsingException; import java.time.Duration; @@ -15,6 +16,8 @@ import java.util.Locale; import static java.lang.String.format; +import static org.elasticsearch.xpack.sql.expression.literal.IntervalUtils.intervalType; +import static org.elasticsearch.xpack.sql.expression.literal.IntervalUtils.of; import static org.elasticsearch.xpack.sql.expression.literal.IntervalUtils.parseInterval; import static org.elasticsearch.xpack.sql.tree.Location.EMPTY; import static org.elasticsearch.xpack.sql.type.DataType.INTERVAL_DAY; @@ -207,6 +210,16 @@ public void testIncorrectSeparator() throws Exception { assertEquals("line -1:0: Invalid [INTERVAL SECOND] value [123^456]: expected [.] (at [3]) but found [^]", pe.getMessage()); } + public void testOfValueTooLarge() throws Exception { + ParsingException pe = expectThrows(ParsingException.class, () -> of(EMPTY, Long.MAX_VALUE, TimeUnit.YEAR)); + assertEquals("line -1:0: Value [9223372036854775807] cannot be used as it is too large to convert into [YEAR]s", pe.getMessage()); + } + + public void testIntervalType() throws Exception { + ParsingException pe = expectThrows(ParsingException.class, () -> intervalType(EMPTY, TimeUnit.DAY, TimeUnit.YEAR)); + assertEquals("line -1:0: Cannot determine datatype for combination [DAY] [YEAR]", pe.getMessage()); + } + private static int randomNonNegativeInt() { int random = randomInt(); return random == Integer.MIN_VALUE ? 0 : Math.abs(random); From 3d62531318add90046750c6936786661d6f90e71 Mon Sep 17 00:00:00 2001 From: Costin Leau Date: Tue, 20 Nov 2018 08:15:53 +0200 Subject: [PATCH 4/6] Add integration tests plus scripting Fix datetime formatting --- .../main/resources/datetime-interval.csv-spec | 100 ++++++++++++ .../xpack/sql/action/CliFormatter.java | 17 +- .../xpack/sql/action/SqlQueryResponse.java | 4 +- .../xpack/sql/proto/DateUtils.java | 41 ----- .../xpack/sql/proto/StringUtils.java | 43 ++++- .../xpack/sql/execution/PlanExecutor.java | 4 - .../scalar/datetime/DateTimeFunction.java | 12 +- .../whitelist/InternalSqlScriptUtils.java | 40 ++++- .../expression/gen/script/ScriptWeaver.java | 24 ++- .../sql/expression/literal/Interval.java | 10 +- .../expression/literal/IntervalDayTime.java | 36 +++++ .../expression/literal/IntervalYearMonth.java | 40 +++++ .../{IntervalUtils.java => Intervals.java} | 16 +- .../predicate/operator/arithmetic/Add.java | 2 +- .../arithmetic/ArithmeticOperation.java | 38 +---- .../operator/arithmetic/Arithmetics.java | 32 ---- .../arithmetic/BinaryArithmeticProcessor.java | 111 +++++++++++-- .../DateTimeArithmeticOperation.java | 56 +++++++ .../predicate/operator/arithmetic/Sub.java | 2 +- .../xpack/sql/parser/ExpressionBuilder.java | 34 ++-- .../xpack/sql/planner/QueryFolder.java | 2 +- .../sql/querydsl/container/GroupByRef.java | 4 - .../xpack/sql/session/Cursors.java | 4 + .../xpack/sql/type/DataType.java | 2 +- .../xpack/sql/type/DataTypes.java | 5 +- .../xpack/sql/util/DateUtils.java | 3 +- .../xpack/sql/plugin/sql_whitelist.txt | 19 ++- ...valUtilsTests.java => IntervalsTests.java} | 12 +- .../arithmetic/BinaryArithmeticTests.java | 149 ++++++++++++++++++ .../sql/type/DataTypeConversionTests.java | 112 +++++++------ 30 files changed, 727 insertions(+), 247 deletions(-) delete mode 100644 x-pack/plugin/sql/sql-proto/src/main/java/org/elasticsearch/xpack/sql/proto/DateUtils.java rename x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/literal/{IntervalUtils.java => Intervals.java} (96%) create mode 100644 x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/predicate/operator/arithmetic/DateTimeArithmeticOperation.java rename x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/expression/literal/{IntervalUtilsTests.java => IntervalsTests.java} (97%) create mode 100644 x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/expression/predicate/operator/arithmetic/BinaryArithmeticTests.java diff --git a/x-pack/plugin/sql/qa/src/main/resources/datetime-interval.csv-spec b/x-pack/plugin/sql/qa/src/main/resources/datetime-interval.csv-spec index 0a2dda3f46eda..c20529485daee 100644 --- a/x-pack/plugin/sql/qa/src/main/resources/datetime-interval.csv-spec +++ b/x-pack/plugin/sql/qa/src/main/resources/datetime-interval.csv-spec @@ -131,3 +131,103 @@ interval --------------- +0 02:43:59.163 ; + +testDatePlusInterval +SELECT MONTH(birth_date) AS m, MONTH(birth_date + INTERVAL '1-2' YEAR TO MONTH) AS f FROM test_emp GROUP BY birth_date ORDER BY birth_date ASC LIMIT 5; + + m | f +---------------+--------------- +null |null +2 |4 +4 |6 +5 |7 +6 |8 +; + +testDatePlusMixInterval +SELECT birth_date, birth_date + INTERVAL '1-2' YEAR TO MONTH AS f FROM test_emp GROUP BY birth_date ORDER BY birth_date ASC LIMIT 5; + + birth_date:ts | f:ts +--------------------+-------------------- +null |null +1952-02-27T00:00:00Z|1953-04-27T00:00:00Z +1952-04-19T00:00:00Z|1953-06-19T00:00:00Z +1952-05-15T00:00:00Z|1953-07-15T00:00:00Z +1952-06-13T00:00:00Z|1953-08-13T00:00:00Z +; + + +testDateMinusInterval +SELECT YEAR(birth_date) AS y, YEAR(birth_date - INTERVAL 1 YEAR) AS f FROM test_emp GROUP BY birth_date ORDER BY birth_date ASC LIMIT 5; + + y | f +---------------+--------------- +null |null +1952 |1951 +1952 |1951 +1952 |1951 +1952 |1951 +; + +testDatePlusMixInterval +SELECT birth_date, birth_date + INTERVAL '1-2' YEAR TO MONTH AS f FROM test_emp GROUP BY birth_date ORDER BY birth_date ASC LIMIT 5; + + birth_date:ts | f:ts +--------------------+-------------------- +null |null +1952-02-27T00:00:00Z|1953-04-27T00:00:00Z +1952-04-19T00:00:00Z|1953-06-19T00:00:00Z +1952-05-15T00:00:00Z|1953-07-15T00:00:00Z +1952-06-13T00:00:00Z|1953-08-13T00:00:00Z +; + + +testDateAndMultipleIntervals +SELECT birth_date, birth_date - INTERVAL 1 YEAR + INTERVAL '2-3' YEAR TO MONTH AS f FROM test_emp GROUP BY birth_date ORDER BY birth_date ASC LIMIT 5; + + birth_date:ts | f:ts +--------------------+-------------------- +null |null +1952-02-27T00:00:00Z|1953-05-27T00:00:00Z +1952-04-19T00:00:00Z|1953-07-19T00:00:00Z +1952-05-15T00:00:00Z|1953-08-15T00:00:00Z +1952-06-13T00:00:00Z|1953-09-13T00:00:00Z +; + + +testDatePlusIntervalWhereClause +SELECT birth_date, YEAR(birth_date + INTERVAL 1 YEAR) AS f FROM test_emp WHERE YEAR(birth_date + INTERVAL 1 YEAR) > 1 GROUP BY birth_date ORDER BY birth_date ASC LIMIT 5; + + birth_date:ts | f:i +--------------------+-------------------- +1952-02-27T00:00:00Z|1953 +1952-04-19T00:00:00Z|1953 +1952-05-15T00:00:00Z|1953 +1952-06-13T00:00:00Z|1953 +1952-07-08T00:00:00Z|1953 +; + +testDateMinusIntervalOrder +SELECT birth_date, MONTH(birth_date - INTERVAL 1 YEAR) AS f FROM test_emp GROUP BY birth_date ORDER BY MONTH(birth_date - INTERVAL 1 YEAR) ASC LIMIT 5; + + birth_date:ts | f:i +--------------------+-------------------- +null |null +1952-02-27T00:00:00Z|2 +1952-04-19T00:00:00Z|4 +1952-05-15T00:00:00Z|5 +1952-06-13T00:00:00Z|6 +; + +// see https://github.com/elastic/elasticsearch/issues/35745 +testDatePlusIntervalHavingClause-Ignore +SELECT birth_date, MAX(hire_date) - INTERVAL 1 YEAR AS f FROM test_emp GROUP BY birth_date ORDER BY birth_date ASC LIMIT 5; + + birth_date:ts | f:ts +--------------------+-------------------- +1952-02-27T00:00:00Z|1953 +1952-04-19T00:00:00Z|1953 +1952-05-15T00:00:00Z|1953 +1952-06-13T00:00:00Z|1953 +1952-07-08T00:00:00Z|1953 +; \ No newline at end of file diff --git a/x-pack/plugin/sql/sql-action/src/main/java/org/elasticsearch/xpack/sql/action/CliFormatter.java b/x-pack/plugin/sql/sql-action/src/main/java/org/elasticsearch/xpack/sql/action/CliFormatter.java index 8d9aae9447d17..f3f63fd18a275 100644 --- a/x-pack/plugin/sql/sql-action/src/main/java/org/elasticsearch/xpack/sql/action/CliFormatter.java +++ b/x-pack/plugin/sql/sql-action/src/main/java/org/elasticsearch/xpack/sql/action/CliFormatter.java @@ -9,13 +9,11 @@ import org.elasticsearch.common.io.stream.StreamOutput; import org.elasticsearch.common.io.stream.Writeable; import org.elasticsearch.xpack.sql.proto.ColumnInfo; +import org.elasticsearch.xpack.sql.proto.StringUtils; -import org.elasticsearch.xpack.sql.proto.DateUtils; import java.io.IOException; -import java.time.ZonedDateTime; import java.util.Arrays; import java.util.List; -import java.util.Objects; /** * Formats {@link SqlQueryResponse} for the CLI. {@linkplain Writeable} so @@ -47,7 +45,7 @@ public CliFormatter(List columns, List> rows) { for (int i = 0; i < width.length; i++) { // TODO are we sure toString is correct here? What about dates that come back as longs. // Tracked by https://github.com/elastic/x-pack-elasticsearch/issues/3081 - width[i] = Math.max(width[i], toString(row.get(i)).length()); + width[i] = Math.max(width[i], StringUtils.toString(row.get(i)).length()); } } } @@ -120,7 +118,8 @@ private String formatWithoutHeader(StringBuilder sb, List> rows) { } // TODO are we sure toString is correct here? What about dates that come back as longs. // Tracked by https://github.com/elastic/x-pack-elasticsearch/issues/3081 - String string = toString(row.get(i)); if (string.length() <= width[i]) { + String string = StringUtils.toString(row.get(i)); + if (string.length() <= width[i]) { // Pad sb.append(string); int padding = width[i] - string.length(); @@ -138,14 +137,6 @@ private String formatWithoutHeader(StringBuilder sb, List> rows) { return sb.toString(); } - private static String toString(Object object) { - if (object instanceof ZonedDateTime) { - return DateUtils.toString((ZonedDateTime) object); - } else { - return Objects.toString(object); - } - } - /** * Pick a good estimate of the buffer size needed to contain the rows. */ diff --git a/x-pack/plugin/sql/sql-action/src/main/java/org/elasticsearch/xpack/sql/action/SqlQueryResponse.java b/x-pack/plugin/sql/sql-action/src/main/java/org/elasticsearch/xpack/sql/action/SqlQueryResponse.java index c016ede399452..bafd4581f1fa5 100644 --- a/x-pack/plugin/sql/sql-action/src/main/java/org/elasticsearch/xpack/sql/action/SqlQueryResponse.java +++ b/x-pack/plugin/sql/sql-action/src/main/java/org/elasticsearch/xpack/sql/action/SqlQueryResponse.java @@ -13,8 +13,8 @@ import org.elasticsearch.common.xcontent.ToXContentObject; import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.xpack.sql.proto.ColumnInfo; -import org.elasticsearch.xpack.sql.proto.DateUtils; import org.elasticsearch.xpack.sql.proto.Mode; +import org.elasticsearch.xpack.sql.proto.StringUtils; import java.io.IOException; import java.time.ZonedDateTime; @@ -176,7 +176,7 @@ public static XContentBuilder value(XContentBuilder builder, Mode mode, Object v } // otherwise use the ISO format else { - builder.value(DateUtils.toString(zdt)); + builder.value(StringUtils.toString(zdt)); } } else { builder.value(value); diff --git a/x-pack/plugin/sql/sql-proto/src/main/java/org/elasticsearch/xpack/sql/proto/DateUtils.java b/x-pack/plugin/sql/sql-proto/src/main/java/org/elasticsearch/xpack/sql/proto/DateUtils.java deleted file mode 100644 index c087affe4ccc5..0000000000000 --- a/x-pack/plugin/sql/sql-proto/src/main/java/org/elasticsearch/xpack/sql/proto/DateUtils.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -package org.elasticsearch.xpack.sql.proto; - -import java.time.ZonedDateTime; -import java.time.format.DateTimeFormatter; -import java.time.format.DateTimeFormatterBuilder; -import java.util.Locale; - -import static java.time.format.DateTimeFormatter.ISO_LOCAL_DATE; -import static java.time.temporal.ChronoField.HOUR_OF_DAY; -import static java.time.temporal.ChronoField.MILLI_OF_SECOND; -import static java.time.temporal.ChronoField.MINUTE_OF_HOUR; -import static java.time.temporal.ChronoField.SECOND_OF_MINUTE; - -public class DateUtils { - - private static final DateTimeFormatter ISO_WITH_MILLIS = new DateTimeFormatterBuilder() - .parseCaseInsensitive() - .append(ISO_LOCAL_DATE) - .appendLiteral('T') - .appendValue(HOUR_OF_DAY, 2) - .appendLiteral(':') - .appendValue(MINUTE_OF_HOUR, 2) - .appendLiteral(':') - .appendValue(SECOND_OF_MINUTE, 2) - .appendFraction(MILLI_OF_SECOND, 3, 3, true) - .appendOffsetId() - .toFormatter(Locale.ROOT); - - private DateUtils() {} - - - public static String toString(ZonedDateTime dateTime) { - return dateTime.format(ISO_WITH_MILLIS); - } -} diff --git a/x-pack/plugin/sql/sql-proto/src/main/java/org/elasticsearch/xpack/sql/proto/StringUtils.java b/x-pack/plugin/sql/sql-proto/src/main/java/org/elasticsearch/xpack/sql/proto/StringUtils.java index 344f614f9c266..10ece6f5a0025 100644 --- a/x-pack/plugin/sql/sql-proto/src/main/java/org/elasticsearch/xpack/sql/proto/StringUtils.java +++ b/x-pack/plugin/sql/sql-proto/src/main/java/org/elasticsearch/xpack/sql/proto/StringUtils.java @@ -9,10 +9,33 @@ import java.sql.Timestamp; import java.time.Duration; import java.time.Period; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeFormatterBuilder; +import java.util.Locale; import java.util.Objects; import java.util.concurrent.TimeUnit; -public class StringUtils { +import static java.time.format.DateTimeFormatter.ISO_LOCAL_DATE; +import static java.time.temporal.ChronoField.HOUR_OF_DAY; +import static java.time.temporal.ChronoField.MILLI_OF_SECOND; +import static java.time.temporal.ChronoField.MINUTE_OF_HOUR; +import static java.time.temporal.ChronoField.SECOND_OF_MINUTE; + +public final class StringUtils { + + private static final DateTimeFormatter ISO_WITH_MILLIS = new DateTimeFormatterBuilder() + .parseCaseInsensitive() + .append(ISO_LOCAL_DATE) + .appendLiteral('T') + .appendValue(HOUR_OF_DAY, 2) + .appendLiteral(':') + .appendValue(MINUTE_OF_HOUR, 2) + .appendLiteral(':') + .appendValue(SECOND_OF_MINUTE, 2) + .appendFraction(MILLI_OF_SECOND, 3, 3, true) + .appendOffsetId() + .toFormatter(Locale.ROOT); private static final int SECONDS_PER_MINUTE = 60; private static final int SECONDS_PER_HOUR = SECONDS_PER_MINUTE * 60; @@ -30,13 +53,22 @@ public static String toString(Object value) { return ts.toInstant().toString(); } + if (value instanceof ZonedDateTime) { + return ((ZonedDateTime) value).format(ISO_WITH_MILLIS); + } + // handle intervals // YEAR/MONTH/YEAR TO MONTH -> YEAR TO MONTH if (value instanceof Period) { // +yyy-mm - 7 chars StringBuilder sb = new StringBuilder(7); Period p = (Period) value; - sb.append(p.isNegative() ? "-" : "+"); + if (p.isNegative()) { + sb.append("-"); + p = p.negated(); + } else { + sb.append("+"); + } sb.append(p.getYears()); sb.append("-"); sb.append(p.getMonths()); @@ -48,7 +80,12 @@ public static String toString(Object value) { // +ddd hh:mm:ss.mmmmmmmmm - 23 chars StringBuilder sb = new StringBuilder(23); Duration d = (Duration) value; - sb.append(d.isNegative() ? "-" : "+"); + if (d.isNegative()) { + sb.append("-"); + d = d.negated(); + } else { + sb.append("+"); + } long durationInSec = d.getSeconds(); diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/execution/PlanExecutor.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/execution/PlanExecutor.java index 23f1a6049dc2c..bef59d0c7ad88 100644 --- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/execution/PlanExecutor.java +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/execution/PlanExecutor.java @@ -49,10 +49,6 @@ public PlanExecutor(Client client, IndexResolver indexResolver, NamedWriteableRe this.planner = new Planner(); } - public NamedWriteableRegistry writableRegistry() { - return writableRegistry; - } - private SqlSession newSession(Configuration cfg) { return new SqlSession(cfg, client, functionRegistry, indexResolver, preAnalyzer, optimizer, planner); } diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/datetime/DateTimeFunction.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/datetime/DateTimeFunction.java index cad8265a03d65..0b429fdf1a187 100644 --- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/datetime/DateTimeFunction.java +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/datetime/DateTimeFunction.java @@ -6,7 +6,6 @@ package org.elasticsearch.xpack.sql.expression.function.scalar.datetime; import org.elasticsearch.xpack.sql.expression.Expression; -import org.elasticsearch.xpack.sql.expression.FieldAttribute; import org.elasticsearch.xpack.sql.expression.function.scalar.datetime.DateTimeProcessor.DateTimeExtractor; import org.elasticsearch.xpack.sql.expression.gen.processor.Processor; import org.elasticsearch.xpack.sql.expression.gen.script.ParamsBuilder; @@ -43,18 +42,19 @@ public static Integer dateTimeChrono(ZonedDateTime dateTime, String tzId, String private static Integer dateTimeChrono(ZonedDateTime dateTime, ChronoField field) { return Integer.valueOf(dateTime.get(field)); } - + @Override - public ScriptTemplate scriptWithField(FieldAttribute field) { + public ScriptTemplate asScript() { ParamsBuilder params = paramsBuilder(); - String template = null; - template = formatTemplate("{sql}.dateTimeChrono(doc[{}].value, {}, {})"); - params.variable(field.name()) + ScriptTemplate script = super.asScript(); + String template = formatTemplate("{sql}.dateTimeChrono(" + script.template() + ", {}, {})"); + params.script(script.params()) .variable(timeZone().getID()) .variable(extractor.chronoField().name()); return new ScriptTemplate(template, params.build(), dataType()); + } @Override diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/whitelist/InternalSqlScriptUtils.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/whitelist/InternalSqlScriptUtils.java index 3139b08fefd84..d72e5a1b9263e 100644 --- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/whitelist/InternalSqlScriptUtils.java +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/whitelist/InternalSqlScriptUtils.java @@ -21,6 +21,8 @@ import org.elasticsearch.xpack.sql.expression.function.scalar.string.ReplaceFunctionProcessor; import org.elasticsearch.xpack.sql.expression.function.scalar.string.StringProcessor.StringOperation; import org.elasticsearch.xpack.sql.expression.function.scalar.string.SubstringFunctionProcessor; +import org.elasticsearch.xpack.sql.expression.literal.IntervalDayTime; +import org.elasticsearch.xpack.sql.expression.literal.IntervalYearMonth; import org.elasticsearch.xpack.sql.expression.predicate.conditional.CoalesceProcessor; import org.elasticsearch.xpack.sql.expression.predicate.logical.BinaryLogicProcessor.BinaryLogicOperation; import org.elasticsearch.xpack.sql.expression.predicate.logical.NotProcessor; @@ -30,8 +32,12 @@ import org.elasticsearch.xpack.sql.expression.predicate.operator.comparison.BinaryComparisonProcessor.BinaryComparisonOperation; import org.elasticsearch.xpack.sql.expression.predicate.operator.comparison.InProcessor; import org.elasticsearch.xpack.sql.expression.predicate.regex.RegexProcessor.RegexOperation; +import org.elasticsearch.xpack.sql.type.DataType; +import org.elasticsearch.xpack.sql.util.DateUtils; import org.elasticsearch.xpack.sql.util.StringUtils; +import java.time.Duration; +import java.time.Period; import java.time.ZonedDateTime; import java.util.List; import java.util.Map; @@ -146,19 +152,19 @@ public static Boolean regex(String value, String pattern) { // // Math // - public static Number add(Number left, Number right) { + public static Object add(Object left, Object right) { return BinaryArithmeticOperation.ADD.apply(left, right); } - public static Number div(Number left, Number right) { + public static Object div(Object left, Object right) { return BinaryArithmeticOperation.DIV.apply(left, right); } - public static Number mod(Number left, Number right) { + public static Object mod(Object left, Object right) { return BinaryArithmeticOperation.MOD.apply(left, right); } - public static Number mul(Number left, Number right) { + public static Object mul(Object left, Object right) { return BinaryArithmeticOperation.MUL.apply(left, right); } @@ -166,7 +172,7 @@ public static Number neg(Number value) { return UnaryArithmeticOperation.NEGATE.apply(value); } - public static Number sub(Number left, Number right) { + public static Object sub(Object left, Object right) { return BinaryArithmeticOperation.SUB.apply(left, right); } @@ -305,13 +311,35 @@ public static Integer quarter(Object dateTime, String tzId) { return QuarterProcessor.quarter(asDateTime(dateTime), tzId); } - private static ZonedDateTime asDateTime(Object dateTime) { + public static ZonedDateTime asDateTime(Object dateTime) { if (dateTime instanceof JodaCompatibleZonedDateTime) { return ((JodaCompatibleZonedDateTime) dateTime).getZonedDateTime(); } + if (dateTime instanceof ZonedDateTime) { + return (ZonedDateTime) dateTime; + } + if (dateTime instanceof Number) { + return DateUtils.of(((Number) dateTime).longValue()); + } + throw new SqlIllegalArgumentException("Invalid date encountered [{}]", dateTime); } + public static IntervalDayTime intervalDayTime(String text, String typeName) { + if (text == null || typeName == null) { + return null; + } + return new IntervalDayTime(Duration.parse(text), DataType.fromTypeName(typeName)); + } + + public static IntervalYearMonth intervalYearMonth(String text, String typeName) { + if (text == null || typeName == null) { + return null; + } + + return new IntervalYearMonth(Period.parse(text), DataType.fromTypeName(typeName)); + } + // // String functions // diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/gen/script/ScriptWeaver.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/gen/script/ScriptWeaver.java index da41ecbd8a44e..faa7985b654f9 100644 --- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/gen/script/ScriptWeaver.java +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/gen/script/ScriptWeaver.java @@ -13,6 +13,8 @@ import org.elasticsearch.xpack.sql.expression.FieldAttribute; import org.elasticsearch.xpack.sql.expression.function.aggregate.AggregateFunctionAttribute; import org.elasticsearch.xpack.sql.expression.function.scalar.ScalarFunctionAttribute; +import org.elasticsearch.xpack.sql.expression.literal.IntervalDayTime; +import org.elasticsearch.xpack.sql.expression.literal.IntervalYearMonth; import org.elasticsearch.xpack.sql.type.DataType; import static org.elasticsearch.xpack.sql.expression.gen.script.ParamsBuilder.paramsBuilder; @@ -45,8 +47,22 @@ default ScriptTemplate asScript(Expression exp) { DataType dataType(); default ScriptTemplate scriptWithFoldable(Expression foldable) { + Object fold = foldable.fold(); + // wrap intervals with dedicated methods for serialization + if (fold instanceof IntervalYearMonth) { + IntervalYearMonth iym = (IntervalYearMonth) fold; + return new ScriptTemplate(processScript("{sql}.intervalYearMonth({},{})"), + paramsBuilder().variable(iym.interval().toString()).variable(iym.dataType().name()).build(), + dataType()); + } else if (fold instanceof IntervalDayTime) { + IntervalDayTime idt = (IntervalDayTime) fold; + return new ScriptTemplate(processScript("{sql}.intervalDayTime({},{})"), + paramsBuilder().variable(idt.interval().toString()).variable(idt.dataType().name()).build(), + dataType()); + } + return new ScriptTemplate(processScript("{}"), - paramsBuilder().variable(foldable.fold()).build(), + paramsBuilder().variable(fold).build(), dataType()); } @@ -58,7 +74,11 @@ default ScriptTemplate scriptWithScalar(ScalarFunctionAttribute scalar) { } default ScriptTemplate scriptWithAggregate(AggregateFunctionAttribute aggregate) { - return new ScriptTemplate(processScript("{}"), + String template = "{}"; + if (aggregate.dataType() == DataType.DATE) { + template = "{sql}.asDateTime({})"; + } + return new ScriptTemplate(processScript(template), paramsBuilder().agg(aggregate).build(), dataType()); } diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/literal/Interval.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/literal/Interval.java index da505fe72cdd4..0aabf3b76b6bd 100644 --- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/literal/Interval.java +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/literal/Interval.java @@ -6,6 +6,7 @@ package org.elasticsearch.xpack.sql.expression.literal; +import org.elasticsearch.common.io.stream.NamedWriteable; import org.elasticsearch.common.xcontent.ToXContentObject; import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.xpack.sql.type.DataType; @@ -23,7 +24,7 @@ * Unfortunately because the SQL interval type is not preserved accurately by the JDK TemporalAmount class * in both cases, the data type needs to be carried around as it cannot be inferred. */ -public abstract class Interval implements ToXContentObject { +public abstract class Interval implements NamedWriteable, ToXContentObject { private final I interval; private final DataType intervalType; @@ -41,6 +42,10 @@ public DataType dataType() { return intervalType; } + public abstract Interval add(Interval interval); + + public abstract Interval sub(Interval interval); + @Override public int hashCode() { return Objects.hash(interval, intervalType); @@ -63,8 +68,7 @@ public boolean equals(Object obj) { @Override public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { - builder.value(interval); - return builder; + return builder.value(interval); } @Override diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/literal/IntervalDayTime.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/literal/IntervalDayTime.java index dd177fff090c8..7fa0e1696ec5b 100644 --- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/literal/IntervalDayTime.java +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/literal/IntervalDayTime.java @@ -6,8 +6,12 @@ package org.elasticsearch.xpack.sql.expression.literal; +import org.elasticsearch.common.io.stream.StreamInput; +import org.elasticsearch.common.io.stream.StreamOutput; import org.elasticsearch.xpack.sql.type.DataType; +import org.elasticsearch.xpack.sql.type.DataTypes; +import java.io.IOException; import java.time.Duration; /** @@ -15,7 +19,39 @@ */ public class IntervalDayTime extends Interval { + public static final String NAME = "idt"; + + private static Duration duration(StreamInput in) throws IOException { + return Duration.ofSeconds(in.readVLong(), in.readVInt()); + } + public IntervalDayTime(Duration interval, DataType intervalType) { super(interval, intervalType); } + + IntervalDayTime(StreamInput in) throws IOException { + super(duration(in), in.readEnum(DataType.class)); + } + + @Override + public void writeTo(StreamOutput out) throws IOException { + out.writeVLong(interval().getSeconds()); + out.writeVInt(interval().getNano()); + out.writeEnum(dataType()); + } + + @Override + public String getWriteableName() { + return NAME; + } + + @Override + public IntervalDayTime add(Interval interval) { + return new IntervalDayTime(interval().plus(interval.interval()), DataTypes.compatibleInterval(dataType(), interval.dataType())); + } + + @Override + public IntervalDayTime sub(Interval interval) { + return new IntervalDayTime(interval().minus(interval.interval()), DataTypes.compatibleInterval(dataType(), interval.dataType())); + } } diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/literal/IntervalYearMonth.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/literal/IntervalYearMonth.java index 2eb784d253f1d..f4267f3716d6b 100644 --- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/literal/IntervalYearMonth.java +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/literal/IntervalYearMonth.java @@ -6,8 +6,12 @@ package org.elasticsearch.xpack.sql.expression.literal; +import org.elasticsearch.common.io.stream.StreamInput; +import org.elasticsearch.common.io.stream.StreamOutput; import org.elasticsearch.xpack.sql.type.DataType; +import org.elasticsearch.xpack.sql.type.DataTypes; +import java.io.IOException; import java.time.Period; /** @@ -15,7 +19,43 @@ */ public class IntervalYearMonth extends Interval { + public static final String NAME = "iym"; + + private static Period period(StreamInput in) throws IOException { + return Period.of(in.readVInt(), in.readVInt(), in.readVInt()); + } + public IntervalYearMonth(Period interval, DataType intervalType) { super(interval, intervalType); } + + IntervalYearMonth(StreamInput in) throws IOException { + super(period(in), in.readEnum(DataType.class)); + } + + @Override + public void writeTo(StreamOutput out) throws IOException { + Period p = interval(); + out.writeVInt(p.getYears()); + out.writeVInt(p.getMonths()); + out.writeVInt(p.getDays()); + out.writeEnum(dataType()); + } + + @Override + public String getWriteableName() { + return NAME; + } + + @Override + public IntervalYearMonth add(Interval interval) { + return new IntervalYearMonth(interval().plus(interval.interval()).normalized(), + DataTypes.compatibleInterval(dataType(), interval.dataType())); + } + + @Override + public IntervalYearMonth sub(Interval interval) { + return new IntervalYearMonth(interval().minus(interval.interval()).normalized(), + DataTypes.compatibleInterval(dataType(), interval.dataType())); + } } diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/literal/IntervalUtils.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/literal/Intervals.java similarity index 96% rename from x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/literal/IntervalUtils.java rename to x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/literal/Intervals.java index 590446230b7f3..ad3b345e9c560 100644 --- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/literal/IntervalUtils.java +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/literal/Intervals.java @@ -6,6 +6,8 @@ package org.elasticsearch.xpack.sql.expression.literal; +import org.elasticsearch.common.io.stream.NamedWriteableRegistry; +import org.elasticsearch.common.io.stream.NamedWriteableRegistry.Entry; import org.elasticsearch.xpack.sql.SqlIllegalArgumentException; import org.elasticsearch.xpack.sql.parser.ParsingException; import org.elasticsearch.xpack.sql.tree.Location; @@ -16,6 +18,7 @@ import java.time.Period; import java.time.temporal.TemporalAmount; import java.util.ArrayList; +import java.util.Collection; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; @@ -28,7 +31,7 @@ import static org.elasticsearch.xpack.sql.type.DataType.INTERVAL_MINUTE_TO_SECOND; import static org.elasticsearch.xpack.sql.type.DataType.INTERVAL_YEAR_TO_MONTH; -public final class IntervalUtils { +public final class Intervals { /** * Time unit definition - used to remember the initial declaration @@ -38,7 +41,7 @@ public enum TimeUnit { YEAR, MONTH, DAY, HOUR, MINUTE, SECOND, MILLISECOND; } - private IntervalUtils() {} + private Intervals() {} public static TemporalAmount of(Location source, long duration, TimeUnit unit) { // Cannot use Period.of since it accepts int so use plus which accepts long @@ -369,4 +372,13 @@ public static TemporalAmount negate(TemporalAmount interval) { public static TemporalAmount parseInterval(Location source, String value, DataType intervalType) { return PARSERS.get(intervalType).parse(source, value); } + + public static Collection getNamedWriteables() { + List entries = new ArrayList<>(); + + entries.add(new Entry(IntervalDayTime.class, IntervalDayTime.NAME, IntervalDayTime::new)); + entries.add(new Entry(IntervalYearMonth.class, IntervalYearMonth.NAME, IntervalYearMonth::new)); + + return entries; + } } \ No newline at end of file diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/predicate/operator/arithmetic/Add.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/predicate/operator/arithmetic/Add.java index 36becf9f17be5..00140a1247cd5 100644 --- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/predicate/operator/arithmetic/Add.java +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/predicate/operator/arithmetic/Add.java @@ -13,7 +13,7 @@ /** * Addition function ({@code a + b}). */ -public class Add extends ArithmeticOperation { +public class Add extends DateTimeArithmeticOperation { public Add(Location location, Expression left, Expression right) { super(location, left, right, BinaryArithmeticOperation.ADD); } diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/predicate/operator/arithmetic/ArithmeticOperation.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/predicate/operator/arithmetic/ArithmeticOperation.java index 1209a24d86589..e4bbc79650571 100644 --- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/predicate/operator/arithmetic/ArithmeticOperation.java +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/predicate/operator/arithmetic/ArithmeticOperation.java @@ -13,11 +13,8 @@ import org.elasticsearch.xpack.sql.tree.Location; import org.elasticsearch.xpack.sql.type.DataType; import org.elasticsearch.xpack.sql.type.DataTypeConversion; -import org.elasticsearch.xpack.sql.type.DataTypes; -import static org.elasticsearch.common.logging.LoggerMessageFormat.format; - -public abstract class ArithmeticOperation extends BinaryOperator { +public abstract class ArithmeticOperation extends BinaryOperator { private DataType dataType; @@ -25,39 +22,6 @@ protected ArithmeticOperation(Location location, Expression left, Expression rig super(location, left, right, operation); } - @Override - protected TypeResolution resolveType() { - if (!childrenResolved()) { - return new TypeResolution("Unresolved children"); - } - - // arithmetic operation can work on: - // 1. numbers - // 2. intervals (of compatible types) - // 3. dates and intervals - // 4. single unit intervals and numbers - - DataType l = left().dataType(); - DataType r = right().dataType(); - - // 1. both are numbers - if (l.isNumeric() && r.isNumeric()) { - return TypeResolution.TYPE_RESOLVED; - } - // 2. 3. 4. intervals - if ((DataTypes.isInterval(l) || DataTypes.isInterval(r))) { - dataType = DataTypeConversion.commonType(l, r); - if (dataType == null) { - return new TypeResolution(format("[{}] has arguments with incompatible types [{}] and [{}]", symbol(), l, r)); - } else { - return TypeResolution.TYPE_RESOLVED; - } - } - - // fall-back to default checks - return super.resolveType(); - } - @Override protected TypeResolution resolveInputType(Expression e, Expressions.ParamOrdinal paramOrdinal) { return Expressions.typeMustBeNumeric(e, symbol(), paramOrdinal); diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/predicate/operator/arithmetic/Arithmetics.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/predicate/operator/arithmetic/Arithmetics.java index bec35eb449ca9..944a144e2f998 100644 --- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/predicate/operator/arithmetic/Arithmetics.java +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/predicate/operator/arithmetic/Arithmetics.java @@ -33,22 +33,6 @@ static Number add(Number l, Number r) { return Integer.valueOf(Math.addExact(l.intValue(), r.intValue())); } - static Period add(Period l, Period r) { - if (l == null || r == null) { - return null; - } - - return l.plus(r); - } - - static Duration add(Duration l, Duration r) { - if (l == null || r == null) { - return null; - } - - return l.plus(r); - } - static ZonedDateTime add(ZonedDateTime l, Period r) { if (l == null || r == null) { return null; @@ -83,22 +67,6 @@ static Number sub(Number l, Number r) { return Integer.valueOf(Math.subtractExact(l.intValue(), r.intValue())); } - static Period sub(Period l, Period r) { - if (l == null || r == null) { - return null; - } - - return l.minus(r); - } - - static Duration sub(Duration l, Duration r) { - if (l == null || r == null) { - return null; - } - - return l.minus(r); - } - static ZonedDateTime sub(ZonedDateTime l, Period r) { if (l == null || r == null) { return null; diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/predicate/operator/arithmetic/BinaryArithmeticProcessor.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/predicate/operator/arithmetic/BinaryArithmeticProcessor.java index 097acf8e716e9..5d1a11e19310b 100644 --- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/predicate/operator/arithmetic/BinaryArithmeticProcessor.java +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/predicate/operator/arithmetic/BinaryArithmeticProcessor.java @@ -6,39 +6,103 @@ package org.elasticsearch.xpack.sql.expression.predicate.operator.arithmetic; import org.elasticsearch.common.io.stream.StreamInput; +import org.elasticsearch.script.JodaCompatibleZonedDateTime; import org.elasticsearch.xpack.sql.SqlIllegalArgumentException; import org.elasticsearch.xpack.sql.expression.gen.processor.FunctionalBinaryProcessor; import org.elasticsearch.xpack.sql.expression.gen.processor.Processor; +import org.elasticsearch.xpack.sql.expression.literal.Interval; +import org.elasticsearch.xpack.sql.expression.literal.IntervalDayTime; +import org.elasticsearch.xpack.sql.expression.literal.IntervalYearMonth; import org.elasticsearch.xpack.sql.expression.predicate.PredicateBiFunction; import org.elasticsearch.xpack.sql.expression.predicate.operator.arithmetic.BinaryArithmeticProcessor.BinaryArithmeticOperation; import java.io.IOException; +import java.time.ZonedDateTime; import java.util.function.BiFunction; -public class BinaryArithmeticProcessor extends FunctionalBinaryProcessor { +public class BinaryArithmeticProcessor extends FunctionalBinaryProcessor { - public enum BinaryArithmeticOperation implements PredicateBiFunction { - ADD(Arithmetics::add, "+"), - SUB(Arithmetics::sub, "-"), + private interface NumericArithmetic extends BiFunction { + default Object wrap(Object l, Object r) { + return apply((Number) l, (Number) r); + } + } + + public enum BinaryArithmeticOperation implements PredicateBiFunction { + ADD((Object l, Object r) -> { + if (l instanceof Number) { + return Arithmetics.add((Number) l, (Number) r); + } + if (l instanceof IntervalYearMonth && r instanceof IntervalYearMonth) { + return ((IntervalYearMonth) l).add((IntervalYearMonth) r); + } + if (l instanceof IntervalDayTime && r instanceof IntervalDayTime) { + return ((IntervalDayTime) l).add((IntervalDayTime) r); + } + l = unwrapJodaTime(l); + r = unwrapJodaTime(r); + if (l instanceof ZonedDateTime && r instanceof IntervalYearMonth) { + return Arithmetics.add((ZonedDateTime) l, ((IntervalYearMonth) r).interval()); + } + if (l instanceof ZonedDateTime && r instanceof IntervalDayTime) { + return Arithmetics.add((ZonedDateTime) l, ((IntervalDayTime) r).interval()); + } + if (r instanceof ZonedDateTime && l instanceof IntervalYearMonth) { + return Arithmetics.add((ZonedDateTime) r, ((IntervalYearMonth) l).interval()); + } + if (r instanceof ZonedDateTime && l instanceof IntervalDayTime) { + return Arithmetics.add((ZonedDateTime) r, ((IntervalDayTime) l).interval()); + } + + throw new SqlIllegalArgumentException("Cannot compute [+] between [{}] [{}]", l.getClass(), r.getClass()); + }, "+"), + SUB((Object l, Object r) -> { + if (l instanceof Number) { + return Arithmetics.sub((Number) l, (Number) r); + } + if (l instanceof IntervalYearMonth && r instanceof IntervalYearMonth) { + return ((IntervalYearMonth) l).sub((IntervalYearMonth) r); + } + if (l instanceof IntervalDayTime && r instanceof IntervalDayTime) { + return ((IntervalDayTime) l).sub((IntervalDayTime) r); + } + l = unwrapJodaTime(l); + r = unwrapJodaTime(r); + if (l instanceof ZonedDateTime && r instanceof IntervalYearMonth) { + return Arithmetics.sub((ZonedDateTime) l, ((IntervalYearMonth) r).interval()); + } + if (l instanceof ZonedDateTime && r instanceof IntervalDayTime) { + return Arithmetics.sub((ZonedDateTime) l, ((IntervalDayTime) r).interval()); + } + if (r instanceof ZonedDateTime && l instanceof Interval) { + throw new SqlIllegalArgumentException("Cannot substract a date from an interval; do you mean the reverse?"); + } + + throw new SqlIllegalArgumentException("Cannot compute [-] between [{}] [{}]", l.getClass(), r.getClass()); + }, "-"), MUL(Arithmetics::mul, "*"), DIV(Arithmetics::div, "/"), MOD(Arithmetics::mod, "%"); - private final BiFunction process; + private final BiFunction process; private final String symbol; - BinaryArithmeticOperation(BiFunction process, String symbol) { + BinaryArithmeticOperation(BiFunction process, String symbol) { this.process = process; this.symbol = symbol; } + BinaryArithmeticOperation(NumericArithmetic process, String symbol) { + this(process::wrap, symbol); + } + @Override public String symbol() { return symbol; } @Override - public final Number doApply(Number left, Number right) { + public final Object doApply(Object left, Object right) { return process.apply(left, right); } @@ -46,9 +110,13 @@ public final Number doApply(Number left, Number right) { public String toString() { return symbol; } + + private static Object unwrapJodaTime(Object o) { + return o instanceof JodaCompatibleZonedDateTime ? ((JodaCompatibleZonedDateTime) o).getZonedDateTime() : o; + } } - public static final String NAME = "ab"; + public static final String NAME = "abn"; public BinaryArithmeticProcessor(Processor left, Processor right, BinaryArithmeticOperation operation) { super(left, right, operation); @@ -64,9 +132,30 @@ public String getWriteableName() { } @Override - protected void checkParameter(Object param) { - if (!(param instanceof Number)) { - throw new SqlIllegalArgumentException("A number is required; received {}", param); + protected Object doProcess(Object left, Object right) { + BinaryArithmeticOperation f = function(); + + if (left == null || right == null) { + return null; + } + + if (f == BinaryArithmeticOperation.MUL || f == BinaryArithmeticOperation.DIV || f == BinaryArithmeticOperation.MOD) { + if (!(left instanceof Number)) { + throw new SqlIllegalArgumentException("A number is required; received {}", left); + } + + if (!(right instanceof Number)) { + throw new SqlIllegalArgumentException("A number is required; received {}", right); + } + + return f.apply(left, right); + } + + if (f == BinaryArithmeticOperation.ADD || f == BinaryArithmeticOperation.SUB) { + return f.apply(left, right); } + + // this should not occur + throw new SqlIllegalArgumentException("Cannot perform arithmetic operation due to arguments"); } } \ No newline at end of file diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/predicate/operator/arithmetic/DateTimeArithmeticOperation.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/predicate/operator/arithmetic/DateTimeArithmeticOperation.java new file mode 100644 index 0000000000000..15be13e1e7533 --- /dev/null +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/predicate/operator/arithmetic/DateTimeArithmeticOperation.java @@ -0,0 +1,56 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +package org.elasticsearch.xpack.sql.expression.predicate.operator.arithmetic; + +import org.elasticsearch.xpack.sql.expression.Expression; +import org.elasticsearch.xpack.sql.expression.predicate.operator.arithmetic.BinaryArithmeticProcessor.BinaryArithmeticOperation; +import org.elasticsearch.xpack.sql.tree.Location; +import org.elasticsearch.xpack.sql.type.DataType; +import org.elasticsearch.xpack.sql.type.DataTypeConversion; +import org.elasticsearch.xpack.sql.type.DataTypes; + +import static org.elasticsearch.common.logging.LoggerMessageFormat.format; + +abstract class DateTimeArithmeticOperation extends ArithmeticOperation { + + DateTimeArithmeticOperation(Location location, Expression left, Expression right, BinaryArithmeticOperation operation) { + super(location, left, right, operation); + } + + @Override + protected TypeResolution resolveType() { + if (!childrenResolved()) { + return new TypeResolution("Unresolved children"); + } + + // arithmetic operation can work on: + // 1. numbers + // 2. intervals (of compatible types) + // 3. dates and intervals + // 4. single unit intervals and numbers + + DataType l = left().dataType(); + DataType r = right().dataType(); + + // 1. both are numbers + if (l.isNumeric() && r.isNumeric()) { + return TypeResolution.TYPE_RESOLVED; + } + // 2. 3. 4. intervals + if ((DataTypes.isInterval(l) || DataTypes.isInterval(r))) { + if (DataTypeConversion.commonType(l, r) == null) { + return new TypeResolution(format("[{}] has arguments with incompatible types [{}] and [{}]", symbol(), l, r)); + } else { + return TypeResolution.TYPE_RESOLVED; + } + } + + // fall-back to default checks + return super.resolveType(); + } + +} diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/predicate/operator/arithmetic/Sub.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/predicate/operator/arithmetic/Sub.java index 25c66572d204c..6eda503bb6618 100644 --- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/predicate/operator/arithmetic/Sub.java +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/predicate/operator/arithmetic/Sub.java @@ -13,7 +13,7 @@ /** * Subtraction function ({@code a - b}). */ -public class Sub extends ArithmeticOperation { +public class Sub extends DateTimeArithmeticOperation { public Sub(Location location, Expression left, Expression right) { super(location, left, right, BinaryArithmeticOperation.SUB); diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/parser/ExpressionBuilder.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/parser/ExpressionBuilder.java index 8dfd699eb785a..ae49186030fc9 100644 --- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/parser/ExpressionBuilder.java +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/parser/ExpressionBuilder.java @@ -26,9 +26,10 @@ import org.elasticsearch.xpack.sql.expression.function.scalar.Cast; import org.elasticsearch.xpack.sql.expression.literal.Interval; import org.elasticsearch.xpack.sql.expression.literal.IntervalDayTime; -import org.elasticsearch.xpack.sql.expression.literal.IntervalUtils; -import org.elasticsearch.xpack.sql.expression.literal.IntervalUtils.TimeUnit; -import org.elasticsearch.xpack.sql.expression.literal.IntervalYearMonth;import org.elasticsearch.xpack.sql.expression.predicate.Range; +import org.elasticsearch.xpack.sql.expression.literal.Intervals; +import org.elasticsearch.xpack.sql.expression.literal.Intervals.TimeUnit; +import org.elasticsearch.xpack.sql.expression.literal.IntervalYearMonth; +import org.elasticsearch.xpack.sql.expression.predicate.Range; import org.elasticsearch.xpack.sql.expression.predicate.fulltext.MatchQueryPredicate; import org.elasticsearch.xpack.sql.expression.predicate.fulltext.MultiMatchQueryPredicate; import org.elasticsearch.xpack.sql.expression.predicate.fulltext.StringQueryPredicate; @@ -102,8 +103,9 @@ import org.elasticsearch.xpack.sql.tree.Location; import org.elasticsearch.xpack.sql.type.DataType; import org.elasticsearch.xpack.sql.type.DataTypes; - -import org.elasticsearch.xpack.sql.util.DateUtils;import org.joda.time.DateTime; +import org.elasticsearch.xpack.sql.util.DateUtils; +import org.elasticsearch.xpack.sql.util.StringUtils; +import org.joda.time.DateTime; import org.joda.time.format.DateTimeFormatter; import org.joda.time.format.DateTimeFormatterBuilder; import org.joda.time.format.ISODateTimeFormat; @@ -529,7 +531,7 @@ public Literal visitIntervalLiteral(IntervalLiteralContext ctx) { } } - DataType intervalType = IntervalUtils.intervalType(source(interval), leading, trailing); + DataType intervalType = Intervals.intervalType(source(interval), leading, trailing); boolean negative = interval.sign != null && interval.sign.getType() == SqlBaseParser.MINUS; @@ -566,21 +568,21 @@ private TemporalAmount of(NumberContext valueNumeric, boolean negative, TimeUnit throw new ParsingException(source(valueNumeric), "Fractional values are not supported for intervals"); } - return IntervalUtils.of(source(valueNumeric), numeric.longValue(), unit); + return Intervals.of(source(valueNumeric), numeric.longValue(), unit); } private TemporalAmount visitIntervalValue(StringContext valuePattern, boolean negative, DataType intervalType) { String valueString = string(valuePattern); Location loc = source(valuePattern); - TemporalAmount interval = IntervalUtils.parseInterval(loc, valueString, intervalType); + TemporalAmount interval = Intervals.parseInterval(loc, valueString, intervalType); if (negative) { - interval = IntervalUtils.negate(interval); + interval = Intervals.negate(interval); } return interval; } @Override - public IntervalUtils.TimeUnit visitIntervalField(IntervalFieldContext ctx) { + public Intervals.TimeUnit visitIntervalField(IntervalFieldContext ctx) { if (ctx == null) { return null; } @@ -588,22 +590,22 @@ public IntervalUtils.TimeUnit visitIntervalField(IntervalFieldContext ctx) { switch (ctx.getChild(TerminalNode.class, 0).getSymbol().getType()) { case SqlBaseParser.YEAR: case SqlBaseParser.YEARS: - return IntervalUtils.TimeUnit.YEAR; + return Intervals.TimeUnit.YEAR; case SqlBaseParser.MONTH: case SqlBaseParser.MONTHS: - return IntervalUtils.TimeUnit.MONTH; + return Intervals.TimeUnit.MONTH; case SqlBaseParser.DAY: case SqlBaseParser.DAYS: - return IntervalUtils.TimeUnit.DAY; + return Intervals.TimeUnit.DAY; case SqlBaseParser.HOUR: case SqlBaseParser.HOURS: - return IntervalUtils.TimeUnit.HOUR; + return Intervals.TimeUnit.HOUR; case SqlBaseParser.MINUTE: case SqlBaseParser.MINUTES: - return IntervalUtils.TimeUnit.MINUTE; + return Intervals.TimeUnit.MINUTE; case SqlBaseParser.SECOND: case SqlBaseParser.SECONDS: - return IntervalUtils.TimeUnit.SECOND; + return Intervals.TimeUnit.SECOND; } throw new IllegalArgumentException("Unsupported interval field: " + ctx.getText()); diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/planner/QueryFolder.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/planner/QueryFolder.java index c17c1311cccc6..d7fb7870bf5af 100644 --- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/planner/QueryFolder.java +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/planner/QueryFolder.java @@ -286,7 +286,7 @@ protected PhysicalPlan rule(AggregateExec a) { if (matchingGroup != null) { if (exp instanceof Attribute || exp instanceof ScalarFunction) { Processor action = null; - TimeZone tz = null; + TimeZone tz = DataType.DATE == exp.dataType() ? UTC : null; /* * special handling of dates since aggs return the typed Date object which needs * extraction instead of handling this in the scroller, the folder handles this diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/querydsl/container/GroupByRef.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/querydsl/container/GroupByRef.java index cfb66fea992ff..66c05a1339d1e 100644 --- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/querydsl/container/GroupByRef.java +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/querydsl/container/GroupByRef.java @@ -22,10 +22,6 @@ public enum Property { private final Property property; private final TimeZone timeZone; - public GroupByRef(String key) { - this(key, null, null); - } - public GroupByRef(String key, Property property, TimeZone timeZone) { this.key = key; this.property = property == null ? Property.VALUE : property; diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/session/Cursors.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/session/Cursors.java index 2ecf8d6e115f1..f8d0393303d64 100644 --- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/session/Cursors.java +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/session/Cursors.java @@ -18,6 +18,7 @@ import org.elasticsearch.xpack.sql.execution.search.extractor.BucketExtractors; import org.elasticsearch.xpack.sql.execution.search.extractor.HitExtractors; import org.elasticsearch.xpack.sql.expression.function.scalar.Processors; +import org.elasticsearch.xpack.sql.expression.literal.Intervals; import org.elasticsearch.xpack.sql.plugin.CliFormatterCursor; import java.io.ByteArrayOutputStream; @@ -53,6 +54,9 @@ public static List getNamedWriteables() { entries.addAll(HitExtractors.getNamedWriteables()); entries.addAll(BucketExtractors.getNamedWriteables()); + // and custom types + entries.addAll(Intervals.getNamedWriteables()); + return entries; } diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/type/DataType.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/type/DataType.java index c704e3f63a278..b8d55f22942eb 100644 --- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/type/DataType.java +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/type/DataType.java @@ -222,7 +222,7 @@ public static DataType fromOdbcType(String odbcType) { /** * Creates returns DataType enum corresponding to the specified es type *

- * For any dataType DataType.fromEsType(dataType.esType) == dataType + * For any dataType DataType.fromTypeName(dataType.esType) == dataType */ public static DataType fromTypeName(String esType) { try { diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/type/DataTypes.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/type/DataTypes.java index 89bf15c7b60b0..601a428f4233c 100644 --- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/type/DataTypes.java +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/type/DataTypes.java @@ -7,7 +7,8 @@ import org.elasticsearch.xpack.sql.SqlIllegalArgumentException; import org.elasticsearch.xpack.sql.expression.literal.Interval; -import org.joda.time.DateTime; + +import java.time.ZonedDateTime; import static org.elasticsearch.xpack.sql.type.DataType.BOOLEAN; import static org.elasticsearch.xpack.sql.type.DataType.BYTE; @@ -66,7 +67,7 @@ public static DataType fromJava(Object value) { if (value instanceof Short) { return SHORT; } - if (value instanceof DateTime) { + if (value instanceof ZonedDateTime) { return DATE; } if (value instanceof String || value instanceof Character) { diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/util/DateUtils.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/util/DateUtils.java index b59b158d360be..814f8427a17ac 100644 --- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/util/DateUtils.java +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/util/DateUtils.java @@ -6,6 +6,7 @@ package org.elasticsearch.xpack.sql.util; +import org.elasticsearch.xpack.sql.proto.StringUtils; import org.joda.time.DateTime; import org.joda.time.format.DateTimeFormatter; import org.joda.time.format.ISODateTimeFormat; @@ -63,6 +64,6 @@ public static ZonedDateTime of(DateTime dateTime) { } public static String toString(ZonedDateTime dateTime) { - return org.elasticsearch.xpack.sql.proto.DateUtils.toString(dateTime); + return StringUtils.toString(dateTime); } } \ No newline at end of file diff --git a/x-pack/plugin/sql/src/main/resources/org/elasticsearch/xpack/sql/plugin/sql_whitelist.txt b/x-pack/plugin/sql/src/main/resources/org/elasticsearch/xpack/sql/plugin/sql_whitelist.txt index f02d8fffb2c4c..58afe78ad4153 100644 --- a/x-pack/plugin/sql/src/main/resources/org/elasticsearch/xpack/sql/plugin/sql_whitelist.txt +++ b/x-pack/plugin/sql/src/main/resources/org/elasticsearch/xpack/sql/plugin/sql_whitelist.txt @@ -6,6 +6,12 @@ # This file contains a whitelist for SQL specific utilities available inside SQL scripting +class org.elasticsearch.xpack.sql.expression.literal.IntervalDayTime { +} + +class org.elasticsearch.xpack.sql.expression.literal.IntervalYearMonth { +} + class org.elasticsearch.xpack.sql.expression.function.scalar.whitelist.InternalSqlScriptUtils { # @@ -49,12 +55,12 @@ class org.elasticsearch.xpack.sql.expression.function.scalar.whitelist.InternalS # # Math # - Number add(Number, Number) - Number div(Number, Number) - Number mod(Number, Number) - Number mul(Number, Number) + def add(Object, Object) + def sub(Object, Object) + def div(Object, Object) + def mod(Object, Object) + def mul(Object, Object) Number neg(Number) - Number sub(Number, Number) Number round(Number, Number) Number truncate(Number, Number) @@ -90,6 +96,9 @@ class org.elasticsearch.xpack.sql.expression.function.scalar.whitelist.InternalS String dayName(Object, String) String monthName(Object, String) Integer quarter(Object, String) + IntervalDayTime intervalDayTime(String, String) + IntervalYearMonth intervalYearMonth(String, String) + ZonedDateTime asDateTime(Object) # # ASCII Functions diff --git a/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/expression/literal/IntervalUtilsTests.java b/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/expression/literal/IntervalsTests.java similarity index 97% rename from x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/expression/literal/IntervalUtilsTests.java rename to x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/expression/literal/IntervalsTests.java index eb9e2f832f53c..1eab0f8483ffa 100644 --- a/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/expression/literal/IntervalUtilsTests.java +++ b/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/expression/literal/IntervalsTests.java @@ -7,7 +7,7 @@ package org.elasticsearch.xpack.sql.expression.literal; import org.elasticsearch.test.ESTestCase; -import org.elasticsearch.xpack.sql.expression.literal.IntervalUtils.TimeUnit; +import org.elasticsearch.xpack.sql.expression.literal.Intervals.TimeUnit; import org.elasticsearch.xpack.sql.parser.ParsingException; import java.time.Duration; @@ -16,9 +16,9 @@ import java.util.Locale; import static java.lang.String.format; -import static org.elasticsearch.xpack.sql.expression.literal.IntervalUtils.intervalType; -import static org.elasticsearch.xpack.sql.expression.literal.IntervalUtils.of; -import static org.elasticsearch.xpack.sql.expression.literal.IntervalUtils.parseInterval; +import static org.elasticsearch.xpack.sql.expression.literal.Intervals.intervalType; +import static org.elasticsearch.xpack.sql.expression.literal.Intervals.of; +import static org.elasticsearch.xpack.sql.expression.literal.Intervals.parseInterval; import static org.elasticsearch.xpack.sql.tree.Location.EMPTY; import static org.elasticsearch.xpack.sql.type.DataType.INTERVAL_DAY; import static org.elasticsearch.xpack.sql.type.DataType.INTERVAL_DAY_TO_HOUR; @@ -34,7 +34,7 @@ import static org.elasticsearch.xpack.sql.type.DataType.INTERVAL_YEAR; import static org.elasticsearch.xpack.sql.type.DataType.INTERVAL_YEAR_TO_MONTH; -public class IntervalUtilsTests extends ESTestCase { +public class IntervalsTests extends ESTestCase { public void testYearInterval() throws Exception { String randomSign = randomSign(); @@ -231,6 +231,6 @@ private static String randomSign() { } private Object maybeNegate(String randomSign, TemporalAmount interval) { - return "-".equals(randomSign) ? IntervalUtils.negate(interval) : interval; + return "-".equals(randomSign) ? Intervals.negate(interval) : interval; } } \ No newline at end of file diff --git a/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/expression/predicate/operator/arithmetic/BinaryArithmeticTests.java b/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/expression/predicate/operator/arithmetic/BinaryArithmeticTests.java new file mode 100644 index 0000000000000..1fead7e97a5f8 --- /dev/null +++ b/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/expression/predicate/operator/arithmetic/BinaryArithmeticTests.java @@ -0,0 +1,149 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +package org.elasticsearch.xpack.sql.expression.predicate.operator.arithmetic; + +import org.elasticsearch.test.ESTestCase; +import org.elasticsearch.xpack.sql.SqlIllegalArgumentException; +import org.elasticsearch.xpack.sql.expression.Literal; +import org.elasticsearch.xpack.sql.expression.literal.IntervalDayTime; +import org.elasticsearch.xpack.sql.expression.literal.IntervalYearMonth; +import org.elasticsearch.xpack.sql.type.DataType; +import org.elasticsearch.xpack.sql.util.DateUtils; + +import java.time.Duration; +import java.time.Period; +import java.time.ZonedDateTime; +import java.time.temporal.TemporalAmount; + +import static org.elasticsearch.xpack.sql.tree.Location.EMPTY; +import static org.elasticsearch.xpack.sql.type.DataType.INTERVAL_DAY; +import static org.elasticsearch.xpack.sql.type.DataType.INTERVAL_DAY_TO_HOUR; +import static org.elasticsearch.xpack.sql.type.DataType.INTERVAL_HOUR; +import static org.elasticsearch.xpack.sql.type.DataType.INTERVAL_MONTH; +import static org.elasticsearch.xpack.sql.type.DataType.INTERVAL_YEAR; +import static org.elasticsearch.xpack.sql.type.DataType.INTERVAL_YEAR_TO_MONTH; + +public class BinaryArithmeticTests extends ESTestCase { + + public void testAddNumbers() throws Exception { + assertEquals(Long.valueOf(3), add(1L, 2L)); + } + + public void testAddYearMonthIntervals() throws Exception { + Literal l = interval(Period.ofYears(1), INTERVAL_YEAR); + Literal r = interval(Period.ofMonths(2), INTERVAL_MONTH); + IntervalYearMonth x = add(l, r); + assertEquals(interval(Period.ofYears(1).plusMonths(2), INTERVAL_YEAR_TO_MONTH), L(x)); + } + + public void testAddYearMonthMixedIntervals() throws Exception { + Literal l = interval(Period.ofYears(1).plusMonths(5), INTERVAL_YEAR_TO_MONTH); + Literal r = interval(Period.ofMonths(2), INTERVAL_MONTH); + IntervalYearMonth x = add(l, r); + assertEquals(interval(Period.ofYears(1).plusMonths(7), INTERVAL_YEAR_TO_MONTH), L(x)); + } + + public void testAddDayTimeIntervals() throws Exception { + Literal l = interval(Duration.ofDays(1), INTERVAL_DAY); + Literal r = interval(Duration.ofHours(2), INTERVAL_HOUR); + IntervalDayTime x = add(l, r); + assertEquals(interval(Duration.ofDays(1).plusHours(2), INTERVAL_DAY_TO_HOUR), L(x)); + } + + public void testAddYearMonthIntervalToDate() throws Exception { + ZonedDateTime now = ZonedDateTime.now(DateUtils.UTC); + Literal l = L(now); + TemporalAmount t = Period.ofYears(100).plusMonths(50); + Literal r = interval(t, INTERVAL_HOUR); + ZonedDateTime x = add(l, r); + assertEquals(L(now.plus(t)), L(x)); + } + + public void testAddDayTimeIntervalToDate() throws Exception { + ZonedDateTime now = ZonedDateTime.now(DateUtils.UTC); + Literal l = L(now); + TemporalAmount t = Duration.ofHours(2); + Literal r = interval(Duration.ofHours(2), INTERVAL_HOUR); + ZonedDateTime x = add(l, r); + assertEquals(L(now.plus(t)), L(x)); + } + + public void testAddDayTimeIntervalToDateIllegal() throws Exception { + ZonedDateTime now = ZonedDateTime.now(DateUtils.UTC); + Literal l = L(now); + TemporalAmount t = Duration.ofHours(2); + Literal r = interval(Duration.ofHours(2), INTERVAL_HOUR); + ZonedDateTime x = add(r, l); + assertEquals(L(now.plus(t)), L(x)); + } + + public void testSubYearMonthIntervals() throws Exception { + Literal l = interval(Period.ofYears(1), INTERVAL_YEAR); + Literal r = interval(Period.ofMonths(2), INTERVAL_MONTH); + IntervalYearMonth x = sub(l, r); + assertEquals(interval(Period.ofMonths(10), INTERVAL_YEAR_TO_MONTH), L(x)); + } + + public void testSubDayTimeIntervals() throws Exception { + Literal l = interval(Duration.ofDays(1).plusHours(10), INTERVAL_DAY_TO_HOUR); + Literal r = interval(Duration.ofHours(2), INTERVAL_HOUR); + IntervalDayTime x = sub(l, r); + assertEquals(interval(Duration.ofDays(1).plusHours(8), INTERVAL_DAY_TO_HOUR), L(x)); + } + + public void testSubYearMonthIntervalToDate() throws Exception { + ZonedDateTime now = ZonedDateTime.now(DateUtils.UTC); + Literal l = L(now); + TemporalAmount t = Period.ofYears(100).plusMonths(50); + Literal r = interval(t, INTERVAL_HOUR); + ZonedDateTime x = sub(l, r); + assertEquals(L(now.minus(t)), L(x)); + } + + public void testSubYearMonthIntervalToDateIllegal() throws Exception { + ZonedDateTime now = ZonedDateTime.now(DateUtils.UTC); + Literal l = L(now); + TemporalAmount t = Period.ofYears(100).plusMonths(50); + Literal r = interval(t, INTERVAL_HOUR); + SqlIllegalArgumentException ex = expectThrows(SqlIllegalArgumentException.class, () -> sub(r, l)); + assertEquals("Cannot substract a date from an interval; do you mean the reverse?", ex.getMessage()); + } + + public void testSubDayTimeIntervalToDate() throws Exception { + ZonedDateTime now = ZonedDateTime.now(DateUtils.UTC); + Literal l = L(now); + TemporalAmount t = Duration.ofHours(2); + Literal r = interval(Duration.ofHours(2), INTERVAL_HOUR); + ZonedDateTime x = sub(l, r); + assertEquals(L(now.minus(t)), L(x)); + } + + @SuppressWarnings("unchecked") + private static T add(Object l, Object r) { + Add add = new Add(EMPTY, L(l), L(r)); + assertTrue(add.foldable()); + return (T) add.fold(); + } + + @SuppressWarnings("unchecked") + private static T sub(Object l, Object r) { + Sub sub = new Sub(EMPTY, L(l), L(r)); + assertTrue(sub.foldable()); + return (T) sub.fold(); + } + + + private static Literal L(Object value) { + return Literal.of(EMPTY, value); + } + + private static Literal interval(TemporalAmount value, DataType intervalType) { + Object i = value instanceof Period ? new IntervalYearMonth((Period) value, intervalType) + : new IntervalDayTime((Duration) value, intervalType); + return Literal.of(EMPTY, i); + } +} diff --git a/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/type/DataTypeConversionTests.java b/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/type/DataTypeConversionTests.java index 49414367767c4..f2a0b84d2849a 100644 --- a/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/type/DataTypeConversionTests.java +++ b/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/type/DataTypeConversionTests.java @@ -15,14 +15,32 @@ import static org.elasticsearch.xpack.sql.expression.function.scalar.datetime.DateTimeTestUtils.dateTime; import static org.elasticsearch.xpack.sql.tree.Location.EMPTY; +import static org.elasticsearch.xpack.sql.type.DataType.BOOLEAN; +import static org.elasticsearch.xpack.sql.type.DataType.BYTE; +import static org.elasticsearch.xpack.sql.type.DataType.DATE; +import static org.elasticsearch.xpack.sql.type.DataType.DOUBLE; +import static org.elasticsearch.xpack.sql.type.DataType.FLOAT; +import static org.elasticsearch.xpack.sql.type.DataType.INTEGER; +import static org.elasticsearch.xpack.sql.type.DataType.IP; +import static org.elasticsearch.xpack.sql.type.DataType.KEYWORD; +import static org.elasticsearch.xpack.sql.type.DataType.LONG; +import static org.elasticsearch.xpack.sql.type.DataType.NULL; +import static org.elasticsearch.xpack.sql.type.DataType.SHORT; +import static org.elasticsearch.xpack.sql.type.DataType.TEXT; +import static org.elasticsearch.xpack.sql.type.DataType.UNSUPPORTED; +import static org.elasticsearch.xpack.sql.type.DataType.fromTypeName; +import static org.elasticsearch.xpack.sql.type.DataType.values; +import static org.elasticsearch.xpack.sql.type.DataTypeConversion.commonType; +import static org.elasticsearch.xpack.sql.type.DataTypeConversion.conversionFor; + public class DataTypeConversionTests extends ESTestCase { public void testConversionToString() { - Conversion conversion = DataTypeConversion.conversionFor(DataType.DOUBLE, DataType.KEYWORD); + Conversion conversion = conversionFor(DOUBLE, KEYWORD); assertNull(conversion.convert(null)); assertEquals("10.0", conversion.convert(10.0)); - conversion = DataTypeConversion.conversionFor(DataType.DATE, DataType.KEYWORD); + conversion = conversionFor(DATE, KEYWORD); assertNull(conversion.convert(null)); assertEquals("1970-01-01T00:00:00.000Z", conversion.convert(dateTime(0))); } @@ -31,9 +49,9 @@ public void testConversionToString() { * Test conversion to long. */ public void testConversionToLong() { - DataType to = DataType.LONG; + DataType to = LONG; { - Conversion conversion = DataTypeConversion.conversionFor(DataType.DOUBLE, to); + Conversion conversion = conversionFor(DOUBLE, to); assertNull(conversion.convert(null)); assertEquals(10L, conversion.convert(10.0)); assertEquals(10L, conversion.convert(10.1)); @@ -42,18 +60,18 @@ public void testConversionToLong() { assertEquals("[" + Double.MAX_VALUE + "] out of [Long] range", e.getMessage()); } { - Conversion conversion = DataTypeConversion.conversionFor(DataType.INTEGER, to); + Conversion conversion = conversionFor(INTEGER, to); assertNull(conversion.convert(null)); assertEquals(10L, conversion.convert(10)); assertEquals(-134L, conversion.convert(-134)); } { - Conversion conversion = DataTypeConversion.conversionFor(DataType.BOOLEAN, to); + Conversion conversion = conversionFor(BOOLEAN, to); assertNull(conversion.convert(null)); assertEquals(1L, conversion.convert(true)); assertEquals(0L, conversion.convert(false)); } - Conversion conversion = DataTypeConversion.conversionFor(DataType.KEYWORD, to); + Conversion conversion = conversionFor(KEYWORD, to); assertNull(conversion.convert(null)); assertEquals(1L, conversion.convert("1")); assertEquals(0L, conversion.convert("-0")); @@ -62,9 +80,9 @@ public void testConversionToLong() { } public void testConversionToDate() { - DataType to = DataType.DATE; + DataType to = DATE; { - Conversion conversion = DataTypeConversion.conversionFor(DataType.DOUBLE, to); + Conversion conversion = conversionFor(DOUBLE, to); assertNull(conversion.convert(null)); assertEquals(dateTime(10L), conversion.convert(10.0)); assertEquals(dateTime(10L), conversion.convert(10.1)); @@ -73,18 +91,18 @@ public void testConversionToDate() { assertEquals("[" + Double.MAX_VALUE + "] out of [Long] range", e.getMessage()); } { - Conversion conversion = DataTypeConversion.conversionFor(DataType.INTEGER, to); + Conversion conversion = conversionFor(INTEGER, to); assertNull(conversion.convert(null)); assertEquals(dateTime(10L), conversion.convert(10)); assertEquals(dateTime(-134L), conversion.convert(-134)); } { - Conversion conversion = DataTypeConversion.conversionFor(DataType.BOOLEAN, to); + Conversion conversion = conversionFor(BOOLEAN, to); assertNull(conversion.convert(null)); assertEquals(dateTime(1), conversion.convert(true)); assertEquals(dateTime(0), conversion.convert(false)); } - Conversion conversion = DataTypeConversion.conversionFor(DataType.KEYWORD, to); + Conversion conversion = conversionFor(KEYWORD, to); assertNull(conversion.convert(null)); assertEquals(dateTime(1000L), conversion.convert("1970-01-01T00:00:01Z")); @@ -93,8 +111,8 @@ public void testConversionToDate() { // double check back and forth conversion ZonedDateTime dt = ZonedDateTime.now(DateUtils.UTC); - Conversion forward = DataTypeConversion.conversionFor(DataType.DATE, DataType.KEYWORD); - Conversion back = DataTypeConversion.conversionFor(DataType.KEYWORD, DataType.DATE); + Conversion forward = conversionFor(DATE, KEYWORD); + Conversion back = conversionFor(KEYWORD, DATE); assertEquals(dt, back.convert(forward.convert(dt))); Exception e = expectThrows(SqlIllegalArgumentException.class, () -> conversion.convert("0xff")); assertEquals("cannot cast [0xff] to [Date]:Invalid format: \"0xff\" is malformed at \"xff\"", e.getMessage()); @@ -102,26 +120,26 @@ public void testConversionToDate() { public void testConversionToDouble() { { - Conversion conversion = DataTypeConversion.conversionFor(DataType.FLOAT, DataType.DOUBLE); + Conversion conversion = conversionFor(FLOAT, DOUBLE); assertNull(conversion.convert(null)); assertEquals(10.0, (double) conversion.convert(10.0f), 0.00001); assertEquals(10.1, (double) conversion.convert(10.1f), 0.00001); assertEquals(10.6, (double) conversion.convert(10.6f), 0.00001); } { - Conversion conversion = DataTypeConversion.conversionFor(DataType.INTEGER, DataType.DOUBLE); + Conversion conversion = conversionFor(INTEGER, DOUBLE); assertNull(conversion.convert(null)); assertEquals(10.0, (double) conversion.convert(10), 0.00001); assertEquals(-134.0, (double) conversion.convert(-134), 0.00001); } { - Conversion conversion = DataTypeConversion.conversionFor(DataType.BOOLEAN, DataType.DOUBLE); + Conversion conversion = conversionFor(BOOLEAN, DOUBLE); assertNull(conversion.convert(null)); assertEquals(1.0, (double) conversion.convert(true), 0); assertEquals(0.0, (double) conversion.convert(false), 0); } { - Conversion conversion = DataTypeConversion.conversionFor(DataType.KEYWORD, DataType.DOUBLE); + Conversion conversion = conversionFor(KEYWORD, DOUBLE); assertNull(conversion.convert(null)); assertEquals(1.0, (double) conversion.convert("1"), 0); assertEquals(0.0, (double) conversion.convert("-0"), 0); @@ -133,35 +151,35 @@ public void testConversionToDouble() { public void testConversionToBoolean() { { - Conversion conversion = DataTypeConversion.conversionFor(DataType.FLOAT, DataType.BOOLEAN); + Conversion conversion = conversionFor(FLOAT, BOOLEAN); assertNull(conversion.convert(null)); assertEquals(true, conversion.convert(10.0f)); assertEquals(true, conversion.convert(-10.0f)); assertEquals(false, conversion.convert(0.0f)); } { - Conversion conversion = DataTypeConversion.conversionFor(DataType.INTEGER, DataType.BOOLEAN); + Conversion conversion = conversionFor(INTEGER, BOOLEAN); assertNull(conversion.convert(null)); assertEquals(true, conversion.convert(10)); assertEquals(true, conversion.convert(-10)); assertEquals(false, conversion.convert(0)); } { - Conversion conversion = DataTypeConversion.conversionFor(DataType.LONG, DataType.BOOLEAN); + Conversion conversion = conversionFor(LONG, BOOLEAN); assertNull(conversion.convert(null)); assertEquals(true, conversion.convert(10L)); assertEquals(true, conversion.convert(-10L)); assertEquals(false, conversion.convert(0L)); } { - Conversion conversion = DataTypeConversion.conversionFor(DataType.DOUBLE, DataType.BOOLEAN); + Conversion conversion = conversionFor(DOUBLE, BOOLEAN); assertNull(conversion.convert(null)); assertEquals(true, conversion.convert(10.0d)); assertEquals(true, conversion.convert(-10.0d)); assertEquals(false, conversion.convert(0.0d)); } { - Conversion conversion = DataTypeConversion.conversionFor(DataType.KEYWORD, DataType.BOOLEAN); + Conversion conversion = conversionFor(KEYWORD, BOOLEAN); assertNull(conversion.convert(null)); // We only handled upper and lower case true and false assertEquals(true, conversion.convert("true")); @@ -186,7 +204,7 @@ public void testConversionToBoolean() { public void testConversionToInt() { { - Conversion conversion = DataTypeConversion.conversionFor(DataType.DOUBLE, DataType.INTEGER); + Conversion conversion = conversionFor(DOUBLE, INTEGER); assertNull(conversion.convert(null)); assertEquals(10, conversion.convert(10.0)); assertEquals(10, conversion.convert(10.1)); @@ -198,7 +216,7 @@ public void testConversionToInt() { public void testConversionToShort() { { - Conversion conversion = DataTypeConversion.conversionFor(DataType.DOUBLE, DataType.SHORT); + Conversion conversion = conversionFor(DOUBLE, SHORT); assertNull(conversion.convert(null)); assertEquals((short) 10, conversion.convert(10.0)); assertEquals((short) 10, conversion.convert(10.1)); @@ -210,7 +228,7 @@ public void testConversionToShort() { public void testConversionToByte() { { - Conversion conversion = DataTypeConversion.conversionFor(DataType.DOUBLE, DataType.BYTE); + Conversion conversion = conversionFor(DOUBLE, BYTE); assertNull(conversion.convert(null)); assertEquals((byte) 10, conversion.convert(10.0)); assertEquals((byte) 10, conversion.convert(10.1)); @@ -221,51 +239,51 @@ public void testConversionToByte() { } public void testConversionToNull() { - Conversion conversion = DataTypeConversion.conversionFor(DataType.DOUBLE, DataType.NULL); + Conversion conversion = conversionFor(DOUBLE, NULL); assertNull(conversion.convert(null)); assertNull(conversion.convert(10.0)); } public void testConversionFromNull() { - Conversion conversion = DataTypeConversion.conversionFor(DataType.NULL, DataType.INTEGER); + Conversion conversion = conversionFor(NULL, INTEGER); assertNull(conversion.convert(null)); assertNull(conversion.convert(10)); } public void testConversionToIdentity() { - Conversion conversion = DataTypeConversion.conversionFor(DataType.INTEGER, DataType.INTEGER); + Conversion conversion = conversionFor(INTEGER, INTEGER); assertNull(conversion.convert(null)); assertEquals(10, conversion.convert(10)); } public void testCommonType() { - assertEquals(DataType.BOOLEAN, DataTypeConversion.commonType(DataType.BOOLEAN, DataType.NULL)); - assertEquals(DataType.BOOLEAN, DataTypeConversion.commonType(DataType.NULL, DataType.BOOLEAN)); - assertEquals(DataType.BOOLEAN, DataTypeConversion.commonType(DataType.BOOLEAN, DataType.BOOLEAN)); - assertEquals(DataType.NULL, DataTypeConversion.commonType(DataType.NULL, DataType.NULL)); - assertEquals(DataType.INTEGER, DataTypeConversion.commonType(DataType.INTEGER, DataType.KEYWORD)); - assertEquals(DataType.LONG, DataTypeConversion.commonType(DataType.TEXT, DataType.LONG)); - assertEquals(null, DataTypeConversion.commonType(DataType.TEXT, DataType.KEYWORD)); - assertEquals(DataType.SHORT, DataTypeConversion.commonType(DataType.SHORT, DataType.BYTE)); - assertEquals(DataType.FLOAT, DataTypeConversion.commonType(DataType.BYTE, DataType.FLOAT)); - assertEquals(DataType.FLOAT, DataTypeConversion.commonType(DataType.FLOAT, DataType.INTEGER)); - assertEquals(DataType.DOUBLE, DataTypeConversion.commonType(DataType.DOUBLE, DataType.FLOAT)); + assertEquals(BOOLEAN, commonType(BOOLEAN, NULL)); + assertEquals(BOOLEAN, commonType(NULL, BOOLEAN)); + assertEquals(BOOLEAN, commonType(BOOLEAN, BOOLEAN)); + assertEquals(NULL, commonType(NULL, NULL)); + assertEquals(INTEGER, commonType(INTEGER, KEYWORD)); + assertEquals(LONG, commonType(TEXT, LONG)); + assertEquals(null, commonType(TEXT, KEYWORD)); + assertEquals(SHORT, commonType(SHORT, BYTE)); + assertEquals(FLOAT, commonType(BYTE, FLOAT)); + assertEquals(FLOAT, commonType(FLOAT, INTEGER)); + assertEquals(DOUBLE, commonType(DOUBLE, FLOAT)); } public void testEsDataTypes() { - for (DataType type : DataType.values()) { - assertEquals(type, DataType.fromEsType(type.esType)); + for (DataType type : values()) { + assertEquals(type, fromTypeName(type.esType)); } } public void testConversionToUnsupported() { Exception e = expectThrows(SqlIllegalArgumentException.class, - () -> DataTypeConversion.conversionFor(DataType.INTEGER, DataType.UNSUPPORTED)); + () -> conversionFor(INTEGER, UNSUPPORTED)); assertEquals("cannot convert from [INTEGER] to [UNSUPPORTED]", e.getMessage()); } public void testStringToIp() { - Conversion conversion = DataTypeConversion.conversionFor(DataType.KEYWORD, DataType.IP); + Conversion conversion = conversionFor(KEYWORD, IP); assertNull(conversion.convert(null)); assertEquals("192.168.1.1", conversion.convert("192.168.1.1")); Exception e = expectThrows(SqlIllegalArgumentException.class, () -> conversion.convert("10.1.1.300")); @@ -273,9 +291,9 @@ public void testStringToIp() { } public void testIpToString() { - Conversion ipToString = DataTypeConversion.conversionFor(DataType.IP, DataType.KEYWORD); - assertEquals("10.0.0.1", ipToString.convert(new Literal(EMPTY, "10.0.0.1", DataType.IP))); - Conversion stringToIp = DataTypeConversion.conversionFor(DataType.KEYWORD, DataType.IP); + Conversion ipToString = conversionFor(IP, KEYWORD); + assertEquals("10.0.0.1", ipToString.convert(new Literal(EMPTY, "10.0.0.1", IP))); + Conversion stringToIp = conversionFor(KEYWORD, IP); assertEquals("10.0.0.1", ipToString.convert(stringToIp.convert(Literal.of(EMPTY, "10.0.0.1")))); } } From ec43c4182bc92418da47a2c9f19fe090c9cf7cbc Mon Sep 17 00:00:00 2001 From: Costin Leau Date: Tue, 20 Nov 2018 23:51:29 +0200 Subject: [PATCH 5/6] Polishing --- .../sql/proto/type/ExtendedJDBCType.java | 52 --------- .../xpack/sql/type/DataTypeConversion.java | 7 -- .../xpack/sql/type/DataTypes.java | 5 - .../expression/literal/IntervalsTests.java | 110 ++++++++---------- .../xpack/sql/parser/ExpressionTests.java | 4 +- .../xpack/sql/type/DataTypesTests.java | 19 +-- 6 files changed, 62 insertions(+), 135 deletions(-) delete mode 100644 x-pack/plugin/sql/sql-proto/src/main/java/org/elasticsearch/xpack/sql/proto/type/ExtendedJDBCType.java diff --git a/x-pack/plugin/sql/sql-proto/src/main/java/org/elasticsearch/xpack/sql/proto/type/ExtendedJDBCType.java b/x-pack/plugin/sql/sql-proto/src/main/java/org/elasticsearch/xpack/sql/proto/type/ExtendedJDBCType.java deleted file mode 100644 index daa39fcfa2fac..0000000000000 --- a/x-pack/plugin/sql/sql-proto/src/main/java/org/elasticsearch/xpack/sql/proto/type/ExtendedJDBCType.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -package org.elasticsearch.xpack.sql.proto.type; - -import java.sql.JDBCType; -import java.sql.SQLType; -import java.sql.Types; - -/** - * Provides ODBC-based codes for the missing SQL data types from {@link Types}/{@link JDBCType}. - */ -enum ExtendedJDBCType implements SQLType { - - INTERVAL_YEAR(101), - INTERVAL_MONTH(102), - INTERVAL_DAY(103), - INTERVAL_HOUR(104), - INTERVAL_MINUTE(105), - INTERVAL_SECOND(106), - INTERVAL_YEAR_TO_MONTH(107), - INTERVAL_DAY_TO_HOUR(108), - INTERVAL_DAY_TO_MINUTE(109), - INTERVAL_DAY_TO_SECOND(110), - INTERVAL_HOUR_TO_MINUTE(111), - INTERVAL_HOUR_TO_SECOND(112), - INTERVAL_MINUTE_TO_SECOND(113); - - private final Integer type; - - ExtendedJDBCType(Integer type) { - this.type = type; - } - - @Override - public String getName() { - return name(); - } - - @Override - public String getVendor() { - return "org.elasticsearch"; - } - - @Override - public Integer getVendorTypeNumber() { - return type; - } -} diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/type/DataTypeConversion.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/type/DataTypeConversion.java index 3c74e9f05cc92..e1724826a6840 100644 --- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/type/DataTypeConversion.java +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/type/DataTypeConversion.java @@ -83,19 +83,12 @@ public static DataType commonType(DataType left, DataType right) { if (right == DATE) { return right; } - // single unit intervals and numbers - if (DataTypes.isSingularInterval(left) && right.isInteger()) { - return left; - } } if (DataTypes.isInterval(right)) { if (left == DATE) { return left; } - if (DataTypes.isSingularInterval(right) && left.isInteger()) { - return right; - } } // none found diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/type/DataTypes.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/type/DataTypes.java index 601a428f4233c..b865f541634b1 100644 --- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/type/DataTypes.java +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/type/DataTypes.java @@ -91,11 +91,6 @@ public static boolean isInterval(DataType type) { return ordinal >= INTERVAL_YEAR.ordinal() && ordinal <= INTERVAL_MINUTE_TO_SECOND.ordinal(); } - public static boolean isSingularInterval(DataType type) { - int ordinal = type.ordinal(); - return ordinal >= INTERVAL_YEAR.ordinal() && ordinal <= INTERVAL_SECOND.ordinal(); - } - // return the compatible interval between the two - it is assumed the types are intervals // YEAR and MONTH -> YEAR_TO_MONTH // DAY... SECOND -> DAY_TIME diff --git a/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/expression/literal/IntervalsTests.java b/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/expression/literal/IntervalsTests.java index 1eab0f8483ffa..521f7cb26e18f 100644 --- a/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/expression/literal/IntervalsTests.java +++ b/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/expression/literal/IntervalsTests.java @@ -6,6 +6,8 @@ package org.elasticsearch.xpack.sql.expression.literal; +import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; + import org.elasticsearch.test.ESTestCase; import org.elasticsearch.xpack.sql.expression.literal.Intervals.TimeUnit; import org.elasticsearch.xpack.sql.parser.ParsingException; @@ -14,8 +16,10 @@ import java.time.Period; import java.time.temporal.TemporalAmount; import java.util.Locale; +import java.util.stream.Stream; import static java.lang.String.format; +import static java.util.stream.Collectors.toList; import static org.elasticsearch.xpack.sql.expression.literal.Intervals.intervalType; import static org.elasticsearch.xpack.sql.expression.literal.Intervals.of; import static org.elasticsearch.xpack.sql.expression.literal.Intervals.parseInterval; @@ -36,156 +40,148 @@ public class IntervalsTests extends ESTestCase { + @ParametersFactory + public static Iterable params() { + return Stream.of("+", "-", "").map(s -> new Object[] { s }).collect(toList()); + } + + private String sign; + + public IntervalsTests(String sign) { + this.sign = sign; + } + public void testYearInterval() throws Exception { - String randomSign = randomSign(); int random = randomNonNegativeInt(); - TemporalAmount amount = parseInterval(EMPTY, randomSign + random, INTERVAL_YEAR); - assertEquals(maybeNegate(randomSign, Period.ofYears(random)), amount); + TemporalAmount amount = parseInterval(EMPTY, sign + random, INTERVAL_YEAR); + assertEquals(maybeNegate(sign, Period.ofYears(random)), amount); } public void testMonthInterval() throws Exception { - String randomSign = randomSign(); int random = randomNonNegativeInt(); - TemporalAmount amount = parseInterval(EMPTY, randomSign + random, INTERVAL_MONTH); - assertEquals(maybeNegate(randomSign, Period.ofMonths(random)), amount); + TemporalAmount amount = parseInterval(EMPTY, sign + random, INTERVAL_MONTH); + assertEquals(maybeNegate(sign, Period.ofMonths(random)), amount); } public void testDayInterval() throws Exception { - String randomSign = randomSign(); int random = randomNonNegativeInt(); - TemporalAmount amount = parseInterval(EMPTY, randomSign + random, INTERVAL_DAY); - assertEquals(maybeNegate(randomSign, Duration.ofDays(random)), amount); + TemporalAmount amount = parseInterval(EMPTY, sign + random, INTERVAL_DAY); + assertEquals(maybeNegate(sign, Duration.ofDays(random)), amount); } public void testHourInterval() throws Exception { - String randomSign = randomSign(); int random = randomNonNegativeInt(); - TemporalAmount amount = parseInterval(EMPTY, randomSign + random, INTERVAL_HOUR); - assertEquals(maybeNegate(randomSign, Duration.ofHours(random)), amount); + TemporalAmount amount = parseInterval(EMPTY, sign + random, INTERVAL_HOUR); + assertEquals(maybeNegate(sign, Duration.ofHours(random)), amount); } public void testMinuteInterval() throws Exception { - String randomSign = randomSign(); int random = randomNonNegativeInt(); - TemporalAmount amount = parseInterval(EMPTY, randomSign + random, INTERVAL_MINUTE); - assertEquals(maybeNegate(randomSign, Duration.ofMinutes(random)), amount); + TemporalAmount amount = parseInterval(EMPTY, sign + random, INTERVAL_MINUTE); + assertEquals(maybeNegate(sign, Duration.ofMinutes(random)), amount); } public void testSecondInterval() throws Exception { - String randomSign = randomSign(); int randomSeconds = randomNonNegativeInt(); - int randomMillis = randomInt(999999999); - String value = format(Locale.ROOT, "%s%d.%d", randomSign, randomSeconds, randomMillis); + int randomMillis = randomBoolean() ? (randomBoolean() ? 0 : 999999999) : randomInt(999999999); + String value = format(Locale.ROOT, "%s%d.%d", sign, randomSeconds, randomMillis); TemporalAmount amount = parseInterval(EMPTY, value, INTERVAL_SECOND); - assertEquals(maybeNegate(randomSign, Duration.ofSeconds(randomSeconds).plusMillis(randomMillis)), amount); + assertEquals(maybeNegate(sign, Duration.ofSeconds(randomSeconds).plusMillis(randomMillis)), amount); } public void testYearToMonth() throws Exception { - String randomSign = randomSign(); int randomYear = randomNonNegativeInt(); int randomMonth = randomInt(11); - String value = format(Locale.ROOT, "%s%d-%d", randomSign, randomYear, randomMonth); + String value = format(Locale.ROOT, "%s%d-%d", sign, randomYear, randomMonth); TemporalAmount amount = parseInterval(EMPTY, value, INTERVAL_YEAR_TO_MONTH); - assertEquals(maybeNegate(randomSign, Period.ofYears(randomYear).plusMonths(randomMonth)), amount); + assertEquals(maybeNegate(sign, Period.ofYears(randomYear).plusMonths(randomMonth)), amount); } public void testDayToHour() throws Exception { - String randomSign = randomSign(); int randomDay = randomNonNegativeInt(); int randomHour = randomInt(23); - String value = format(Locale.ROOT, "%s%d %d", randomSign, randomDay, randomHour); + String value = format(Locale.ROOT, "%s%d %d", sign, randomDay, randomHour); TemporalAmount amount = parseInterval(EMPTY, value, INTERVAL_DAY_TO_HOUR); - assertEquals(maybeNegate(randomSign, Duration.ofDays(randomDay).plusHours(randomHour)), amount); + assertEquals(maybeNegate(sign, Duration.ofDays(randomDay).plusHours(randomHour)), amount); } public void testDayToMinute() throws Exception { - String randomSign = randomSign(); int randomDay = randomNonNegativeInt(); int randomHour = randomInt(23); int randomMinute = randomInt(59); - String value = format(Locale.ROOT, "%s%d %d:%d", randomSign, randomDay, randomHour, randomMinute); + String value = format(Locale.ROOT, "%s%d %d:%d", sign, randomDay, randomHour, randomMinute); TemporalAmount amount = parseInterval(EMPTY, value, INTERVAL_DAY_TO_MINUTE); - assertEquals(maybeNegate(randomSign, Duration.ofDays(randomDay).plusHours(randomHour).plusMinutes(randomMinute)), amount); + assertEquals(maybeNegate(sign, Duration.ofDays(randomDay).plusHours(randomHour).plusMinutes(randomMinute)), amount); } public void testDayToSecond() throws Exception { - String randomSign = randomSign(); int randomDay = randomNonNegativeInt(); int randomHour = randomInt(23); int randomMinute = randomInt(59); int randomSecond = randomInt(59); int randomMilli = randomInt(999999999); - String value = format(Locale.ROOT, "%s%d %d:%d:%d.%d", randomSign, randomDay, randomHour, randomMinute, randomSecond, - randomMilli); + String value = format(Locale.ROOT, "%s%d %d:%d:%d.%d", sign, randomDay, randomHour, randomMinute, randomSecond, randomMilli); TemporalAmount amount = parseInterval(EMPTY, value, INTERVAL_DAY_TO_SECOND); - assertEquals(maybeNegate(randomSign, Duration.ofDays(randomDay).plusHours(randomHour).plusMinutes(randomMinute) + assertEquals(maybeNegate(sign, Duration.ofDays(randomDay).plusHours(randomHour).plusMinutes(randomMinute) .plusSeconds(randomSecond).plusMillis(randomMilli)), amount); } public void testHourToMinute() throws Exception { - String randomSign = randomSign(); int randomHour = randomNonNegativeInt(); int randomMinute = randomInt(59); - String value = format(Locale.ROOT, "%s%d:%d", randomSign, randomHour, randomMinute); + String value = format(Locale.ROOT, "%s%d:%d", sign, randomHour, randomMinute); TemporalAmount amount = parseInterval(EMPTY, value, INTERVAL_HOUR_TO_MINUTE); - assertEquals(maybeNegate(randomSign, Duration.ofHours(randomHour).plusMinutes(randomMinute)), amount); + assertEquals(maybeNegate(sign, Duration.ofHours(randomHour).plusMinutes(randomMinute)), amount); } public void testHourToSecond() throws Exception { - String randomSign = randomSign(); int randomHour = randomNonNegativeInt(); int randomMinute = randomInt(59); int randomSecond = randomInt(59); int randomMilli = randomInt(999999999); - String value = format(Locale.ROOT, "%s%d:%d:%d.%d", randomSign, randomHour, randomMinute, randomSecond, randomMilli); + String value = format(Locale.ROOT, "%s%d:%d:%d.%d", sign, randomHour, randomMinute, randomSecond, randomMilli); TemporalAmount amount = parseInterval(EMPTY, value, INTERVAL_HOUR_TO_SECOND); - assertEquals(maybeNegate(randomSign, + assertEquals(maybeNegate(sign, Duration.ofHours(randomHour).plusMinutes(randomMinute).plusSeconds(randomSecond).plusMillis(randomMilli)), amount); } public void testMinuteToSecond() throws Exception { - String randomSign = randomSign(); int randomMinute = randomNonNegativeInt(); int randomSecond = randomInt(59); int randomMilli = randomInt(999999999); - String value = format(Locale.ROOT, "%s%d:%d.%d", randomSign, randomMinute, randomSecond, randomMilli); + String value = format(Locale.ROOT, "%s%d:%d.%d", sign, randomMinute, randomSecond, randomMilli); TemporalAmount amount = parseInterval(EMPTY, value, INTERVAL_MINUTE_TO_SECOND); - assertEquals(maybeNegate(randomSign, Duration.ofMinutes(randomMinute).plusSeconds(randomSecond).plusMillis(randomMilli)), amount); + assertEquals(maybeNegate(sign, Duration.ofMinutes(randomMinute).plusSeconds(randomSecond).plusMillis(randomMilli)), amount); } // validation public void testYearToMonthTooBig() throws Exception { - String randomSign = randomSign(); int randomYear = randomNonNegativeInt(); int randomTooBig = randomIntBetween(12, 9999); - String value = format(Locale.ROOT, "%s%d-%d", randomSign, randomYear, randomTooBig); - ParsingException pe = expectThrows(ParsingException.class, - () -> parseInterval(EMPTY, value, INTERVAL_YEAR_TO_MONTH)); + String value = format(Locale.ROOT, "%s%d-%d", sign, randomYear, randomTooBig); + ParsingException pe = expectThrows(ParsingException.class, () -> parseInterval(EMPTY, value, INTERVAL_YEAR_TO_MONTH)); assertEquals("line -1:0: Invalid [INTERVAL YEAR TO MONTH] value [" + value + "]: [MONTH] unit has illegal value [" + randomTooBig + "], expected a positive number up to [11]", pe.getMessage()); } public void testMillisTooBig() throws Exception { - String randomSign = randomSign(); int randomSeconds = randomNonNegativeInt(); int millisTooLarge = 1234567890; - String value = format(Locale.ROOT, "%s%d.%d", randomSign, randomSeconds, millisTooLarge); - ParsingException pe = expectThrows(ParsingException.class, - () -> parseInterval(EMPTY, value, INTERVAL_SECOND)); + String value = format(Locale.ROOT, "%s%d.%d", sign, randomSeconds, millisTooLarge); + ParsingException pe = expectThrows(ParsingException.class, () -> parseInterval(EMPTY, value, INTERVAL_SECOND)); assertEquals("line -1:0: Invalid [INTERVAL SECOND] value [" + value + "]: [MILLISECOND] unit has illegal value [" + millisTooLarge + "], expected a positive number up to [999999999]", pe.getMessage()); } public void testDayToMinuteTooBig() throws Exception { - String randomSign = randomSign(); int randomDay = randomNonNegativeInt(); int randomHour = randomIntBetween(24, 9999); int randomMinute = randomInt(59); - String value = format(Locale.ROOT, "%s%d %d:%d", randomSign, randomDay, randomHour, randomMinute); + String value = format(Locale.ROOT, "%s%d %d:%d", sign, randomDay, randomHour, randomMinute); ParsingException pe = expectThrows(ParsingException.class, () -> parseInterval(EMPTY, value, INTERVAL_DAY_TO_MINUTE)); assertEquals("line -1:0: Invalid [INTERVAL DAY TO MINUTE] value [" + value + "]: [HOUR] unit has illegal value [" + randomHour + "], expected a positive number up to [23]", pe.getMessage()); @@ -199,8 +195,7 @@ public void testExtraCharLeading() throws Exception { public void testExtraCharTrailing() throws Exception { String value = "123x"; - ParsingException pe = expectThrows(ParsingException.class, - () -> parseInterval(EMPTY, value, INTERVAL_YEAR)); + ParsingException pe = expectThrows(ParsingException.class, () -> parseInterval(EMPTY, value, INTERVAL_YEAR)); assertEquals("line -1:0: Invalid [INTERVAL YEAR] value [123x]: unexpected trailing characters found [x]", pe.getMessage()); } @@ -225,12 +220,7 @@ private static int randomNonNegativeInt() { return random == Integer.MIN_VALUE ? 0 : Math.abs(random); } - //Maybe returns a sign, which might be + or -. - private static String randomSign() { - return randomBoolean() ? (randomBoolean() ? "+" : "-") : ""; - } - - private Object maybeNegate(String randomSign, TemporalAmount interval) { - return "-".equals(randomSign) ? Intervals.negate(interval) : interval; + private Object maybeNegate(String sign, TemporalAmount interval) { + return "-".equals(sign) ? Intervals.negate(interval) : interval; } } \ No newline at end of file diff --git a/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/parser/ExpressionTests.java b/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/parser/ExpressionTests.java index 842c23926e094..cb933b1fdd876 100644 --- a/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/parser/ExpressionTests.java +++ b/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/parser/ExpressionTests.java @@ -79,9 +79,9 @@ public void testLiteralLong() { } public void testLiteralLongNegative() { - Expression lt = parser.createExpression(String.valueOf(-Long.MAX_VALUE)); + Expression lt = parser.createExpression(String.valueOf(Long.MIN_VALUE)); assertTrue(lt.foldable()); - assertEquals(-Long.MAX_VALUE, lt.fold()); + assertEquals(Long.MIN_VALUE, lt.fold()); assertEquals(DataType.LONG, lt.dataType()); } diff --git a/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/type/DataTypesTests.java b/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/type/DataTypesTests.java index a082f025b0c6a..ff6bf4611c827 100644 --- a/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/type/DataTypesTests.java +++ b/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/type/DataTypesTests.java @@ -28,7 +28,6 @@ import static org.elasticsearch.xpack.sql.type.DataType.LONG; import static org.elasticsearch.xpack.sql.type.DataTypes.compatibleInterval; import static org.elasticsearch.xpack.sql.type.DataTypes.isInterval; -import static org.elasticsearch.xpack.sql.type.DataTypes.isSingularInterval; import static org.elasticsearch.xpack.sql.type.DataTypes.metaSqlDataType; import static org.elasticsearch.xpack.sql.type.DataTypes.metaSqlDateTimeSub; import static org.elasticsearch.xpack.sql.type.DataTypes.metaSqlMaximumScale; @@ -72,14 +71,9 @@ public void testMetaRadix() { // type checks public void testIsInterval() throws Exception { - assertTrue(isInterval(randomFrom(EnumSet.range(INTERVAL_YEAR, INTERVAL_MINUTE_TO_SECOND)))); - } - - public void testIsSingularInterval() throws Exception { - assertTrue( - isSingularInterval(randomFrom(EnumSet.of(INTERVAL_YEAR, INTERVAL_DAY, INTERVAL_HOUR, INTERVAL_MINUTE, INTERVAL_SECOND)))); - assertFalse(isSingularInterval(randomFrom(EnumSet.of(INTERVAL_YEAR_TO_MONTH, INTERVAL_DAY_TO_HOUR, INTERVAL_DAY_TO_MINUTE, - INTERVAL_DAY_TO_SECOND, INTERVAL_HOUR_TO_MINUTE, INTERVAL_HOUR_TO_SECOND, INTERVAL_MINUTE_TO_SECOND)))); + for (DataType dataType : EnumSet.range(INTERVAL_YEAR, INTERVAL_MINUTE_TO_SECOND)) { + assertTrue(isInterval(dataType)); + } } public void testIntervalCompatibilityYearMonth() throws Exception { @@ -107,6 +101,13 @@ public void testIntervalCompatibilityDayTime() throws Exception { assertEquals(INTERVAL_MINUTE_TO_SECOND, compatibleInterval(INTERVAL_SECOND, INTERVAL_MINUTE)); } + public void testIncompatibleInterval() throws Exception { + assertNull(compatibleInterval(INTERVAL_YEAR, INTERVAL_SECOND)); + assertNull(compatibleInterval(INTERVAL_YEAR, INTERVAL_DAY_TO_HOUR)); + assertNull(compatibleInterval(INTERVAL_HOUR, INTERVAL_MONTH)); + assertNull(compatibleInterval(INTERVAL_MINUTE_TO_SECOND, INTERVAL_MONTH)); + } + private DataType randomDataTypeNoDate() { return randomValueOtherThan(DataType.DATE, () -> randomFrom(DataType.values())); } From 0e3b1820807c8750053bef2c1f2085f41786ced7 Mon Sep 17 00:00:00 2001 From: Costin Leau Date: Wed, 21 Nov 2018 12:24:23 +0200 Subject: [PATCH 6/6] Address feedback --- .../arithmetic/BinaryArithmeticProcessor.java | 6 ++++-- .../xpack/sql/type/DataTypeConversion.java | 3 --- .../arithmetic/BinaryArithmeticTests.java | 16 ++++++++++++++-- 3 files changed, 18 insertions(+), 7 deletions(-) diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/predicate/operator/arithmetic/BinaryArithmeticProcessor.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/predicate/operator/arithmetic/BinaryArithmeticProcessor.java index 5d1a11e19310b..aa0c3e830b9e7 100644 --- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/predicate/operator/arithmetic/BinaryArithmeticProcessor.java +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/predicate/operator/arithmetic/BinaryArithmeticProcessor.java @@ -54,7 +54,8 @@ public enum BinaryArithmeticOperation implements PredicateBiFunction { if (l instanceof Number) { @@ -78,7 +79,8 @@ public enum BinaryArithmeticOperation implements PredicateBiFunction add(r, L(1))); + assertEquals("Cannot compute [+] between [IntervalDayTime] [Integer]", expect.getMessage()); + } + public void testSubYearMonthIntervals() throws Exception { Literal l = interval(Period.ofYears(1), INTERVAL_YEAR); Literal r = interval(Period.ofMonths(2), INTERVAL_MONTH); @@ -113,6 +119,12 @@ public void testSubYearMonthIntervalToDateIllegal() throws Exception { assertEquals("Cannot substract a date from an interval; do you mean the reverse?", ex.getMessage()); } + public void testSubNumberFromIntervalIllegal() throws Exception { + Literal r = interval(Duration.ofHours(2), INTERVAL_HOUR); + SqlIllegalArgumentException expect = expectThrows(SqlIllegalArgumentException.class, () -> sub(r, L(1))); + assertEquals("Cannot compute [-] between [IntervalDayTime] [Integer]", expect.getMessage()); + } + public void testSubDayTimeIntervalToDate() throws Exception { ZonedDateTime now = ZonedDateTime.now(DateUtils.UTC); Literal l = L(now); @@ -146,4 +158,4 @@ private static Literal interval(TemporalAmount value, DataType intervalType) { : new IntervalDayTime((Duration) value, intervalType); return Literal.of(EMPTY, i); } -} +} \ No newline at end of file