diff --git a/plugin/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/jdbc/JdbcPreparedStatement.java b/plugin/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/jdbc/JdbcPreparedStatement.java index 49a4b4fb43f..b0af977c4ec 100644 --- a/plugin/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/jdbc/JdbcPreparedStatement.java +++ b/plugin/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/jdbc/JdbcPreparedStatement.java @@ -28,6 +28,7 @@ import java.sql.Time; import java.sql.Timestamp; import java.sql.Types; import java.util.Calendar; +import java.util.Collections; class JdbcPreparedStatement extends JdbcStatement implements PreparedStatement { final PreparedQuery query; @@ -47,7 +48,7 @@ class JdbcPreparedStatement extends JdbcStatement implements PreparedStatement { @Override public ResultSet executeQuery() throws SQLException { checkOpen(); - initResultSet(query.assemble()); + initResultSet(query.sql(), query.params()); return rs; } @@ -60,7 +61,8 @@ class JdbcPreparedStatement extends JdbcStatement implements PreparedStatement { checkOpen(); if (parameterIndex < 0 || parameterIndex > query.paramCount()) { - throw new SQLException("Invalid parameter [ " + parameterIndex + "; needs to be between 1 and [" + query.paramCount() + "]"); + throw new SQLException("Invalid parameter index [ " + parameterIndex + "; needs to be between 1 and [" + query.paramCount() + + "]"); } query.setParam(parameterIndex, value, JDBCType.valueOf(type)); @@ -68,42 +70,42 @@ class JdbcPreparedStatement extends JdbcStatement implements PreparedStatement { @Override public void setNull(int parameterIndex, int sqlType) throws SQLException { - setParam(parameterIndex, "NULL", sqlType); + setParam(parameterIndex, null, sqlType); } @Override public void setBoolean(int parameterIndex, boolean x) throws SQLException { - setParam(parameterIndex, Boolean.toString(x), Types.BOOLEAN); + setParam(parameterIndex, x, Types.BOOLEAN); } @Override public void setByte(int parameterIndex, byte x) throws SQLException { - setParam(parameterIndex, Byte.toString(x), Types.TINYINT); + setParam(parameterIndex, x, Types.TINYINT); } @Override public void setShort(int parameterIndex, short x) throws SQLException { - setParam(parameterIndex, Short.toString(x), Types.SMALLINT); + setParam(parameterIndex, x, Types.SMALLINT); } @Override public void setInt(int parameterIndex, int x) throws SQLException { - setParam(parameterIndex, Integer.toString(x), Types.INTEGER); + setParam(parameterIndex, x, Types.INTEGER); } @Override public void setLong(int parameterIndex, long x) throws SQLException { - setParam(parameterIndex, Long.toString(x), Types.BIGINT); + setParam(parameterIndex, x, Types.BIGINT); } @Override public void setFloat(int parameterIndex, float x) throws SQLException { - setParam(parameterIndex, Float.toString(x), Types.REAL); + setParam(parameterIndex, x, Types.REAL); } @Override public void setDouble(int parameterIndex, double x) throws SQLException { - setParam(parameterIndex, Double.toString(x), Types.DOUBLE); + setParam(parameterIndex, x, Types.DOUBLE); } @Override @@ -113,7 +115,7 @@ class JdbcPreparedStatement extends JdbcStatement implements PreparedStatement { @Override public void setString(int parameterIndex, String x) throws SQLException { - setParam(parameterIndex, PreparedQuery.escapeString(x), Types.VARCHAR); + setParam(parameterIndex, x, Types.VARCHAR); } @Override diff --git a/plugin/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/jdbc/JdbcStatement.java b/plugin/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/jdbc/JdbcStatement.java index ae9a6621bee..34350dfb81e 100644 --- a/plugin/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/jdbc/JdbcStatement.java +++ b/plugin/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/jdbc/JdbcStatement.java @@ -7,6 +7,7 @@ package org.elasticsearch.xpack.sql.jdbc.jdbc; import org.elasticsearch.xpack.sql.jdbc.net.client.Cursor; import org.elasticsearch.xpack.sql.jdbc.net.client.RequestMeta; +import org.elasticsearch.xpack.sql.plugin.SqlTypedParamValue; import java.sql.Connection; import java.sql.ResultSet; @@ -14,6 +15,8 @@ import java.sql.SQLException; import java.sql.SQLFeatureNotSupportedException; import java.sql.SQLWarning; import java.sql.Statement; +import java.util.Collections; +import java.util.List; import java.util.concurrent.TimeUnit; class JdbcStatement implements Statement, JdbcWrapper { @@ -146,16 +149,16 @@ class JdbcStatement implements Statement, JdbcWrapper { @Override public boolean execute(String sql) throws SQLException { checkOpen(); - initResultSet(sql); + initResultSet(sql, Collections.emptyList()); return true; } // execute the query and handle the rs closing and initialization - protected void initResultSet(String sql) throws SQLException { + protected void initResultSet(String sql, List params) throws SQLException { // close previous result set closeResultSet(); - Cursor cursor = con.client.query(sql, requestMeta); + Cursor cursor = con.client.query(sql, params, requestMeta); rs = new JdbcResultSet(cfg, this, cursor); } diff --git a/plugin/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/jdbc/PreparedQuery.java b/plugin/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/jdbc/PreparedQuery.java index db16ba98a52..4aaf337f2b7 100644 --- a/plugin/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/jdbc/PreparedQuery.java +++ b/plugin/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/jdbc/PreparedQuery.java @@ -6,12 +6,14 @@ package org.elasticsearch.xpack.sql.jdbc.jdbc; import org.elasticsearch.xpack.sql.jdbc.JdbcSQLException; +import org.elasticsearch.xpack.sql.plugin.SqlTypedParamValue; +import org.elasticsearch.xpack.sql.type.DataType; import java.sql.JDBCType; import java.sql.SQLException; -import java.sql.SQLFeatureNotSupportedException; -import java.util.ArrayList; +import java.util.Arrays; import java.util.List; +import java.util.stream.Collectors; class PreparedQuery { @@ -25,12 +27,12 @@ class PreparedQuery { } } - private final List fragments; - final ParamInfo[] params; + private final String sql; + private final ParamInfo[] params; - PreparedQuery(List fragments) { - this.fragments = fragments; - this.params = new ParamInfo[fragments.size() - 1]; + private PreparedQuery(String sql, int paramCount) { + this.sql = sql; + this.params = new ParamInfo[paramCount]; clearParams(); } @@ -59,178 +61,29 @@ class PreparedQuery { } } - String assemble() { - StringBuilder sb = new StringBuilder(); - for (int i = 0; i < fragments.size(); i++) { - sb.append(fragments.get(i)); - if (i < params.length) { - // TODO: this needs converting - sb.append(params[i].value); - } - } + /** + * Returns the sql statement + */ + String sql() { + return sql; + } - return sb.toString(); + /** + * Returns the parameters if the SQL statement is parametrized + */ + List params() { + return Arrays.stream(this.params).map( + paramInfo -> new SqlTypedParamValue(paramInfo.value, DataType.fromJdbcType(paramInfo.type)) + ).collect(Collectors.toList()); } @Override public String toString() { - return assemble(); + return sql() + " " + params(); } - // Find the ? parameters for binding - // Additionally, throw away all JDBC escaping + // Creates a PreparedQuery static PreparedQuery prepare(String sql) throws SQLException { - int l = sql.length(); - - List fragments = new ArrayList<>(); - StringBuilder current = new StringBuilder(); - - for (int i = 0; i < l; i++) { - char c = sql.charAt(i); - - switch (c) { - // JDBC escape syntax - // https://db.apache.org/derby/docs/10.5/ref/rrefjdbc1020262.html - case '{': - jdbcEscape(); - break; - case '\'': - i = string(i, sql, current, c); - break; - case '"': - i = string(i, sql, current, c); - break; - case '?': - fragments.add(current.toString()); - current.setLength(0); - i++; - break; - case '-': - if (i + 1 < l && sql.charAt(i + 1) == '-') { - i = lineComment(i, sql, current); - } - else { - current.append(c); - } - break; - case '/': - if (i + 1 < l && sql.charAt(i + 1) == '*') { - i = multiLineComment(i, sql, current); - } - else { - current.append(c); - } - break; - - default: - current.append(c); - break; - } - } - - fragments.add(current.toString()); - - return new PreparedQuery(fragments); - } - - private static void jdbcEscape() throws SQLException { - throw new SQLFeatureNotSupportedException("JDBC escaping not supported yet"); - } - - - private static int lineComment(int i, String sql, StringBuilder current) { - for (; i < sql.length(); i++) { - char c = sql.charAt(i); - if (c != '\n' && c != '\r') { - current.append(c); - } - else { - return i; - } - } - return i; - } - - private static int multiLineComment(int i, String sql, StringBuilder current) throws JdbcSQLException { - int block = 1; - - for (; i < sql.length() - 1; i++) { - char c = sql.charAt(i); - if (c == '/' && sql.charAt(i + 1) == '*') { - current.append(c); - current.append(sql.charAt(++i)); - block++; - } - else if (c == '*' && sql.charAt(i + 1) == '/') { - current.append(c); - current.append(sql.charAt(++i)); - block--; - } - else { - current.append(c); - } - if (block == 0) { - return i; - } - } - throw new JdbcSQLException("Cannot parse given sql; unclosed /* comment"); - } - - private static int string(int i, String sql, StringBuilder current, char q) throws JdbcSQLException { - current.append(sql.charAt(i++)); - for (; i < sql.length(); i++) { - char c = sql.charAt(i); - if (c == q) { - current.append(c); - // double quotes mean escaping - if (sql.charAt(i + 1) == q) { - current.append(sql.charAt(++i)); - } - else { - return i; - } - } - else { - current.append(c); - } - } - throw new JdbcSQLException("Cannot parse given sql; unclosed string"); - } - - static String escapeString(String s) { - if (s == null) { - return "NULL"; - } - - if (s.contains("'") ) { - s = escapeString(s, '\''); - } - if (s.contains("\"")) { - s = escapeString(s, '"'); - } - - // add quotes - return "'" + s + "'"; - } - - private static String escapeString(String s, char sq) { - StringBuilder sb = new StringBuilder(); - - // escape individual single quotes - for (int i = 0; i < s.length(); i++) { - char c = s.charAt(i); - - // needs escaping - if (c == sq) { - // check if it's already escaped - if (s.charAt(i + 1) == sq) { - i++; - } - sb.append(c); - sb.append(c); - } - } - - return sb.toString(); + return new PreparedQuery(sql, SqlQueryParameterAnalyzer.parametersCount(sql)); } } \ No newline at end of file diff --git a/plugin/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/jdbc/SqlQueryParameterAnalyzer.java b/plugin/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/jdbc/SqlQueryParameterAnalyzer.java new file mode 100644 index 00000000000..8278857bcfa --- /dev/null +++ b/plugin/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/jdbc/SqlQueryParameterAnalyzer.java @@ -0,0 +1,123 @@ +/* + * 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 java.sql.SQLException; + +/** + * Simplistic parser that can find parameters and escape sequences in a query and counts the number of parameters + *

+ * It is used by JDBC client to approximate the number of parameters that JDBC client should accept. The parser is simplistic + * 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 { + + private SqlQueryParameterAnalyzer() { + + } + + /** + * Returns number of parameters in the specified SQL query + */ + public static int parametersCount(String sql) throws SQLException { + + int l = sql.length(); + int params = 0; + for (int i = 0; i < l; i++) { + char c = sql.charAt(i); + + switch (c) { + case '{': + i = skipJdbcEscape(i, sql); + break; + case '\'': + i = skipString(i, sql, c); + break; + case '"': + i = skipString(i, sql, c); + break; + case '?': + params ++; + break; + case '-': + if (i + 1 < l && sql.charAt(i + 1) == '-') { + i = skipLineComment(i, sql); + } + break; + case '/': + if (i + 1 < l && sql.charAt(i + 1) == '*') { + i = skipMultiLineComment(i, sql); + } + break; + } + } + return params; + } + + /** + * Skips jdbc escape sequence starting at the current position i, returns the length of the sequence + */ + private static int skipJdbcEscape(int i, String sql) throws SQLException { + // TODO: JDBC escape syntax + // https://db.apache.org/derby/docs/10.5/ref/rrefjdbc1020262.html + throw new SQLException("Jdbc escape sequences are not supported yet"); + } + + + /** + * Skips a line comment starting at the current position i, returns the length of the comment + */ + private static int skipLineComment(int i, String sql) { + for (; i < sql.length(); i++) { + char c = sql.charAt(i); + if (c == '\n' || c == '\r') { + return i; + } + } + return i; + } + + /** + * Skips a multi-line comment starting at the current position i, returns the length of the comment + */ + private static int skipMultiLineComment(int i, String sql) throws SQLException { + int block = 0; + + for (; i < sql.length() - 1; i++) { + char c = sql.charAt(i); + if (c == '/' && sql.charAt(i + 1) == '*') { + i++; + block++; + } else if (c == '*' && sql.charAt(i + 1) == '/') { + i++; + block--; + } + if (block == 0) { + return i; + } + } + throw new SQLException("Cannot parse given sql; unclosed /* comment"); + } + + /** + * Skips a string starting at the current position i, returns the length of the string + */ + private static int skipString(int i, String sql, char q) throws SQLException { + for (i = i + 1; i < sql.length(); i++) { + char c = sql.charAt(i); + if (c == q) { + // double quotes mean escaping + if (i + 1 < sql.length() && sql.charAt(i + 1) == q) { + i++; + } else { + return i; + } + } + } + throw new SQLException("Cannot parse given sql; unclosed string"); + } +} diff --git a/plugin/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/net/client/JdbcHttpClient.java b/plugin/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/net/client/JdbcHttpClient.java index b376e39babd..508b0f28412 100644 --- a/plugin/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/net/client/JdbcHttpClient.java +++ b/plugin/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/net/client/JdbcHttpClient.java @@ -15,6 +15,7 @@ import org.elasticsearch.xpack.sql.jdbc.net.protocol.InfoResponse; import org.elasticsearch.xpack.sql.plugin.AbstractSqlRequest; import org.elasticsearch.xpack.sql.plugin.SqlQueryRequest; import org.elasticsearch.xpack.sql.plugin.SqlQueryResponse; +import org.elasticsearch.xpack.sql.plugin.SqlTypedParamValue; import org.joda.time.DateTimeZone; import java.sql.SQLException; @@ -41,9 +42,9 @@ public class JdbcHttpClient { return httpClient.ping(timeoutInMs); } - public Cursor query(String sql, RequestMeta meta) throws SQLException { + public Cursor query(String sql, List params, RequestMeta meta) throws SQLException { int fetch = meta.fetchSize() > 0 ? meta.fetchSize() : conCfg.pageSize(); - SqlQueryRequest sqlRequest = new SqlQueryRequest(AbstractSqlRequest.Mode.JDBC, sql, null, DateTimeZone.UTC, fetch, + SqlQueryRequest sqlRequest = new SqlQueryRequest(AbstractSqlRequest.Mode.JDBC, sql, params, null, DateTimeZone.UTC, fetch, TimeValue.timeValueMillis(meta.timeoutInMs()), TimeValue.timeValueMillis(meta.timeoutInMs()), ""); SqlQueryResponse response = httpClient.query(sqlRequest); return new DefaultCursor(this, response.cursor(), toJdbcColumnInfo(response.columns()), response.rows(), meta); diff --git a/plugin/sql/jdbc/src/test/java/org/elasticsearch/xpack/sql/jdbc/jdbc/SqlQueryParameterAnalyzerTests.java b/plugin/sql/jdbc/src/test/java/org/elasticsearch/xpack/sql/jdbc/jdbc/SqlQueryParameterAnalyzerTests.java new file mode 100644 index 00000000000..c9676a49e87 --- /dev/null +++ b/plugin/sql/jdbc/src/test/java/org/elasticsearch/xpack/sql/jdbc/jdbc/SqlQueryParameterAnalyzerTests.java @@ -0,0 +1,60 @@ +/* + * 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.test.ESTestCase; + +import java.sql.SQLException; + +public class SqlQueryParameterAnalyzerTests extends ESTestCase { + + public void testNoParameters() throws Exception { + assertEquals(0, SqlQueryParameterAnalyzer.parametersCount("SELECT * FROM table")); + assertEquals(0, SqlQueryParameterAnalyzer.parametersCount("SELECT * FROM 'table'")); + assertEquals(0, SqlQueryParameterAnalyzer.parametersCount("SELECT * FROM \"table\"")); + assertEquals(0, SqlQueryParameterAnalyzer.parametersCount("SELECT * FROM \"table\" WHERE i = 0")); + assertEquals(0, SqlQueryParameterAnalyzer.parametersCount("SELECT * FROM 'table' WHERE s = '?'")); + assertEquals(0, SqlQueryParameterAnalyzer.parametersCount("SELECT * FROM 'table' WHERE s = 'foo''bar''?'")); + assertEquals(0, SqlQueryParameterAnalyzer.parametersCount("SELECT * FROM `table` where b = 'fo\"o\\\"b{ar\\}?b\"az?}\\-?\"?\\?{'")); + + } + + + public void testSingleParameter() throws Exception { + assertEquals(1, SqlQueryParameterAnalyzer.parametersCount("SELECT * FROM 'table' WHERE s = '?' AND b = ?")); + assertEquals(1, SqlQueryParameterAnalyzer.parametersCount("SELECT * FROM 'table' WHERE b = ? AND s = '?'")); + assertEquals(1, SqlQueryParameterAnalyzer.parametersCount("SELECT ?/10 /* multiline \n" + + " * query \n" + + " * more ? /* lines */ ? here \n" + + " */ FROM foo")); + assertEquals(1, SqlQueryParameterAnalyzer.parametersCount("SELECT ?")); + + } + + public void testMultipleParameters() throws Exception { + assertEquals(4, SqlQueryParameterAnalyzer.parametersCount("SELECT ?, ?, ? , ?")); + assertEquals(3, SqlQueryParameterAnalyzer.parametersCount("SELECT ?, ?, '?' , ?")); + assertEquals(3, SqlQueryParameterAnalyzer.parametersCount("SELECT ?, ?\n, '?' , ?")); + assertEquals(3, SqlQueryParameterAnalyzer.parametersCount("SELECT ? - 10 -- first parameter with ????\n" + + ", ? -- second parameter with random \" and ' \n" + + ", ? -- last parameter without new line")); + } + + public void testUnclosedJdbcEscape() { + SQLException exception = expectThrows(SQLException.class, () -> SqlQueryParameterAnalyzer.parametersCount("SELECT {foobar")); + assertEquals("Jdbc escape sequences are not supported yet", exception.getMessage()); + } + + public void testUnclosedMultilineComment() { + SQLException exception = expectThrows(SQLException.class, () -> SqlQueryParameterAnalyzer.parametersCount("SELECT /* * * * ")); + assertEquals("Cannot parse given sql; unclosed /* comment", exception.getMessage()); + } + + public void testUnclosedSingleQuoteStrign() { + SQLException exception = expectThrows(SQLException.class, () -> SqlQueryParameterAnalyzer.parametersCount("SELECT ' '' '' ")); + assertEquals("Cannot parse given sql; unclosed string", exception.getMessage()); + } +} diff --git a/plugin/sql/sql-proto/src/main/java/org/elasticsearch/xpack/sql/plugin/AbstractSqlQueryRequest.java b/plugin/sql/sql-proto/src/main/java/org/elasticsearch/xpack/sql/plugin/AbstractSqlQueryRequest.java index f298772b8e4..36b31ea9c53 100644 --- a/plugin/sql/sql-proto/src/main/java/org/elasticsearch/xpack/sql/plugin/AbstractSqlQueryRequest.java +++ b/plugin/sql/sql-proto/src/main/java/org/elasticsearch/xpack/sql/plugin/AbstractSqlQueryRequest.java @@ -11,14 +11,20 @@ import org.elasticsearch.common.inject.internal.Nullable; import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; import org.elasticsearch.common.unit.TimeValue; +import org.elasticsearch.common.xcontent.AbstractObjectParser; import org.elasticsearch.common.xcontent.ObjectParser; import org.elasticsearch.common.xcontent.ToXContentFragment; import org.elasticsearch.common.xcontent.XContentBuilder; +import org.elasticsearch.common.xcontent.XContentParser; +import org.elasticsearch.common.xcontent.XContentParserUtils; import org.elasticsearch.index.query.AbstractQueryBuilder; import org.elasticsearch.index.query.QueryBuilder; import org.joda.time.DateTimeZone; import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; import java.util.Objects; import java.util.function.Supplier; @@ -42,15 +48,17 @@ public abstract class AbstractSqlQueryRequest extends AbstractSqlRequest impleme private TimeValue pageTimeout = DEFAULT_PAGE_TIMEOUT; @Nullable private QueryBuilder filter = null; + private List params = Collections.emptyList(); public AbstractSqlQueryRequest() { super(); } - public AbstractSqlQueryRequest(Mode mode, String query, QueryBuilder filter, DateTimeZone timeZone, int fetchSize, - TimeValue requestTimeout, TimeValue pageTimeout) { + public AbstractSqlQueryRequest(Mode mode, String query, List params, QueryBuilder filter, DateTimeZone timeZone, + int fetchSize, TimeValue requestTimeout, TimeValue pageTimeout) { super(mode); this.query = query; + this.params = params; this.timeZone = timeZone; this.fetchSize = fetchSize; this.requestTimeout = requestTimeout; @@ -62,6 +70,7 @@ public abstract class AbstractSqlQueryRequest extends AbstractSqlRequest impleme // TODO: convert this into ConstructingObjectParser ObjectParser parser = new ObjectParser<>("sql/query", true, supplier); parser.declareString(AbstractSqlQueryRequest::query, new ParseField("query")); + parser.declareObjectArray(AbstractSqlQueryRequest::params, (p, c) -> SqlTypedParamValue.fromXContent(p), new ParseField("params")); parser.declareString((request, zoneId) -> request.timeZone(DateTimeZone.forID(zoneId)), new ParseField("time_zone")); parser.declareInt(AbstractSqlQueryRequest::fetchSize, new ParseField("fetch_size")); parser.declareString( @@ -72,10 +81,12 @@ public abstract class AbstractSqlQueryRequest extends AbstractSqlRequest impleme new ParseField("page_timeout")); parser.declareObject(AbstractSqlQueryRequest::filter, (p, c) -> AbstractQueryBuilder.parseInnerQueryBuilder(p), new ParseField("filter")); - return parser; } + /** + * Text of SQL query + */ public String query() { return query; } @@ -88,6 +99,24 @@ public abstract class AbstractSqlQueryRequest extends AbstractSqlRequest impleme return this; } + /** + * An optional list of parameters if the SQL query is parametrized + */ + public List params() { + return params; + } + + public AbstractSqlQueryRequest params(List params) { + if (params == null) { + throw new IllegalArgumentException("params may not be null."); + } + this.params = params; + return this; + } + + /** + * The client's time zone + */ public DateTimeZone timeZone() { return timeZone; } @@ -118,6 +147,9 @@ public abstract class AbstractSqlQueryRequest extends AbstractSqlRequest impleme return this; } + /** + * The timeout specified on the search request + */ public TimeValue requestTimeout() { return requestTimeout; } @@ -127,7 +159,9 @@ public abstract class AbstractSqlQueryRequest extends AbstractSqlRequest impleme return this; } - + /** + * The scroll timeout + */ public TimeValue pageTimeout() { return pageTimeout; } @@ -155,6 +189,7 @@ public abstract class AbstractSqlQueryRequest extends AbstractSqlRequest impleme public AbstractSqlQueryRequest(StreamInput in) throws IOException { super(in); query = in.readString(); + params = in.readList(SqlTypedParamValue::new); timeZone = DateTimeZone.forID(in.readString()); fetchSize = in.readVInt(); requestTimeout = new TimeValue(in); @@ -166,6 +201,7 @@ public abstract class AbstractSqlQueryRequest extends AbstractSqlRequest impleme public void writeTo(StreamOutput out) throws IOException { super.writeTo(out); out.writeString(query); + out.writeList(params); out.writeString(timeZone.getID()); out.writeVInt(fetchSize); requestTimeout.writeTo(out); @@ -181,6 +217,7 @@ public abstract class AbstractSqlQueryRequest extends AbstractSqlRequest impleme AbstractSqlQueryRequest that = (AbstractSqlQueryRequest) o; return fetchSize == that.fetchSize && Objects.equals(query, that.query) && + Objects.equals(params, that.params) && Objects.equals(timeZone, that.timeZone) && Objects.equals(requestTimeout, that.requestTimeout) && Objects.equals(pageTimeout, that.pageTimeout) && @@ -197,6 +234,13 @@ public abstract class AbstractSqlQueryRequest extends AbstractSqlRequest impleme if (query != null) { builder.field("query", query); } + if (this.params.isEmpty() == false) { + builder.startArray("params"); + for (SqlTypedParamValue val : this.params) { + val.toXContent(builder, params); + } + builder.endArray(); + } if (timeZone != null) { builder.field("time_zone", timeZone.getID()); } @@ -215,4 +259,5 @@ public abstract class AbstractSqlQueryRequest extends AbstractSqlRequest impleme } return builder; } + } \ No newline at end of file diff --git a/plugin/sql/sql-proto/src/main/java/org/elasticsearch/xpack/sql/plugin/SqlQueryRequest.java b/plugin/sql/sql-proto/src/main/java/org/elasticsearch/xpack/sql/plugin/SqlQueryRequest.java index a2497484968..4b7cc6014bd 100644 --- a/plugin/sql/sql-proto/src/main/java/org/elasticsearch/xpack/sql/plugin/SqlQueryRequest.java +++ b/plugin/sql/sql-proto/src/main/java/org/elasticsearch/xpack/sql/plugin/SqlQueryRequest.java @@ -20,6 +20,7 @@ import org.elasticsearch.index.query.QueryBuilder; import org.joda.time.DateTimeZone; import java.io.IOException; +import java.util.List; import java.util.Objects; import static org.elasticsearch.action.ValidateActions.addValidationError; @@ -44,9 +45,9 @@ public class SqlQueryRequest extends AbstractSqlQueryRequest implements ToXConte public SqlQueryRequest() { } - public SqlQueryRequest(Mode mode, String query, QueryBuilder filter, DateTimeZone timeZone, int fetchSize, TimeValue requestTimeout, - TimeValue pageTimeout, String cursor) { - super(mode, query, filter, timeZone, fetchSize, requestTimeout, pageTimeout); + public SqlQueryRequest(Mode mode, String query, List params, QueryBuilder filter, DateTimeZone timeZone, + int fetchSize, TimeValue requestTimeout, TimeValue pageTimeout, String cursor) { + super(mode, query, params, filter, timeZone, fetchSize, requestTimeout, pageTimeout); this.cursor = cursor; } diff --git a/plugin/sql/sql-proto/src/main/java/org/elasticsearch/xpack/sql/plugin/SqlQueryRequestBuilder.java b/plugin/sql/sql-proto/src/main/java/org/elasticsearch/xpack/sql/plugin/SqlQueryRequestBuilder.java index 730315cf054..a9300d6629f 100644 --- a/plugin/sql/sql-proto/src/main/java/org/elasticsearch/xpack/sql/plugin/SqlQueryRequestBuilder.java +++ b/plugin/sql/sql-proto/src/main/java/org/elasticsearch/xpack/sql/plugin/SqlQueryRequestBuilder.java @@ -12,6 +12,9 @@ import org.elasticsearch.index.query.QueryBuilder; import org.elasticsearch.xpack.sql.plugin.AbstractSqlRequest.Mode; import org.joda.time.DateTimeZone; +import java.util.Collections; +import java.util.List; + import static org.elasticsearch.xpack.sql.plugin.AbstractSqlQueryRequest.DEFAULT_FETCH_SIZE; import static org.elasticsearch.xpack.sql.plugin.AbstractSqlQueryRequest.DEFAULT_PAGE_TIMEOUT; import static org.elasticsearch.xpack.sql.plugin.AbstractSqlQueryRequest.DEFAULT_REQUEST_TIMEOUT; @@ -23,14 +26,15 @@ import static org.elasticsearch.xpack.sql.plugin.AbstractSqlQueryRequest.DEFAULT public class SqlQueryRequestBuilder extends ActionRequestBuilder { public SqlQueryRequestBuilder(ElasticsearchClient client, SqlQueryAction action) { - this(client, action, "", null, DEFAULT_TIME_ZONE, DEFAULT_FETCH_SIZE, DEFAULT_REQUEST_TIMEOUT, DEFAULT_PAGE_TIMEOUT, "", - Mode.PLAIN); + this(client, action, "", Collections.emptyList(), null, DEFAULT_TIME_ZONE, DEFAULT_FETCH_SIZE, DEFAULT_REQUEST_TIMEOUT, + DEFAULT_PAGE_TIMEOUT, "", Mode.PLAIN); } - public SqlQueryRequestBuilder(ElasticsearchClient client, SqlQueryAction action, String query, QueryBuilder filter, - DateTimeZone timeZone, int fetchSize, TimeValue requestTimeout, TimeValue pageTimeout, - String nextPageInfo, Mode mode) { - super(client, action, new SqlQueryRequest(mode, query, filter, timeZone, fetchSize, requestTimeout, pageTimeout, nextPageInfo)); + public SqlQueryRequestBuilder(ElasticsearchClient client, SqlQueryAction action, String query, List params, + QueryBuilder filter, DateTimeZone timeZone, int fetchSize, TimeValue requestTimeout, + TimeValue pageTimeout, String nextPageInfo, Mode mode) { + super(client, action, new SqlQueryRequest(mode, query, params, filter, timeZone, fetchSize, requestTimeout, pageTimeout, + nextPageInfo)); } public SqlQueryRequestBuilder query(String query) { diff --git a/plugin/sql/sql-proto/src/main/java/org/elasticsearch/xpack/sql/plugin/SqlTranslateRequest.java b/plugin/sql/sql-proto/src/main/java/org/elasticsearch/xpack/sql/plugin/SqlTranslateRequest.java index 1fb6b9dfe92..4e5e94b475c 100644 --- a/plugin/sql/sql-proto/src/main/java/org/elasticsearch/xpack/sql/plugin/SqlTranslateRequest.java +++ b/plugin/sql/sql-proto/src/main/java/org/elasticsearch/xpack/sql/plugin/SqlTranslateRequest.java @@ -15,6 +15,7 @@ import org.elasticsearch.index.query.QueryBuilder; import org.joda.time.DateTimeZone; import java.io.IOException; +import java.util.List; import static org.elasticsearch.action.ValidateActions.addValidationError; @@ -27,9 +28,9 @@ public class SqlTranslateRequest extends AbstractSqlQueryRequest { public SqlTranslateRequest() { } - public SqlTranslateRequest(Mode mode, String query, QueryBuilder filter, DateTimeZone timeZone, int fetchSize, TimeValue requestTimeout, - TimeValue pageTimeout) { - super(mode, query, filter, timeZone, fetchSize, requestTimeout, pageTimeout); + public SqlTranslateRequest(Mode mode, String query, List params, QueryBuilder filter, DateTimeZone timeZone, + int fetchSize, TimeValue requestTimeout, TimeValue pageTimeout) { + super(mode, query, params, filter, timeZone, fetchSize, requestTimeout, pageTimeout); } public SqlTranslateRequest(StreamInput in) throws IOException { diff --git a/plugin/sql/sql-proto/src/main/java/org/elasticsearch/xpack/sql/plugin/SqlTranslateRequestBuilder.java b/plugin/sql/sql-proto/src/main/java/org/elasticsearch/xpack/sql/plugin/SqlTranslateRequestBuilder.java index 58cbf2ef78f..ba3e5663983 100644 --- a/plugin/sql/sql-proto/src/main/java/org/elasticsearch/xpack/sql/plugin/SqlTranslateRequestBuilder.java +++ b/plugin/sql/sql-proto/src/main/java/org/elasticsearch/xpack/sql/plugin/SqlTranslateRequestBuilder.java @@ -11,6 +11,9 @@ import org.elasticsearch.common.unit.TimeValue; import org.elasticsearch.index.query.QueryBuilder; import org.joda.time.DateTimeZone; +import java.util.Collections; +import java.util.List; + import static org.elasticsearch.xpack.sql.plugin.AbstractSqlQueryRequest.DEFAULT_FETCH_SIZE; import static org.elasticsearch.xpack.sql.plugin.AbstractSqlQueryRequest.DEFAULT_PAGE_TIMEOUT; import static org.elasticsearch.xpack.sql.plugin.AbstractSqlQueryRequest.DEFAULT_REQUEST_TIMEOUT; @@ -22,14 +25,14 @@ import static org.elasticsearch.xpack.sql.plugin.AbstractSqlQueryRequest.DEFAULT public class SqlTranslateRequestBuilder extends ActionRequestBuilder { public SqlTranslateRequestBuilder(ElasticsearchClient client, SqlTranslateAction action) { - this(client, action, AbstractSqlRequest.Mode.PLAIN, null, null, DEFAULT_TIME_ZONE, DEFAULT_FETCH_SIZE, DEFAULT_REQUEST_TIMEOUT, - DEFAULT_PAGE_TIMEOUT); + this(client, action, AbstractSqlRequest.Mode.PLAIN, null, null, Collections.emptyList(), DEFAULT_TIME_ZONE, DEFAULT_FETCH_SIZE, + DEFAULT_REQUEST_TIMEOUT, DEFAULT_PAGE_TIMEOUT); } public SqlTranslateRequestBuilder(ElasticsearchClient client, SqlTranslateAction action, AbstractSqlRequest.Mode mode, String query, - QueryBuilder filter, DateTimeZone timeZone, int fetchSize, TimeValue requestTimeout, - TimeValue pageTimeout) { - super(client, action, new SqlTranslateRequest(mode, query, filter, timeZone, fetchSize, requestTimeout, pageTimeout)); + QueryBuilder filter, List params, DateTimeZone timeZone, int fetchSize, + TimeValue requestTimeout, TimeValue pageTimeout) { + super(client, action, new SqlTranslateRequest(mode, query, params, filter, timeZone, fetchSize, requestTimeout, pageTimeout)); } public SqlTranslateRequestBuilder query(String query) { diff --git a/plugin/sql/sql-proto/src/main/java/org/elasticsearch/xpack/sql/plugin/SqlTypedParamValue.java b/plugin/sql/sql-proto/src/main/java/org/elasticsearch/xpack/sql/plugin/SqlTypedParamValue.java new file mode 100644 index 00000000000..11350c8df50 --- /dev/null +++ b/plugin/sql/sql-proto/src/main/java/org/elasticsearch/xpack/sql/plugin/SqlTypedParamValue.java @@ -0,0 +1,90 @@ +/* + * 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.plugin; + +import org.elasticsearch.common.ParseField; +import org.elasticsearch.common.io.stream.StreamInput; +import org.elasticsearch.common.io.stream.StreamOutput; +import org.elasticsearch.common.io.stream.Writeable; +import org.elasticsearch.common.xcontent.ConstructingObjectParser; +import org.elasticsearch.common.xcontent.ObjectParser; +import org.elasticsearch.common.xcontent.ToXContentObject; +import org.elasticsearch.common.xcontent.XContentBuilder; +import org.elasticsearch.common.xcontent.XContentHelper; +import org.elasticsearch.common.xcontent.XContentParser; +import org.elasticsearch.common.xcontent.XContentParserUtils; +import org.elasticsearch.xpack.sql.type.DataType; + +import java.io.IOException; +import java.util.Objects; + +import static org.elasticsearch.common.xcontent.ConstructingObjectParser.constructorArg; + +/** + * Represent a strongly typed parameter value + */ +public class SqlTypedParamValue implements ToXContentObject, Writeable { + private static final ConstructingObjectParser PARSER = + new ConstructingObjectParser<>("params", true, objects -> + new SqlTypedParamValue( + objects[0], + DataType.fromEsType((String) objects[1]))); + + private static final ParseField VALUE = new ParseField("value"); + private static final ParseField TYPE = new ParseField("type"); + + static { + PARSER.declareField(constructorArg(), (p, c) -> XContentParserUtils.parseFieldsValue(p), VALUE, ObjectParser.ValueType.VALUE); + PARSER.declareString(constructorArg(), TYPE); + } + + public final Object value; + public final DataType dataType; + + public SqlTypedParamValue(Object value, DataType dataType) { + this.value = value; + this.dataType = dataType; + } + + @Override + public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { + builder.startObject(); + builder.field("type", dataType.esType); + builder.field("value", value); + builder.endObject(); + return builder; + } + + public static SqlTypedParamValue fromXContent(XContentParser parser) { + return PARSER.apply(parser, null); + } + + @Override + public void writeTo(StreamOutput out) throws IOException { + out.writeEnum(dataType); + out.writeGenericValue(value); + } + + public SqlTypedParamValue(StreamInput in) throws IOException { + dataType = in.readEnum(DataType.class); + value = in.readGenericValue(); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + SqlTypedParamValue that = (SqlTypedParamValue) o; + return Objects.equals(value, that.value) && + dataType == that.dataType; + } + + @Override + public int hashCode() { + + return Objects.hash(value, dataType); + } +} diff --git a/plugin/sql/sql-proto/src/main/java/org/elasticsearch/xpack/sql/type/DataType.java b/plugin/sql/sql-proto/src/main/java/org/elasticsearch/xpack/sql/type/DataType.java index fd762a1f3ae..95c9ade5e29 100644 --- a/plugin/sql/sql-proto/src/main/java/org/elasticsearch/xpack/sql/type/DataType.java +++ b/plugin/sql/sql-proto/src/main/java/org/elasticsearch/xpack/sql/type/DataType.java @@ -149,4 +149,13 @@ public enum DataType { } return jdbcToEs.get(jdbcType); } + + /** + * Creates returns DataType enum coresponding to the specified es type + *

+ * For any dataType DataType.fromEsType(dataType.esType) == dataType + */ + public static DataType fromEsType(String esType) { + return DataType.valueOf(esType.toUpperCase(Locale.ROOT)); + } } \ No newline at end of file diff --git a/plugin/sql/sql-proto/src/test/java/org/elasticsearch/xpack/sql/plugin/SqlQueryRequestTests.java b/plugin/sql/sql-proto/src/test/java/org/elasticsearch/xpack/sql/plugin/SqlQueryRequestTests.java index 48f2e5386d9..f9430a495cc 100644 --- a/plugin/sql/sql-proto/src/test/java/org/elasticsearch/xpack/sql/plugin/SqlQueryRequestTests.java +++ b/plugin/sql/sql-proto/src/test/java/org/elasticsearch/xpack/sql/plugin/SqlQueryRequestTests.java @@ -14,10 +14,14 @@ import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.search.SearchModule; import org.elasticsearch.test.AbstractSerializingTestCase; import org.elasticsearch.test.ESTestCase; +import org.elasticsearch.xpack.sql.type.DataType; import org.junit.Before; +import java.util.ArrayList; import java.util.Collections; +import java.util.List; import java.util.function.Consumer; +import java.util.function.Supplier; import static org.elasticsearch.xpack.sql.plugin.SqlTestUtils.randomFilter; import static org.elasticsearch.xpack.sql.plugin.SqlTestUtils.randomFilterOrNull; @@ -45,12 +49,32 @@ public class SqlQueryRequestTests extends AbstractSerializingTestCase randomParameters() { + if (randomBoolean()) { + return Collections.emptyList(); + } else { + int len = randomIntBetween(1, 10); + List arr = new ArrayList<>(len); + for (int i = 0; i < len; i++) { + @SuppressWarnings("unchecked") Supplier supplier = randomFrom( + () -> new SqlTypedParamValue(randomBoolean(), DataType.BOOLEAN), + () -> new SqlTypedParamValue(randomLong(), DataType.LONG), + () -> new SqlTypedParamValue(randomDouble(), DataType.DOUBLE), + () -> new SqlTypedParamValue(null, DataType.NULL), + () -> new SqlTypedParamValue(randomAlphaOfLength(10), DataType.KEYWORD) + ); + arr.add(supplier.get()); + } + return Collections.unmodifiableList(arr); + } + } + @Override protected Writeable.Reader instanceReader() { return SqlQueryRequest::new; @@ -71,6 +95,7 @@ public class SqlQueryRequestTests extends AbstractSerializingTestCase mutator = randomFrom( request -> request.mode(randomValueOtherThan(request.mode(), () -> randomFrom(AbstractSqlRequest.Mode.values()))), request -> request.query(randomValueOtherThan(request.query(), () -> randomAlphaOfLength(5))), + request -> request.params(randomValueOtherThan(request.params(), this::randomParameters)), request -> request.timeZone(randomValueOtherThan(request.timeZone(), ESTestCase::randomDateTimeZone)), request -> request.fetchSize(randomValueOtherThan(request.fetchSize(), () -> between(1, Integer.MAX_VALUE))), request -> request.requestTimeout(randomValueOtherThan(request.requestTimeout(), this::randomTV)), @@ -78,7 +103,7 @@ public class SqlQueryRequestTests extends AbstractSerializingTestCase request.filter() == null ? randomFilter(random()) : randomFilterOrNull(random()))), request -> request.cursor(randomValueOtherThan(request.cursor(), SqlQueryResponseTests::randomStringCursor)) ); - SqlQueryRequest newRequest = new SqlQueryRequest(instance.mode(), instance.query(), instance.filter(), + SqlQueryRequest newRequest = new SqlQueryRequest(instance.mode(), instance.query(), instance.params(), instance.filter(), instance.timeZone(), instance.fetchSize(), instance.requestTimeout(), instance.pageTimeout(), instance.cursor()); mutator.accept(newRequest); return newRequest; diff --git a/plugin/sql/sql-proto/src/test/java/org/elasticsearch/xpack/sql/plugin/SqlTranslateRequestTests.java b/plugin/sql/sql-proto/src/test/java/org/elasticsearch/xpack/sql/plugin/SqlTranslateRequestTests.java index 08782ef6b6e..42057b53951 100644 --- a/plugin/sql/sql-proto/src/test/java/org/elasticsearch/xpack/sql/plugin/SqlTranslateRequestTests.java +++ b/plugin/sql/sql-proto/src/test/java/org/elasticsearch/xpack/sql/plugin/SqlTranslateRequestTests.java @@ -34,8 +34,8 @@ public class SqlTranslateRequestTests extends AbstractSerializingTestCase request.filter(randomValueOtherThan(request.filter(), () -> request.filter() == null ? randomFilter(random()) : randomFilterOrNull(random()))) ); - SqlTranslateRequest newRequest = new SqlTranslateRequest(instance.mode(), instance.query(), instance.filter(), + SqlTranslateRequest newRequest = new SqlTranslateRequest(instance.mode(), instance.query(), instance.params(), instance.filter(), instance.timeZone(), instance.fetchSize(), instance.requestTimeout(), instance.pageTimeout()); mutator.accept(newRequest); return newRequest; diff --git a/plugin/sql/sql-shared-client/src/main/java/org/elasticsearch/xpack/sql/client/HttpClient.java b/plugin/sql/sql-shared-client/src/main/java/org/elasticsearch/xpack/sql/client/HttpClient.java index eaac650c61c..2f5175eaf4d 100644 --- a/plugin/sql/sql-shared-client/src/main/java/org/elasticsearch/xpack/sql/client/HttpClient.java +++ b/plugin/sql/sql-shared-client/src/main/java/org/elasticsearch/xpack/sql/client/HttpClient.java @@ -36,6 +36,7 @@ import java.io.InputStream; import java.security.AccessController; import java.security.PrivilegedAction; import java.sql.SQLException; +import java.util.Collections; import java.util.function.Function; /** @@ -65,8 +66,8 @@ public class HttpClient { public SqlQueryResponse queryInit(String query, int fetchSize) throws SQLException { // TODO allow customizing the time zone - this is what session set/reset/get should be about - SqlQueryRequest sqlRequest = new SqlQueryRequest(AbstractSqlRequest.Mode.PLAIN, query, null, DateTimeZone.UTC, fetchSize, - TimeValue.timeValueMillis(cfg.queryTimeout()), TimeValue.timeValueMillis(cfg.pageTimeout()), "" + SqlQueryRequest sqlRequest = new SqlQueryRequest(AbstractSqlRequest.Mode.PLAIN, query, Collections.emptyList(), null, + DateTimeZone.UTC, fetchSize, TimeValue.timeValueMillis(cfg.queryTimeout()), TimeValue.timeValueMillis(cfg.pageTimeout()), "" ); return query(sqlRequest); } @@ -87,7 +88,7 @@ public class HttpClient { SqlClearCursorResponse::fromXContent); return response.isSucceeded(); } - + private Response post(String path, Request request, CheckedFunction responseParser) throws SQLException { diff --git a/plugin/sql/src/main/antlr/SqlBase.g4 b/plugin/sql/src/main/antlr/SqlBase.g4 index e62664ad93a..fffd16a7fec 100644 --- a/plugin/sql/src/main/antlr/SqlBase.g4 +++ b/plugin/sql/src/main/antlr/SqlBase.g4 @@ -56,12 +56,12 @@ statement | SHOW FUNCTIONS (LIKE? pattern)? #showFunctions | SHOW SCHEMAS #showSchemas | SYS CATALOGS #sysCatalogs - | SYS TABLES (CATALOG LIKE? clusterPattern=pattern)? + | SYS TABLES (CATALOG LIKE? clusterPattern=pattern)? (LIKE? tablePattern=pattern)? (TYPE STRING (',' STRING)* )? #sysTables - | SYS COLUMNS (CATALOG cluster=STRING)? - (TABLE LIKE? indexPattern=pattern)? - (LIKE? columnPattern=pattern)? #sysColumns + | SYS COLUMNS (CATALOG cluster=(STRING | PARAM))? + (TABLE LIKE? indexPattern=pattern)? + (LIKE? columnPattern=pattern)? #sysColumns | SYS TYPES #sysTypes | SYS TABLE TYPES #sysTableTypes ; @@ -186,6 +186,7 @@ predicate pattern : value=STRING (ESCAPE escape=STRING)? + | PARAM ; valueExpression @@ -215,6 +216,7 @@ constant | number #numericLiteral | booleanValue #booleanLiteral | STRING+ #stringLiteral + | PARAM #param ; comparisonOperator @@ -359,6 +361,7 @@ SLASH: '/'; PERCENT: '%'; CONCAT: '||'; DOT: '.'; +PARAM: '?'; STRING : '\'' ( ~'\'' | '\'\'' )* '\'' diff --git a/plugin/sql/src/main/antlr/SqlBase.tokens b/plugin/sql/src/main/antlr/SqlBase.tokens index dc07e45ca7a..87cf9a4809d 100644 --- a/plugin/sql/src/main/antlr/SqlBase.tokens +++ b/plugin/sql/src/main/antlr/SqlBase.tokens @@ -1,180 +1,182 @@ -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 -DEBUG=18 -DESC=19 -DESCRIBE=20 -DISTINCT=21 -ESCAPE=22 -EXECUTABLE=23 -EXISTS=24 -EXPLAIN=25 -EXTRACT=26 -FALSE=27 -FORMAT=28 -FROM=29 -FULL=30 -FUNCTIONS=31 -GRAPHVIZ=32 -GROUP=33 -HAVING=34 -IN=35 -INNER=36 -IS=37 -JOIN=38 -LEFT=39 -LIKE=40 -LIMIT=41 -MAPPED=42 -MATCH=43 -NATURAL=44 -NOT=45 -NULL=46 -ON=47 -OPTIMIZED=48 -OR=49 -ORDER=50 -OUTER=51 -PARSED=52 -PHYSICAL=53 -PLAN=54 -RIGHT=55 -RLIKE=56 -QUERY=57 -SCHEMAS=58 -SELECT=59 -SHOW=60 -SYS=61 -TABLE=62 -TABLES=63 -TEXT=64 -TRUE=65 -TYPE=66 -TYPES=67 -USING=68 -VERIFY=69 -WHERE=70 -WITH=71 -EQ=72 -NEQ=73 -LT=74 -LTE=75 -GT=76 -GTE=77 -PLUS=78 -MINUS=79 -ASTERISK=80 -SLASH=81 -PERCENT=82 -CONCAT=83 -DOT=84 -STRING=85 -INTEGER_VALUE=86 -DECIMAL_VALUE=87 -IDENTIFIER=88 -DIGIT_IDENTIFIER=89 -TABLE_IDENTIFIER=90 -QUOTED_IDENTIFIER=91 -BACKQUOTED_IDENTIFIER=92 -SIMPLE_COMMENT=93 -BRACKETED_COMMENT=94 -WS=95 -UNRECOGNIZED=96 -DELIMITER=97 -'('=1 -')'=2 -','=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 -'DEBUG'=18 -'DESC'=19 -'DESCRIBE'=20 -'DISTINCT'=21 -'ESCAPE'=22 -'EXECUTABLE'=23 -'EXISTS'=24 -'EXPLAIN'=25 -'EXTRACT'=26 -'FALSE'=27 -'FORMAT'=28 -'FROM'=29 -'FULL'=30 -'FUNCTIONS'=31 -'GRAPHVIZ'=32 -'GROUP'=33 -'HAVING'=34 -'IN'=35 -'INNER'=36 -'IS'=37 -'JOIN'=38 -'LEFT'=39 -'LIKE'=40 -'LIMIT'=41 -'MAPPED'=42 -'MATCH'=43 -'NATURAL'=44 -'NOT'=45 -'NULL'=46 -'ON'=47 -'OPTIMIZED'=48 -'OR'=49 -'ORDER'=50 -'OUTER'=51 -'PARSED'=52 -'PHYSICAL'=53 -'PLAN'=54 -'RIGHT'=55 -'RLIKE'=56 -'QUERY'=57 -'SCHEMAS'=58 -'SELECT'=59 -'SHOW'=60 -'SYS'=61 -'TABLE'=62 -'TABLES'=63 -'TEXT'=64 -'TRUE'=65 -'TYPE'=66 -'TYPES'=67 -'USING'=68 -'VERIFY'=69 -'WHERE'=70 -'WITH'=71 -'='=72 -'<'=74 -'<='=75 -'>'=76 -'>='=77 -'+'=78 -'-'=79 -'*'=80 -'/'=81 -'%'=82 -'||'=83 -'.'=84 +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 +DEBUG=18 +DESC=19 +DESCRIBE=20 +DISTINCT=21 +ESCAPE=22 +EXECUTABLE=23 +EXISTS=24 +EXPLAIN=25 +EXTRACT=26 +FALSE=27 +FORMAT=28 +FROM=29 +FULL=30 +FUNCTIONS=31 +GRAPHVIZ=32 +GROUP=33 +HAVING=34 +IN=35 +INNER=36 +IS=37 +JOIN=38 +LEFT=39 +LIKE=40 +LIMIT=41 +MAPPED=42 +MATCH=43 +NATURAL=44 +NOT=45 +NULL=46 +ON=47 +OPTIMIZED=48 +OR=49 +ORDER=50 +OUTER=51 +PARSED=52 +PHYSICAL=53 +PLAN=54 +RIGHT=55 +RLIKE=56 +QUERY=57 +SCHEMAS=58 +SELECT=59 +SHOW=60 +SYS=61 +TABLE=62 +TABLES=63 +TEXT=64 +TRUE=65 +TYPE=66 +TYPES=67 +USING=68 +VERIFY=69 +WHERE=70 +WITH=71 +EQ=72 +NEQ=73 +LT=74 +LTE=75 +GT=76 +GTE=77 +PLUS=78 +MINUS=79 +ASTERISK=80 +SLASH=81 +PERCENT=82 +CONCAT=83 +DOT=84 +PARAM=85 +STRING=86 +INTEGER_VALUE=87 +DECIMAL_VALUE=88 +IDENTIFIER=89 +DIGIT_IDENTIFIER=90 +TABLE_IDENTIFIER=91 +QUOTED_IDENTIFIER=92 +BACKQUOTED_IDENTIFIER=93 +SIMPLE_COMMENT=94 +BRACKETED_COMMENT=95 +WS=96 +UNRECOGNIZED=97 +DELIMITER=98 +'('=1 +')'=2 +','=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 +'DEBUG'=18 +'DESC'=19 +'DESCRIBE'=20 +'DISTINCT'=21 +'ESCAPE'=22 +'EXECUTABLE'=23 +'EXISTS'=24 +'EXPLAIN'=25 +'EXTRACT'=26 +'FALSE'=27 +'FORMAT'=28 +'FROM'=29 +'FULL'=30 +'FUNCTIONS'=31 +'GRAPHVIZ'=32 +'GROUP'=33 +'HAVING'=34 +'IN'=35 +'INNER'=36 +'IS'=37 +'JOIN'=38 +'LEFT'=39 +'LIKE'=40 +'LIMIT'=41 +'MAPPED'=42 +'MATCH'=43 +'NATURAL'=44 +'NOT'=45 +'NULL'=46 +'ON'=47 +'OPTIMIZED'=48 +'OR'=49 +'ORDER'=50 +'OUTER'=51 +'PARSED'=52 +'PHYSICAL'=53 +'PLAN'=54 +'RIGHT'=55 +'RLIKE'=56 +'QUERY'=57 +'SCHEMAS'=58 +'SELECT'=59 +'SHOW'=60 +'SYS'=61 +'TABLE'=62 +'TABLES'=63 +'TEXT'=64 +'TRUE'=65 +'TYPE'=66 +'TYPES'=67 +'USING'=68 +'VERIFY'=69 +'WHERE'=70 +'WITH'=71 +'='=72 +'<'=74 +'<='=75 +'>'=76 +'>='=77 +'+'=78 +'-'=79 +'*'=80 +'/'=81 +'%'=82 +'||'=83 +'.'=84 +'?'=85 diff --git a/plugin/sql/src/main/antlr/SqlBaseLexer.tokens b/plugin/sql/src/main/antlr/SqlBaseLexer.tokens index 1ea3fbd56b7..a687a9215ec 100644 --- a/plugin/sql/src/main/antlr/SqlBaseLexer.tokens +++ b/plugin/sql/src/main/antlr/SqlBaseLexer.tokens @@ -1,179 +1,181 @@ -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 -DEBUG=18 -DESC=19 -DESCRIBE=20 -DISTINCT=21 -ESCAPE=22 -EXECUTABLE=23 -EXISTS=24 -EXPLAIN=25 -EXTRACT=26 -FALSE=27 -FORMAT=28 -FROM=29 -FULL=30 -FUNCTIONS=31 -GRAPHVIZ=32 -GROUP=33 -HAVING=34 -IN=35 -INNER=36 -IS=37 -JOIN=38 -LEFT=39 -LIKE=40 -LIMIT=41 -MAPPED=42 -MATCH=43 -NATURAL=44 -NOT=45 -NULL=46 -ON=47 -OPTIMIZED=48 -OR=49 -ORDER=50 -OUTER=51 -PARSED=52 -PHYSICAL=53 -PLAN=54 -RIGHT=55 -RLIKE=56 -QUERY=57 -SCHEMAS=58 -SELECT=59 -SHOW=60 -SYS=61 -TABLE=62 -TABLES=63 -TEXT=64 -TRUE=65 -TYPE=66 -TYPES=67 -USING=68 -VERIFY=69 -WHERE=70 -WITH=71 -EQ=72 -NEQ=73 -LT=74 -LTE=75 -GT=76 -GTE=77 -PLUS=78 -MINUS=79 -ASTERISK=80 -SLASH=81 -PERCENT=82 -CONCAT=83 -DOT=84 -STRING=85 -INTEGER_VALUE=86 -DECIMAL_VALUE=87 -IDENTIFIER=88 -DIGIT_IDENTIFIER=89 -TABLE_IDENTIFIER=90 -QUOTED_IDENTIFIER=91 -BACKQUOTED_IDENTIFIER=92 -SIMPLE_COMMENT=93 -BRACKETED_COMMENT=94 -WS=95 -UNRECOGNIZED=96 -'('=1 -')'=2 -','=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 -'DEBUG'=18 -'DESC'=19 -'DESCRIBE'=20 -'DISTINCT'=21 -'ESCAPE'=22 -'EXECUTABLE'=23 -'EXISTS'=24 -'EXPLAIN'=25 -'EXTRACT'=26 -'FALSE'=27 -'FORMAT'=28 -'FROM'=29 -'FULL'=30 -'FUNCTIONS'=31 -'GRAPHVIZ'=32 -'GROUP'=33 -'HAVING'=34 -'IN'=35 -'INNER'=36 -'IS'=37 -'JOIN'=38 -'LEFT'=39 -'LIKE'=40 -'LIMIT'=41 -'MAPPED'=42 -'MATCH'=43 -'NATURAL'=44 -'NOT'=45 -'NULL'=46 -'ON'=47 -'OPTIMIZED'=48 -'OR'=49 -'ORDER'=50 -'OUTER'=51 -'PARSED'=52 -'PHYSICAL'=53 -'PLAN'=54 -'RIGHT'=55 -'RLIKE'=56 -'QUERY'=57 -'SCHEMAS'=58 -'SELECT'=59 -'SHOW'=60 -'SYS'=61 -'TABLE'=62 -'TABLES'=63 -'TEXT'=64 -'TRUE'=65 -'TYPE'=66 -'TYPES'=67 -'USING'=68 -'VERIFY'=69 -'WHERE'=70 -'WITH'=71 -'='=72 -'<'=74 -'<='=75 -'>'=76 -'>='=77 -'+'=78 -'-'=79 -'*'=80 -'/'=81 -'%'=82 -'||'=83 -'.'=84 +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 +DEBUG=18 +DESC=19 +DESCRIBE=20 +DISTINCT=21 +ESCAPE=22 +EXECUTABLE=23 +EXISTS=24 +EXPLAIN=25 +EXTRACT=26 +FALSE=27 +FORMAT=28 +FROM=29 +FULL=30 +FUNCTIONS=31 +GRAPHVIZ=32 +GROUP=33 +HAVING=34 +IN=35 +INNER=36 +IS=37 +JOIN=38 +LEFT=39 +LIKE=40 +LIMIT=41 +MAPPED=42 +MATCH=43 +NATURAL=44 +NOT=45 +NULL=46 +ON=47 +OPTIMIZED=48 +OR=49 +ORDER=50 +OUTER=51 +PARSED=52 +PHYSICAL=53 +PLAN=54 +RIGHT=55 +RLIKE=56 +QUERY=57 +SCHEMAS=58 +SELECT=59 +SHOW=60 +SYS=61 +TABLE=62 +TABLES=63 +TEXT=64 +TRUE=65 +TYPE=66 +TYPES=67 +USING=68 +VERIFY=69 +WHERE=70 +WITH=71 +EQ=72 +NEQ=73 +LT=74 +LTE=75 +GT=76 +GTE=77 +PLUS=78 +MINUS=79 +ASTERISK=80 +SLASH=81 +PERCENT=82 +CONCAT=83 +DOT=84 +PARAM=85 +STRING=86 +INTEGER_VALUE=87 +DECIMAL_VALUE=88 +IDENTIFIER=89 +DIGIT_IDENTIFIER=90 +TABLE_IDENTIFIER=91 +QUOTED_IDENTIFIER=92 +BACKQUOTED_IDENTIFIER=93 +SIMPLE_COMMENT=94 +BRACKETED_COMMENT=95 +WS=96 +UNRECOGNIZED=97 +'('=1 +')'=2 +','=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 +'DEBUG'=18 +'DESC'=19 +'DESCRIBE'=20 +'DISTINCT'=21 +'ESCAPE'=22 +'EXECUTABLE'=23 +'EXISTS'=24 +'EXPLAIN'=25 +'EXTRACT'=26 +'FALSE'=27 +'FORMAT'=28 +'FROM'=29 +'FULL'=30 +'FUNCTIONS'=31 +'GRAPHVIZ'=32 +'GROUP'=33 +'HAVING'=34 +'IN'=35 +'INNER'=36 +'IS'=37 +'JOIN'=38 +'LEFT'=39 +'LIKE'=40 +'LIMIT'=41 +'MAPPED'=42 +'MATCH'=43 +'NATURAL'=44 +'NOT'=45 +'NULL'=46 +'ON'=47 +'OPTIMIZED'=48 +'OR'=49 +'ORDER'=50 +'OUTER'=51 +'PARSED'=52 +'PHYSICAL'=53 +'PLAN'=54 +'RIGHT'=55 +'RLIKE'=56 +'QUERY'=57 +'SCHEMAS'=58 +'SELECT'=59 +'SHOW'=60 +'SYS'=61 +'TABLE'=62 +'TABLES'=63 +'TEXT'=64 +'TRUE'=65 +'TYPE'=66 +'TYPES'=67 +'USING'=68 +'VERIFY'=69 +'WHERE'=70 +'WITH'=71 +'='=72 +'<'=74 +'<='=75 +'>'=76 +'>='=77 +'+'=78 +'-'=79 +'*'=80 +'/'=81 +'%'=82 +'||'=83 +'.'=84 +'?'=85 diff --git a/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/execution/PlanExecutor.java b/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/execution/PlanExecutor.java index cf69ccf6767..b27f88fc2f5 100644 --- a/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/execution/PlanExecutor.java +++ b/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/execution/PlanExecutor.java @@ -16,12 +16,15 @@ import org.elasticsearch.xpack.sql.optimizer.Optimizer; import org.elasticsearch.xpack.sql.plan.physical.EsQueryExec; import org.elasticsearch.xpack.sql.planner.Planner; import org.elasticsearch.xpack.sql.planner.PlanningException; +import org.elasticsearch.xpack.sql.plugin.SqlTypedParamValue; import org.elasticsearch.xpack.sql.session.Configuration; import org.elasticsearch.xpack.sql.session.Cursor; import org.elasticsearch.xpack.sql.session.RowSet; import org.elasticsearch.xpack.sql.session.SchemaRowSet; import org.elasticsearch.xpack.sql.session.SqlSession; +import java.util.List; + public class PlanExecutor { private final Client client; @@ -46,19 +49,19 @@ public class PlanExecutor { return new SqlSession(cfg, client, functionRegistry, indexResolver, preAnalyzer, optimizer, planner); } - public void searchSource(String sql, Configuration settings, ActionListener listener) { - newSession(settings).sqlExecutable(sql, ActionListener.wrap(exec -> { + public void searchSource(Configuration cfg, String sql, List params, ActionListener listener) { + newSession(cfg).sqlExecutable(sql, params, ActionListener.wrap(exec -> { if (exec instanceof EsQueryExec) { EsQueryExec e = (EsQueryExec) exec; - listener.onResponse(SourceGenerator.sourceBuilder(e.queryContainer(), settings.filter(), settings.pageSize())); + listener.onResponse(SourceGenerator.sourceBuilder(e.queryContainer(), cfg.filter(), cfg.pageSize())); } else { listener.onFailure(new PlanningException("Cannot generate a query DSL for {}", sql)); } }, listener::onFailure)); } - public void sql(Configuration cfg, String sql, ActionListener listener) { - newSession(cfg).sql(sql, listener); + public void sql(Configuration cfg, String sql, List params, ActionListener listener) { + newSession(cfg).sql(sql, params, listener); } public void nextPage(Configuration cfg, Cursor cursor, ActionListener listener) { diff --git a/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/parser/AstBuilder.java b/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/parser/AstBuilder.java index be1de0ad6ee..de28f331872 100644 --- a/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/parser/AstBuilder.java +++ b/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/parser/AstBuilder.java @@ -5,13 +5,20 @@ */ package org.elasticsearch.xpack.sql.parser; +import org.antlr.v4.runtime.Token; import org.elasticsearch.xpack.sql.parser.SqlBaseParser.SingleStatementContext; import org.elasticsearch.xpack.sql.plan.logical.LogicalPlan; -import org.joda.time.DateTimeZone; +import org.elasticsearch.xpack.sql.plugin.SqlTypedParamValue; + +import java.util.Map; class AstBuilder extends CommandBuilder { - AstBuilder(DateTimeZone timeZone) { - super(timeZone); + /** + * Create AST Builder + * @param params a map between '?' tokens that represent parameters and the actual parameter values + */ + AstBuilder(Map params) { + super(params); } @Override diff --git a/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/parser/CommandBuilder.java b/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/parser/CommandBuilder.java index 0d2baa51cd1..5ef44220dec 100644 --- a/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/parser/CommandBuilder.java +++ b/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/parser/CommandBuilder.java @@ -5,6 +5,7 @@ */ package org.elasticsearch.xpack.sql.parser; +import org.antlr.v4.runtime.Token; import org.antlr.v4.runtime.tree.TerminalNode; import org.elasticsearch.common.Booleans; import org.elasticsearch.xpack.sql.analysis.index.IndexResolver.IndexType; @@ -32,17 +33,18 @@ import org.elasticsearch.xpack.sql.plan.logical.command.sys.SysColumns; import org.elasticsearch.xpack.sql.plan.logical.command.sys.SysTableTypes; import org.elasticsearch.xpack.sql.plan.logical.command.sys.SysTables; import org.elasticsearch.xpack.sql.plan.logical.command.sys.SysTypes; +import org.elasticsearch.xpack.sql.plugin.SqlTypedParamValue; import org.elasticsearch.xpack.sql.tree.Location; -import org.joda.time.DateTimeZone; import java.util.ArrayList; import java.util.EnumSet; import java.util.List; import java.util.Locale; +import java.util.Map; abstract class CommandBuilder extends LogicalPlanBuilder { - protected CommandBuilder(DateTimeZone timeZone) { - super(timeZone); + protected CommandBuilder(Map params) { + super(params); } @Override @@ -160,7 +162,8 @@ abstract class CommandBuilder extends LogicalPlanBuilder { @Override public Object visitSysColumns(SysColumnsContext ctx) { - return new SysColumns(source(ctx), string(ctx.cluster), visitPattern(ctx.indexPattern), visitPattern(ctx.columnPattern)); + Location loc = source(ctx); + return new SysColumns(loc, stringOrParam(ctx.cluster, loc), visitPattern(ctx.indexPattern), visitPattern(ctx.columnPattern)); } @Override @@ -168,7 +171,6 @@ abstract class CommandBuilder extends LogicalPlanBuilder { return new SysTypes(source(ctx)); } - @Override public Object visitSysTableTypes(SysTableTypesContext ctx) { return new SysTableTypes(source(ctx)); } diff --git a/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/parser/ExpressionBuilder.java b/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/parser/ExpressionBuilder.java index 940fe06a85b..9d47b26b86b 100644 --- a/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/parser/ExpressionBuilder.java +++ b/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/parser/ExpressionBuilder.java @@ -6,10 +6,12 @@ package org.elasticsearch.xpack.sql.parser; import org.antlr.v4.runtime.ParserRuleContext; +import org.antlr.v4.runtime.Token; import org.antlr.v4.runtime.tree.ParseTree; import org.antlr.v4.runtime.tree.TerminalNode; import org.elasticsearch.common.Booleans; import org.elasticsearch.common.Strings; +import org.elasticsearch.xpack.sql.SqlIllegalArgumentException; import org.elasticsearch.xpack.sql.expression.Alias; import org.elasticsearch.xpack.sql.expression.Exists; import org.elasticsearch.xpack.sql.expression.Expression; @@ -72,25 +74,29 @@ import org.elasticsearch.xpack.sql.parser.SqlBaseParser.StarContext; import org.elasticsearch.xpack.sql.parser.SqlBaseParser.StringLiteralContext; import org.elasticsearch.xpack.sql.parser.SqlBaseParser.StringQueryContext; import org.elasticsearch.xpack.sql.parser.SqlBaseParser.SubqueryExpressionContext; +import org.elasticsearch.xpack.sql.plugin.SqlTypedParamValue; import org.elasticsearch.xpack.sql.tree.Location; import org.elasticsearch.xpack.sql.type.DataType; -import org.joda.time.DateTimeZone; +import org.elasticsearch.xpack.sql.type.DataTypes; import java.math.BigDecimal; import java.util.List; import java.util.Locale; +import java.util.Map; import static java.util.Collections.singletonList; +import static org.elasticsearch.xpack.sql.type.DataTypeConversion.conversionFor; abstract class ExpressionBuilder extends IdentifierBuilder { - /** - * Time zone that we're executing in. Set on functions that deal - * with dates and times for use later in the evaluation process. - */ - private final DateTimeZone timeZone; // TODO remove me + private final Map params; - protected ExpressionBuilder(DateTimeZone timeZone) { - this.timeZone = timeZone; + /** + * ExpressionBuilder constructor + * + * @param params a map between '?' tokens that represent parameters and the actual parameter values + */ + protected ExpressionBuilder(Map params) { + this.params = params; } protected Expression expression(ParseTree ctx) { @@ -209,6 +215,11 @@ abstract class ExpressionBuilder extends IdentifierBuilder { return null; } + if (ctx.PARAM() != null) { + Object pattern = paramValue(ctx.PARAM().getSymbol(), source(ctx)); + return new LikePattern(source(ctx), pattern.toString(), (char) 0); + } + String pattern = string(ctx.value); int pos = pattern.indexOf('*'); if (pos >= 0) { @@ -440,6 +451,57 @@ abstract class ExpressionBuilder extends IdentifierBuilder { return new Literal(source(ctx), sb.toString(), DataType.KEYWORD); } + @Override + public Object visitParam(SqlBaseParser.ParamContext ctx) { + Token token = ctx.PARAM().getSymbol(); + return paramValue(token, source(ctx)); + } + + private Object paramValue(Token token, Location loc) { + if (params.containsKey(token) == false) { + throw new ParsingException(loc, "Unexpected parameter"); + } + SqlTypedParamValue param = params.get(token); + if (param.value == null) { + // no conversion is required for null values + return new Literal(loc, null, param.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); + } + if (sourceType == param.dataType) { + // no conversion is required if the value is already have correct type + return new Literal(loc, param.value, param.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); + } catch (SqlIllegalArgumentException ex) { + throw new ParsingException(ex, loc, "Unexpected actual parameter type [{}] for type [{}]", sourceType, param.dataType); + } + } + + /** + * Extracts the actual unescaped string (literal) value of a token or a parameter value + */ + public String stringOrParam(Token token, Location loc) { + if (token == null) { + return null; + } + if (token.getType() == SqlBaseLexer.PARAM) { + if (params.containsKey(token) == false) { + throw new ParsingException(loc, "Unexpected parameter"); + } + SqlTypedParamValue param = params.get(token); + return param.value == null ? null : param.value.toString(); + } + return unquoteString(token.getText()); + } + @Override public Object visitDecimalLiteral(DecimalLiteralContext ctx) { return new Literal(source(ctx), new BigDecimal(ctx.getText()).doubleValue(), DataType.DOUBLE); diff --git a/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/parser/LogicalPlanBuilder.java b/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/parser/LogicalPlanBuilder.java index 9b62a8c8c1a..9cc6b79f3c3 100644 --- a/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/parser/LogicalPlanBuilder.java +++ b/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/parser/LogicalPlanBuilder.java @@ -5,6 +5,7 @@ */ package org.elasticsearch.xpack.sql.parser; +import org.antlr.v4.runtime.Token; import org.antlr.v4.runtime.tree.TerminalNode; import org.elasticsearch.xpack.sql.expression.Expression; import org.elasticsearch.xpack.sql.expression.Literal; @@ -40,10 +41,9 @@ import org.elasticsearch.xpack.sql.plan.logical.Project; import org.elasticsearch.xpack.sql.plan.logical.SubQueryAlias; import org.elasticsearch.xpack.sql.plan.logical.UnresolvedRelation; import org.elasticsearch.xpack.sql.plan.logical.With; +import org.elasticsearch.xpack.sql.plugin.SqlTypedParamValue; import org.elasticsearch.xpack.sql.session.EmptyExecutable; import org.elasticsearch.xpack.sql.type.DataType; -import org.elasticsearch.xpack.sql.type.DataTypes; -import org.joda.time.DateTimeZone; import java.util.LinkedHashMap; import java.util.List; @@ -53,8 +53,8 @@ import static java.util.Collections.emptyList; import static java.util.stream.Collectors.toList; abstract class LogicalPlanBuilder extends ExpressionBuilder { - protected LogicalPlanBuilder(DateTimeZone timeZone) { - super(timeZone); + protected LogicalPlanBuilder(Map params) { + super(params); } @Override diff --git a/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/parser/ParsingException.java b/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/parser/ParsingException.java index 59f6702ec70..e294b831a88 100644 --- a/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/parser/ParsingException.java +++ b/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/parser/ParsingException.java @@ -33,6 +33,12 @@ public class ParsingException extends ClientSqlException { this.charPositionInLine = nodeLocation.getColumnNumber(); } + public ParsingException(Exception cause, Location nodeLocation, String message, Object... args) { + super(cause, message, args); + this.line = nodeLocation.getLineNumber(); + this.charPositionInLine = nodeLocation.getColumnNumber(); + } + public int getLineNumber() { return line; } diff --git a/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/parser/SqlBaseBaseListener.java b/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/parser/SqlBaseBaseListener.java index 4733d68970c..d66774e2f43 100644 --- a/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/parser/SqlBaseBaseListener.java +++ b/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/parser/SqlBaseBaseListener.java @@ -760,6 +760,18 @@ class SqlBaseBaseListener implements SqlBaseListener { *

The default implementation does nothing.

*/ @Override public void exitStringLiteral(SqlBaseParser.StringLiteralContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void enterParam(SqlBaseParser.ParamContext ctx) { } + /** + * {@inheritDoc} + * + *

The default implementation does nothing.

+ */ + @Override public void exitParam(SqlBaseParser.ParamContext ctx) { } /** * {@inheritDoc} * diff --git a/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/parser/SqlBaseBaseVisitor.java b/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/parser/SqlBaseBaseVisitor.java index 47d989deea0..fe9a41e2494 100644 --- a/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/parser/SqlBaseBaseVisitor.java +++ b/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/parser/SqlBaseBaseVisitor.java @@ -450,6 +450,13 @@ class SqlBaseBaseVisitor extends AbstractParseTreeVisitor implements SqlBa * {@link #visitChildren} on {@code ctx}.

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

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

+ */ + @Override public T visitParam(SqlBaseParser.ParamContext ctx) { return visitChildren(ctx); } /** * {@inheritDoc} * diff --git a/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/parser/SqlBaseLexer.java b/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/parser/SqlBaseLexer.java index 9b23a3ce5e7..1367b98a899 100644 --- a/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/parser/SqlBaseLexer.java +++ b/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/parser/SqlBaseLexer.java @@ -33,9 +33,9 @@ class SqlBaseLexer extends Lexer { SHOW=60, SYS=61, TABLE=62, TABLES=63, TEXT=64, TRUE=65, TYPE=66, TYPES=67, USING=68, VERIFY=69, WHERE=70, WITH=71, EQ=72, NEQ=73, LT=74, LTE=75, GT=76, GTE=77, PLUS=78, MINUS=79, ASTERISK=80, SLASH=81, PERCENT=82, CONCAT=83, - DOT=84, STRING=85, INTEGER_VALUE=86, DECIMAL_VALUE=87, IDENTIFIER=88, - DIGIT_IDENTIFIER=89, TABLE_IDENTIFIER=90, QUOTED_IDENTIFIER=91, BACKQUOTED_IDENTIFIER=92, - SIMPLE_COMMENT=93, BRACKETED_COMMENT=94, WS=95, UNRECOGNIZED=96; + DOT=84, PARAM=85, STRING=86, INTEGER_VALUE=87, DECIMAL_VALUE=88, IDENTIFIER=89, + DIGIT_IDENTIFIER=90, TABLE_IDENTIFIER=91, QUOTED_IDENTIFIER=92, BACKQUOTED_IDENTIFIER=93, + SIMPLE_COMMENT=94, BRACKETED_COMMENT=95, WS=96, UNRECOGNIZED=97; public static String[] modeNames = { "DEFAULT_MODE" }; @@ -51,10 +51,10 @@ class SqlBaseLexer extends Lexer { "QUERY", "SCHEMAS", "SELECT", "SHOW", "SYS", "TABLE", "TABLES", "TEXT", "TRUE", "TYPE", "TYPES", "USING", "VERIFY", "WHERE", "WITH", "EQ", "NEQ", "LT", "LTE", "GT", "GTE", "PLUS", "MINUS", "ASTERISK", "SLASH", "PERCENT", - "CONCAT", "DOT", "STRING", "INTEGER_VALUE", "DECIMAL_VALUE", "IDENTIFIER", - "DIGIT_IDENTIFIER", "TABLE_IDENTIFIER", "QUOTED_IDENTIFIER", "BACKQUOTED_IDENTIFIER", - "EXPONENT", "DIGIT", "LETTER", "SIMPLE_COMMENT", "BRACKETED_COMMENT", - "WS", "UNRECOGNIZED" + "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 = { @@ -69,7 +69,7 @@ class SqlBaseLexer extends Lexer { "'RLIKE'", "'QUERY'", "'SCHEMAS'", "'SELECT'", "'SHOW'", "'SYS'", "'TABLE'", "'TABLES'", "'TEXT'", "'TRUE'", "'TYPE'", "'TYPES'", "'USING'", "'VERIFY'", "'WHERE'", "'WITH'", "'='", null, "'<'", "'<='", "'>'", "'>='", "'+'", - "'-'", "'*'", "'/'", "'%'", "'||'", "'.'" + "'-'", "'*'", "'/'", "'%'", "'||'", "'.'", "'?'" }; private static final String[] _SYMBOLIC_NAMES = { null, null, null, null, null, "ALL", "ANALYZE", "ANALYZED", "AND", "ANY", @@ -82,9 +82,10 @@ class SqlBaseLexer extends Lexer { "QUERY", "SCHEMAS", "SELECT", "SHOW", "SYS", "TABLE", "TABLES", "TEXT", "TRUE", "TYPE", "TYPES", "USING", "VERIFY", "WHERE", "WITH", "EQ", "NEQ", "LT", "LTE", "GT", "GTE", "PLUS", "MINUS", "ASTERISK", "SLASH", "PERCENT", - "CONCAT", "DOT", "STRING", "INTEGER_VALUE", "DECIMAL_VALUE", "IDENTIFIER", - "DIGIT_IDENTIFIER", "TABLE_IDENTIFIER", "QUOTED_IDENTIFIER", "BACKQUOTED_IDENTIFIER", - "SIMPLE_COMMENT", "BRACKETED_COMMENT", "WS", "UNRECOGNIZED" + "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); @@ -141,7 +142,7 @@ class SqlBaseLexer extends Lexer { public ATN getATN() { return _ATN; } public static final String _serializedATN = - "\3\u0430\ud6d1\u8206\uad2d\u4417\uaef1\u8d80\uaadd\2b\u0337\b\1\4\2\t"+ + "\3\u0430\ud6d1\u8206\uad2d\u4417\uaef1\u8d80\uaadd\2c\u033b\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"+ @@ -152,274 +153,276 @@ class SqlBaseLexer extends Lexer { "\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\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\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\24\3\24\3\24\3\24\3\24\3\25\3\25\3\25\3\25\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\30\3\30\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\32\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\35\3\35\3\35\3\35\3\35\3\35\3\35\3\36\3\36\3\36\3\36"+ - "\3\36\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\60\3\60\3\60\3\61"+ - "\3\61\3\61\3\61\3\61\3\61\3\61\3\61\3\61\3\61\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\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\66\3\67\3\67"+ - "\3\67\3\67\3\67\38\38\38\38\38\38\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@\3A\3A\3A\3A\3A\3B\3B\3B\3B"+ - "\3B\3C\3C\3C\3C\3C\3D\3D\3D\3D\3D\3D\3E\3E\3E\3E\3E\3E\3F\3F\3F\3F\3F"+ - "\3F\3F\3G\3G\3G\3G\3G\3G\3H\3H\3H\3H\3H\3I\3I\3J\3J\3J\3J\3J\3J\3J\5J"+ - "\u0279\nJ\3K\3K\3L\3L\3L\3M\3M\3N\3N\3N\3O\3O\3P\3P\3Q\3Q\3R\3R\3S\3S"+ - "\3T\3T\3T\3U\3U\3V\3V\3V\3V\7V\u0298\nV\fV\16V\u029b\13V\3V\3V\3W\6W\u02a0"+ - "\nW\rW\16W\u02a1\3X\6X\u02a5\nX\rX\16X\u02a6\3X\3X\7X\u02ab\nX\fX\16X"+ - "\u02ae\13X\3X\3X\6X\u02b2\nX\rX\16X\u02b3\3X\6X\u02b7\nX\rX\16X\u02b8"+ - "\3X\3X\7X\u02bd\nX\fX\16X\u02c0\13X\5X\u02c2\nX\3X\3X\3X\3X\6X\u02c8\n"+ - "X\rX\16X\u02c9\3X\3X\5X\u02ce\nX\3Y\3Y\5Y\u02d2\nY\3Y\3Y\3Y\7Y\u02d7\n"+ - "Y\fY\16Y\u02da\13Y\3Z\3Z\3Z\3Z\6Z\u02e0\nZ\rZ\16Z\u02e1\3[\3[\3[\3[\6"+ - "[\u02e8\n[\r[\16[\u02e9\3\\\3\\\3\\\3\\\7\\\u02f0\n\\\f\\\16\\\u02f3\13"+ - "\\\3\\\3\\\3]\3]\3]\3]\7]\u02fb\n]\f]\16]\u02fe\13]\3]\3]\3^\3^\5^\u0304"+ - "\n^\3^\6^\u0307\n^\r^\16^\u0308\3_\3_\3`\3`\3a\3a\3a\3a\7a\u0313\na\f"+ - "a\16a\u0316\13a\3a\5a\u0319\na\3a\5a\u031c\na\3a\3a\3b\3b\3b\3b\3b\7b"+ - "\u0325\nb\fb\16b\u0328\13b\3b\3b\3b\3b\3b\3c\6c\u0330\nc\rc\16c\u0331"+ - "\3c\3c\3d\3d\3\u0326\2e\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\62"+ - "c\63e\64g\65i\66k\67m8o9q:s;u{?}@\177A\u0081B\u0083C\u0085D\u0087"+ - "E\u0089F\u008bG\u008dH\u008fI\u0091J\u0093K\u0095L\u0097M\u0099N\u009b"+ - "O\u009dP\u009fQ\u00a1R\u00a3S\u00a5T\u00a7U\u00a9V\u00abW\u00adX\u00af"+ - "Y\u00b1Z\u00b3[\u00b5\\\u00b7]\u00b9^\u00bb\2\u00bd\2\u00bf\2\u00c1_\u00c3"+ - "`\u00c5a\u00c7b\3\2\f\3\2))\4\2BBaa\5\2<\3\2\2\2\u017b\u017c"+ - "\7H\2\2\u017c\u017d\7W\2\2\u017d\u017e\7P\2\2\u017e\u017f\7E\2\2\u017f"+ - "\u0180\7V\2\2\u0180\u0181\7K\2\2\u0181\u0182\7Q\2\2\u0182\u0183\7P\2\2"+ - "\u0183\u0184\7U\2\2\u0184@\3\2\2\2\u0185\u0186\7I\2\2\u0186\u0187\7T\2"+ - "\2\u0187\u0188\7C\2\2\u0188\u0189\7R\2\2\u0189\u018a\7J\2\2\u018a\u018b"+ - "\7X\2\2\u018b\u018c\7K\2\2\u018c\u018d\7\\\2\2\u018dB\3\2\2\2\u018e\u018f"+ - "\7I\2\2\u018f\u0190\7T\2\2\u0190\u0191\7Q\2\2\u0191\u0192\7W\2\2\u0192"+ - "\u0193\7R\2\2\u0193D\3\2\2\2\u0194\u0195\7J\2\2\u0195\u0196\7C\2\2\u0196"+ - "\u0197\7X\2\2\u0197\u0198\7K\2\2\u0198\u0199\7P\2\2\u0199\u019a\7I\2\2"+ - "\u019aF\3\2\2\2\u019b\u019c\7K\2\2\u019c\u019d\7P\2\2\u019dH\3\2\2\2\u019e"+ - "\u019f\7K\2\2\u019f\u01a0\7P\2\2\u01a0\u01a1\7P\2\2\u01a1\u01a2\7G\2\2"+ - "\u01a2\u01a3\7T\2\2\u01a3J\3\2\2\2\u01a4\u01a5\7K\2\2\u01a5\u01a6\7U\2"+ - "\2\u01a6L\3\2\2\2\u01a7\u01a8\7L\2\2\u01a8\u01a9\7Q\2\2\u01a9\u01aa\7"+ - "K\2\2\u01aa\u01ab\7P\2\2\u01abN\3\2\2\2\u01ac\u01ad\7N\2\2\u01ad\u01ae"+ - "\7G\2\2\u01ae\u01af\7H\2\2\u01af\u01b0\7V\2\2\u01b0P\3\2\2\2\u01b1\u01b2"+ - "\7N\2\2\u01b2\u01b3\7K\2\2\u01b3\u01b4\7M\2\2\u01b4\u01b5\7G\2\2\u01b5"+ - "R\3\2\2\2\u01b6\u01b7\7N\2\2\u01b7\u01b8\7K\2\2\u01b8\u01b9\7O\2\2\u01b9"+ - "\u01ba\7K\2\2\u01ba\u01bb\7V\2\2\u01bbT\3\2\2\2\u01bc\u01bd\7O\2\2\u01bd"+ - "\u01be\7C\2\2\u01be\u01bf\7R\2\2\u01bf\u01c0\7R\2\2\u01c0\u01c1\7G\2\2"+ - "\u01c1\u01c2\7F\2\2\u01c2V\3\2\2\2\u01c3\u01c4\7O\2\2\u01c4\u01c5\7C\2"+ - "\2\u01c5\u01c6\7V\2\2\u01c6\u01c7\7E\2\2\u01c7\u01c8\7J\2\2\u01c8X\3\2"+ - "\2\2\u01c9\u01ca\7P\2\2\u01ca\u01cb\7C\2\2\u01cb\u01cc\7V\2\2\u01cc\u01cd"+ - "\7W\2\2\u01cd\u01ce\7T\2\2\u01ce\u01cf\7C\2\2\u01cf\u01d0\7N\2\2\u01d0"+ - "Z\3\2\2\2\u01d1\u01d2\7P\2\2\u01d2\u01d3\7Q\2\2\u01d3\u01d4\7V\2\2\u01d4"+ - "\\\3\2\2\2\u01d5\u01d6\7P\2\2\u01d6\u01d7\7W\2\2\u01d7\u01d8\7N\2\2\u01d8"+ - "\u01d9\7N\2\2\u01d9^\3\2\2\2\u01da\u01db\7Q\2\2\u01db\u01dc\7P\2\2\u01dc"+ - "`\3\2\2\2\u01dd\u01de\7Q\2\2\u01de\u01df\7R\2\2\u01df\u01e0\7V\2\2\u01e0"+ - "\u01e1\7K\2\2\u01e1\u01e2\7O\2\2\u01e2\u01e3\7K\2\2\u01e3\u01e4\7\\\2"+ - "\2\u01e4\u01e5\7G\2\2\u01e5\u01e6\7F\2\2\u01e6b\3\2\2\2\u01e7\u01e8\7"+ - "Q\2\2\u01e8\u01e9\7T\2\2\u01e9d\3\2\2\2\u01ea\u01eb\7Q\2\2\u01eb\u01ec"+ - "\7T\2\2\u01ec\u01ed\7F\2\2\u01ed\u01ee\7G\2\2\u01ee\u01ef\7T\2\2\u01ef"+ - "f\3\2\2\2\u01f0\u01f1\7Q\2\2\u01f1\u01f2\7W\2\2\u01f2\u01f3\7V\2\2\u01f3"+ - "\u01f4\7G\2\2\u01f4\u01f5\7T\2\2\u01f5h\3\2\2\2\u01f6\u01f7\7R\2\2\u01f7"+ - "\u01f8\7C\2\2\u01f8\u01f9\7T\2\2\u01f9\u01fa\7U\2\2\u01fa\u01fb\7G\2\2"+ - "\u01fb\u01fc\7F\2\2\u01fcj\3\2\2\2\u01fd\u01fe\7R\2\2\u01fe\u01ff\7J\2"+ - "\2\u01ff\u0200\7[\2\2\u0200\u0201\7U\2\2\u0201\u0202\7K\2\2\u0202\u0203"+ - "\7E\2\2\u0203\u0204\7C\2\2\u0204\u0205\7N\2\2\u0205l\3\2\2\2\u0206\u0207"+ - "\7R\2\2\u0207\u0208\7N\2\2\u0208\u0209\7C\2\2\u0209\u020a\7P\2\2\u020a"+ - "n\3\2\2\2\u020b\u020c\7T\2\2\u020c\u020d\7K\2\2\u020d\u020e\7I\2\2\u020e"+ - "\u020f\7J\2\2\u020f\u0210\7V\2\2\u0210p\3\2\2\2\u0211\u0212\7T\2\2\u0212"+ - "\u0213\7N\2\2\u0213\u0214\7K\2\2\u0214\u0215\7M\2\2\u0215\u0216\7G\2\2"+ - "\u0216r\3\2\2\2\u0217\u0218\7S\2\2\u0218\u0219\7W\2\2\u0219\u021a\7G\2"+ - "\2\u021a\u021b\7T\2\2\u021b\u021c\7[\2\2\u021ct\3\2\2\2\u021d\u021e\7"+ - "U\2\2\u021e\u021f\7E\2\2\u021f\u0220\7J\2\2\u0220\u0221\7G\2\2\u0221\u0222"+ - "\7O\2\2\u0222\u0223\7C\2\2\u0223\u0224\7U\2\2\u0224v\3\2\2\2\u0225\u0226"+ - "\7U\2\2\u0226\u0227\7G\2\2\u0227\u0228\7N\2\2\u0228\u0229\7G\2\2\u0229"+ - "\u022a\7E\2\2\u022a\u022b\7V\2\2\u022bx\3\2\2\2\u022c\u022d\7U\2\2\u022d"+ - "\u022e\7J\2\2\u022e\u022f\7Q\2\2\u022f\u0230\7Y\2\2\u0230z\3\2\2\2\u0231"+ - "\u0232\7U\2\2\u0232\u0233\7[\2\2\u0233\u0234\7U\2\2\u0234|\3\2\2\2\u0235"+ - "\u0236\7V\2\2\u0236\u0237\7C\2\2\u0237\u0238\7D\2\2\u0238\u0239\7N\2\2"+ - "\u0239\u023a\7G\2\2\u023a~\3\2\2\2\u023b\u023c\7V\2\2\u023c\u023d\7C\2"+ - "\2\u023d\u023e\7D\2\2\u023e\u023f\7N\2\2\u023f\u0240\7G\2\2\u0240\u0241"+ - "\7U\2\2\u0241\u0080\3\2\2\2\u0242\u0243\7V\2\2\u0243\u0244\7G\2\2\u0244"+ - "\u0245\7Z\2\2\u0245\u0246\7V\2\2\u0246\u0082\3\2\2\2\u0247\u0248\7V\2"+ - "\2\u0248\u0249\7T\2\2\u0249\u024a\7W\2\2\u024a\u024b\7G\2\2\u024b\u0084"+ - "\3\2\2\2\u024c\u024d\7V\2\2\u024d\u024e\7[\2\2\u024e\u024f\7R\2\2\u024f"+ - "\u0250\7G\2\2\u0250\u0086\3\2\2\2\u0251\u0252\7V\2\2\u0252\u0253\7[\2"+ - "\2\u0253\u0254\7R\2\2\u0254\u0255\7G\2\2\u0255\u0256\7U\2\2\u0256\u0088"+ - "\3\2\2\2\u0257\u0258\7W\2\2\u0258\u0259\7U\2\2\u0259\u025a\7K\2\2\u025a"+ - "\u025b\7P\2\2\u025b\u025c\7I\2\2\u025c\u008a\3\2\2\2\u025d\u025e\7X\2"+ - "\2\u025e\u025f\7G\2\2\u025f\u0260\7T\2\2\u0260\u0261\7K\2\2\u0261\u0262"+ - "\7H\2\2\u0262\u0263\7[\2\2\u0263\u008c\3\2\2\2\u0264\u0265\7Y\2\2\u0265"+ - "\u0266\7J\2\2\u0266\u0267\7G\2\2\u0267\u0268\7T\2\2\u0268\u0269\7G\2\2"+ - "\u0269\u008e\3\2\2\2\u026a\u026b\7Y\2\2\u026b\u026c\7K\2\2\u026c\u026d"+ - "\7V\2\2\u026d\u026e\7J\2\2\u026e\u0090\3\2\2\2\u026f\u0270\7?\2\2\u0270"+ - "\u0092\3\2\2\2\u0271\u0272\7>\2\2\u0272\u0279\7@\2\2\u0273\u0274\7#\2"+ - "\2\u0274\u0279\7?\2\2\u0275\u0276\7>\2\2\u0276\u0277\7?\2\2\u0277\u0279"+ - "\7@\2\2\u0278\u0271\3\2\2\2\u0278\u0273\3\2\2\2\u0278\u0275\3\2\2\2\u0279"+ - "\u0094\3\2\2\2\u027a\u027b\7>\2\2\u027b\u0096\3\2\2\2\u027c\u027d\7>\2"+ - "\2\u027d\u027e\7?\2\2\u027e\u0098\3\2\2\2\u027f\u0280\7@\2\2\u0280\u009a"+ - "\3\2\2\2\u0281\u0282\7@\2\2\u0282\u0283\7?\2\2\u0283\u009c\3\2\2\2\u0284"+ - "\u0285\7-\2\2\u0285\u009e\3\2\2\2\u0286\u0287\7/\2\2\u0287\u00a0\3\2\2"+ - "\2\u0288\u0289\7,\2\2\u0289\u00a2\3\2\2\2\u028a\u028b\7\61\2\2\u028b\u00a4"+ - "\3\2\2\2\u028c\u028d\7\'\2\2\u028d\u00a6\3\2\2\2\u028e\u028f\7~\2\2\u028f"+ - "\u0290\7~\2\2\u0290\u00a8\3\2\2\2\u0291\u0292\7\60\2\2\u0292\u00aa\3\2"+ - "\2\2\u0293\u0299\7)\2\2\u0294\u0298\n\2\2\2\u0295\u0296\7)\2\2\u0296\u0298"+ - "\7)\2\2\u0297\u0294\3\2\2\2\u0297\u0295\3\2\2\2\u0298\u029b\3\2\2\2\u0299"+ - "\u0297\3\2\2\2\u0299\u029a\3\2\2\2\u029a\u029c\3\2\2\2\u029b\u0299\3\2"+ - "\2\2\u029c\u029d\7)\2\2\u029d\u00ac\3\2\2\2\u029e\u02a0\5\u00bd_\2\u029f"+ - "\u029e\3\2\2\2\u02a0\u02a1\3\2\2\2\u02a1\u029f\3\2\2\2\u02a1\u02a2\3\2"+ - "\2\2\u02a2\u00ae\3\2\2\2\u02a3\u02a5\5\u00bd_\2\u02a4\u02a3\3\2\2\2\u02a5"+ - "\u02a6\3\2\2\2\u02a6\u02a4\3\2\2\2\u02a6\u02a7\3\2\2\2\u02a7\u02a8\3\2"+ - "\2\2\u02a8\u02ac\5\u00a9U\2\u02a9\u02ab\5\u00bd_\2\u02aa\u02a9\3\2\2\2"+ - "\u02ab\u02ae\3\2\2\2\u02ac\u02aa\3\2\2\2\u02ac\u02ad\3\2\2\2\u02ad\u02ce"+ - "\3\2\2\2\u02ae\u02ac\3\2\2\2\u02af\u02b1\5\u00a9U\2\u02b0\u02b2\5\u00bd"+ - "_\2\u02b1\u02b0\3\2\2\2\u02b2\u02b3\3\2\2\2\u02b3\u02b1\3\2\2\2\u02b3"+ - "\u02b4\3\2\2\2\u02b4\u02ce\3\2\2\2\u02b5\u02b7\5\u00bd_\2\u02b6\u02b5"+ - "\3\2\2\2\u02b7\u02b8\3\2\2\2\u02b8\u02b6\3\2\2\2\u02b8\u02b9\3\2\2\2\u02b9"+ - "\u02c1\3\2\2\2\u02ba\u02be\5\u00a9U\2\u02bb\u02bd\5\u00bd_\2\u02bc\u02bb"+ - "\3\2\2\2\u02bd\u02c0\3\2\2\2\u02be\u02bc\3\2\2\2\u02be\u02bf\3\2\2\2\u02bf"+ - "\u02c2\3\2\2\2\u02c0\u02be\3\2\2\2\u02c1\u02ba\3\2\2\2\u02c1\u02c2\3\2"+ - "\2\2\u02c2\u02c3\3\2\2\2\u02c3\u02c4\5\u00bb^\2\u02c4\u02ce\3\2\2\2\u02c5"+ - "\u02c7\5\u00a9U\2\u02c6\u02c8\5\u00bd_\2\u02c7\u02c6\3\2\2\2\u02c8\u02c9"+ - "\3\2\2\2\u02c9\u02c7\3\2\2\2\u02c9\u02ca\3\2\2\2\u02ca\u02cb\3\2\2\2\u02cb"+ - "\u02cc\5\u00bb^\2\u02cc\u02ce\3\2\2\2\u02cd\u02a4\3\2\2\2\u02cd\u02af"+ - "\3\2\2\2\u02cd\u02b6\3\2\2\2\u02cd\u02c5\3\2\2\2\u02ce\u00b0\3\2\2\2\u02cf"+ - "\u02d2\5\u00bf`\2\u02d0\u02d2\7a\2\2\u02d1\u02cf\3\2\2\2\u02d1\u02d0\3"+ - "\2\2\2\u02d2\u02d8\3\2\2\2\u02d3\u02d7\5\u00bf`\2\u02d4\u02d7\5\u00bd"+ - "_\2\u02d5\u02d7\t\3\2\2\u02d6\u02d3\3\2\2\2\u02d6\u02d4\3\2\2\2\u02d6"+ - "\u02d5\3\2\2\2\u02d7\u02da\3\2\2\2\u02d8\u02d6\3\2\2\2\u02d8\u02d9\3\2"+ - "\2\2\u02d9\u00b2\3\2\2\2\u02da\u02d8\3\2\2\2\u02db\u02df\5\u00bd_\2\u02dc"+ - "\u02e0\5\u00bf`\2\u02dd\u02e0\5\u00bd_\2\u02de\u02e0\t\4\2\2\u02df\u02dc"+ - "\3\2\2\2\u02df\u02dd\3\2\2\2\u02df\u02de\3\2\2\2\u02e0\u02e1\3\2\2\2\u02e1"+ - "\u02df\3\2\2\2\u02e1\u02e2\3\2\2\2\u02e2\u00b4\3\2\2\2\u02e3\u02e8\5\u00bf"+ - "`\2\u02e4\u02e8\5\u00bd_\2\u02e5\u02e8\t\3\2\2\u02e6\u02e8\5\u00a1Q\2"+ - "\u02e7\u02e3\3\2\2\2\u02e7\u02e4\3\2\2\2\u02e7\u02e5\3\2\2\2\u02e7\u02e6"+ - "\3\2\2\2\u02e8\u02e9\3\2\2\2\u02e9\u02e7\3\2\2\2\u02e9\u02ea\3\2\2\2\u02ea"+ - "\u00b6\3\2\2\2\u02eb\u02f1\7$\2\2\u02ec\u02f0\n\5\2\2\u02ed\u02ee\7$\2"+ - "\2\u02ee\u02f0\7$\2\2\u02ef\u02ec\3\2\2\2\u02ef\u02ed\3\2\2\2\u02f0\u02f3"+ - "\3\2\2\2\u02f1\u02ef\3\2\2\2\u02f1\u02f2\3\2\2\2\u02f2\u02f4\3\2\2\2\u02f3"+ - "\u02f1\3\2\2\2\u02f4\u02f5\7$\2\2\u02f5\u00b8\3\2\2\2\u02f6\u02fc\7b\2"+ - "\2\u02f7\u02fb\n\6\2\2\u02f8\u02f9\7b\2\2\u02f9\u02fb\7b\2\2\u02fa\u02f7"+ - "\3\2\2\2\u02fa\u02f8\3\2\2\2\u02fb\u02fe\3\2\2\2\u02fc\u02fa\3\2\2\2\u02fc"+ - "\u02fd\3\2\2\2\u02fd\u02ff\3\2\2\2\u02fe\u02fc\3\2\2\2\u02ff\u0300\7b"+ - "\2\2\u0300\u00ba\3\2\2\2\u0301\u0303\7G\2\2\u0302\u0304\t\7\2\2\u0303"+ - "\u0302\3\2\2\2\u0303\u0304\3\2\2\2\u0304\u0306\3\2\2\2\u0305\u0307\5\u00bd"+ - "_\2\u0306\u0305\3\2\2\2\u0307\u0308\3\2\2\2\u0308\u0306\3\2\2\2\u0308"+ - "\u0309\3\2\2\2\u0309\u00bc\3\2\2\2\u030a\u030b\t\b\2\2\u030b\u00be\3\2"+ - "\2\2\u030c\u030d\t\t\2\2\u030d\u00c0\3\2\2\2\u030e\u030f\7/\2\2\u030f"+ - "\u0310\7/\2\2\u0310\u0314\3\2\2\2\u0311\u0313\n\n\2\2\u0312\u0311\3\2"+ - "\2\2\u0313\u0316\3\2\2\2\u0314\u0312\3\2\2\2\u0314\u0315\3\2\2\2\u0315"+ - "\u0318\3\2\2\2\u0316\u0314\3\2\2\2\u0317\u0319\7\17\2\2\u0318\u0317\3"+ - "\2\2\2\u0318\u0319\3\2\2\2\u0319\u031b\3\2\2\2\u031a\u031c\7\f\2\2\u031b"+ - "\u031a\3\2\2\2\u031b\u031c\3\2\2\2\u031c\u031d\3\2\2\2\u031d\u031e\ba"+ - "\2\2\u031e\u00c2\3\2\2\2\u031f\u0320\7\61\2\2\u0320\u0321\7,\2\2\u0321"+ - "\u0326\3\2\2\2\u0322\u0325\5\u00c3b\2\u0323\u0325\13\2\2\2\u0324\u0322"+ - "\3\2\2\2\u0324\u0323\3\2\2\2\u0325\u0328\3\2\2\2\u0326\u0327\3\2\2\2\u0326"+ - "\u0324\3\2\2\2\u0327\u0329\3\2\2\2\u0328\u0326\3\2\2\2\u0329\u032a\7,"+ - "\2\2\u032a\u032b\7\61\2\2\u032b\u032c\3\2\2\2\u032c\u032d\bb\2\2\u032d"+ - "\u00c4\3\2\2\2\u032e\u0330\t\13\2\2\u032f\u032e\3\2\2\2\u0330\u0331\3"+ - "\2\2\2\u0331\u032f\3\2\2\2\u0331\u0332\3\2\2\2\u0332\u0333\3\2\2\2\u0333"+ - "\u0334\bc\2\2\u0334\u00c6\3\2\2\2\u0335\u0336\13\2\2\2\u0336\u00c8\3\2"+ - "\2\2\"\2\u0278\u0297\u0299\u02a1\u02a6\u02ac\u02b3\u02b8\u02be\u02c1\u02c9"+ - "\u02cd\u02d1\u02d6\u02d8\u02df\u02e1\u02e7\u02e9\u02ef\u02f1\u02fa\u02fc"+ - "\u0303\u0308\u0314\u0318\u031b\u0324\u0326\u0331\3\2\3\2"; + "`\t`\4a\ta\4b\tb\4c\tc\4d\td\4e\te\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\24\3\24\3\24\3\24\3\24\3\25\3\25\3\25\3\25\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\30\3\30\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\32\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\35\3\35\3\35\3\35\3\35\3\35\3\35\3\36\3\36\3\36"+ + "\3\36\3\36\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\60\3\60\3\60"+ + "\3\61\3\61\3\61\3\61\3\61\3\61\3\61\3\61\3\61\3\61\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\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\66\3\67"+ + "\3\67\3\67\3\67\3\67\38\38\38\38\38\38\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@\3A\3A\3A\3A\3A\3B\3B\3"+ + "B\3B\3B\3C\3C\3C\3C\3C\3D\3D\3D\3D\3D\3D\3E\3E\3E\3E\3E\3E\3F\3F\3F\3"+ + "F\3F\3F\3F\3G\3G\3G\3G\3G\3G\3H\3H\3H\3H\3H\3I\3I\3J\3J\3J\3J\3J\3J\3"+ + "J\5J\u027b\nJ\3K\3K\3L\3L\3L\3M\3M\3N\3N\3N\3O\3O\3P\3P\3Q\3Q\3R\3R\3"+ + "S\3S\3T\3T\3T\3U\3U\3V\3V\3W\3W\3W\3W\7W\u029c\nW\fW\16W\u029f\13W\3W"+ + "\3W\3X\6X\u02a4\nX\rX\16X\u02a5\3Y\6Y\u02a9\nY\rY\16Y\u02aa\3Y\3Y\7Y\u02af"+ + "\nY\fY\16Y\u02b2\13Y\3Y\3Y\6Y\u02b6\nY\rY\16Y\u02b7\3Y\6Y\u02bb\nY\rY"+ + "\16Y\u02bc\3Y\3Y\7Y\u02c1\nY\fY\16Y\u02c4\13Y\5Y\u02c6\nY\3Y\3Y\3Y\3Y"+ + "\6Y\u02cc\nY\rY\16Y\u02cd\3Y\3Y\5Y\u02d2\nY\3Z\3Z\5Z\u02d6\nZ\3Z\3Z\3"+ + "Z\7Z\u02db\nZ\fZ\16Z\u02de\13Z\3[\3[\3[\3[\6[\u02e4\n[\r[\16[\u02e5\3"+ + "\\\3\\\3\\\3\\\6\\\u02ec\n\\\r\\\16\\\u02ed\3]\3]\3]\3]\7]\u02f4\n]\f"+ + "]\16]\u02f7\13]\3]\3]\3^\3^\3^\3^\7^\u02ff\n^\f^\16^\u0302\13^\3^\3^\3"+ + "_\3_\5_\u0308\n_\3_\6_\u030b\n_\r_\16_\u030c\3`\3`\3a\3a\3b\3b\3b\3b\7"+ + "b\u0317\nb\fb\16b\u031a\13b\3b\5b\u031d\nb\3b\5b\u0320\nb\3b\3b\3c\3c"+ + "\3c\3c\3c\7c\u0329\nc\fc\16c\u032c\13c\3c\3c\3c\3c\3c\3d\6d\u0334\nd\r"+ + "d\16d\u0335\3d\3d\3e\3e\3\u032a\2f\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\67m8o9q:s;u{?}@\177A\u0081B\u0083"+ + "C\u0085D\u0087E\u0089F\u008bG\u008dH\u008fI\u0091J\u0093K\u0095L\u0097"+ + "M\u0099N\u009bO\u009dP\u009fQ\u00a1R\u00a3S\u00a5T\u00a7U\u00a9V\u00ab"+ + "W\u00adX\u00afY\u00b1Z\u00b3[\u00b5\\\u00b7]\u00b9^\u00bb_\u00bd\2\u00bf"+ + "\2\u00c1\2\u00c3`\u00c5a\u00c7b\u00c9c\3\2\f\3\2))\4\2BBaa\5\2<\3\2\2\2\u017d\u017e"+ + "\7H\2\2\u017e\u017f\7W\2\2\u017f\u0180\7P\2\2\u0180\u0181\7E\2\2\u0181"+ + "\u0182\7V\2\2\u0182\u0183\7K\2\2\u0183\u0184\7Q\2\2\u0184\u0185\7P\2\2"+ + "\u0185\u0186\7U\2\2\u0186@\3\2\2\2\u0187\u0188\7I\2\2\u0188\u0189\7T\2"+ + "\2\u0189\u018a\7C\2\2\u018a\u018b\7R\2\2\u018b\u018c\7J\2\2\u018c\u018d"+ + "\7X\2\2\u018d\u018e\7K\2\2\u018e\u018f\7\\\2\2\u018fB\3\2\2\2\u0190\u0191"+ + "\7I\2\2\u0191\u0192\7T\2\2\u0192\u0193\7Q\2\2\u0193\u0194\7W\2\2\u0194"+ + "\u0195\7R\2\2\u0195D\3\2\2\2\u0196\u0197\7J\2\2\u0197\u0198\7C\2\2\u0198"+ + "\u0199\7X\2\2\u0199\u019a\7K\2\2\u019a\u019b\7P\2\2\u019b\u019c\7I\2\2"+ + "\u019cF\3\2\2\2\u019d\u019e\7K\2\2\u019e\u019f\7P\2\2\u019fH\3\2\2\2\u01a0"+ + "\u01a1\7K\2\2\u01a1\u01a2\7P\2\2\u01a2\u01a3\7P\2\2\u01a3\u01a4\7G\2\2"+ + "\u01a4\u01a5\7T\2\2\u01a5J\3\2\2\2\u01a6\u01a7\7K\2\2\u01a7\u01a8\7U\2"+ + "\2\u01a8L\3\2\2\2\u01a9\u01aa\7L\2\2\u01aa\u01ab\7Q\2\2\u01ab\u01ac\7"+ + "K\2\2\u01ac\u01ad\7P\2\2\u01adN\3\2\2\2\u01ae\u01af\7N\2\2\u01af\u01b0"+ + "\7G\2\2\u01b0\u01b1\7H\2\2\u01b1\u01b2\7V\2\2\u01b2P\3\2\2\2\u01b3\u01b4"+ + "\7N\2\2\u01b4\u01b5\7K\2\2\u01b5\u01b6\7M\2\2\u01b6\u01b7\7G\2\2\u01b7"+ + "R\3\2\2\2\u01b8\u01b9\7N\2\2\u01b9\u01ba\7K\2\2\u01ba\u01bb\7O\2\2\u01bb"+ + "\u01bc\7K\2\2\u01bc\u01bd\7V\2\2\u01bdT\3\2\2\2\u01be\u01bf\7O\2\2\u01bf"+ + "\u01c0\7C\2\2\u01c0\u01c1\7R\2\2\u01c1\u01c2\7R\2\2\u01c2\u01c3\7G\2\2"+ + "\u01c3\u01c4\7F\2\2\u01c4V\3\2\2\2\u01c5\u01c6\7O\2\2\u01c6\u01c7\7C\2"+ + "\2\u01c7\u01c8\7V\2\2\u01c8\u01c9\7E\2\2\u01c9\u01ca\7J\2\2\u01caX\3\2"+ + "\2\2\u01cb\u01cc\7P\2\2\u01cc\u01cd\7C\2\2\u01cd\u01ce\7V\2\2\u01ce\u01cf"+ + "\7W\2\2\u01cf\u01d0\7T\2\2\u01d0\u01d1\7C\2\2\u01d1\u01d2\7N\2\2\u01d2"+ + "Z\3\2\2\2\u01d3\u01d4\7P\2\2\u01d4\u01d5\7Q\2\2\u01d5\u01d6\7V\2\2\u01d6"+ + "\\\3\2\2\2\u01d7\u01d8\7P\2\2\u01d8\u01d9\7W\2\2\u01d9\u01da\7N\2\2\u01da"+ + "\u01db\7N\2\2\u01db^\3\2\2\2\u01dc\u01dd\7Q\2\2\u01dd\u01de\7P\2\2\u01de"+ + "`\3\2\2\2\u01df\u01e0\7Q\2\2\u01e0\u01e1\7R\2\2\u01e1\u01e2\7V\2\2\u01e2"+ + "\u01e3\7K\2\2\u01e3\u01e4\7O\2\2\u01e4\u01e5\7K\2\2\u01e5\u01e6\7\\\2"+ + "\2\u01e6\u01e7\7G\2\2\u01e7\u01e8\7F\2\2\u01e8b\3\2\2\2\u01e9\u01ea\7"+ + "Q\2\2\u01ea\u01eb\7T\2\2\u01ebd\3\2\2\2\u01ec\u01ed\7Q\2\2\u01ed\u01ee"+ + "\7T\2\2\u01ee\u01ef\7F\2\2\u01ef\u01f0\7G\2\2\u01f0\u01f1\7T\2\2\u01f1"+ + "f\3\2\2\2\u01f2\u01f3\7Q\2\2\u01f3\u01f4\7W\2\2\u01f4\u01f5\7V\2\2\u01f5"+ + "\u01f6\7G\2\2\u01f6\u01f7\7T\2\2\u01f7h\3\2\2\2\u01f8\u01f9\7R\2\2\u01f9"+ + "\u01fa\7C\2\2\u01fa\u01fb\7T\2\2\u01fb\u01fc\7U\2\2\u01fc\u01fd\7G\2\2"+ + "\u01fd\u01fe\7F\2\2\u01fej\3\2\2\2\u01ff\u0200\7R\2\2\u0200\u0201\7J\2"+ + "\2\u0201\u0202\7[\2\2\u0202\u0203\7U\2\2\u0203\u0204\7K\2\2\u0204\u0205"+ + "\7E\2\2\u0205\u0206\7C\2\2\u0206\u0207\7N\2\2\u0207l\3\2\2\2\u0208\u0209"+ + "\7R\2\2\u0209\u020a\7N\2\2\u020a\u020b\7C\2\2\u020b\u020c\7P\2\2\u020c"+ + "n\3\2\2\2\u020d\u020e\7T\2\2\u020e\u020f\7K\2\2\u020f\u0210\7I\2\2\u0210"+ + "\u0211\7J\2\2\u0211\u0212\7V\2\2\u0212p\3\2\2\2\u0213\u0214\7T\2\2\u0214"+ + "\u0215\7N\2\2\u0215\u0216\7K\2\2\u0216\u0217\7M\2\2\u0217\u0218\7G\2\2"+ + "\u0218r\3\2\2\2\u0219\u021a\7S\2\2\u021a\u021b\7W\2\2\u021b\u021c\7G\2"+ + "\2\u021c\u021d\7T\2\2\u021d\u021e\7[\2\2\u021et\3\2\2\2\u021f\u0220\7"+ + "U\2\2\u0220\u0221\7E\2\2\u0221\u0222\7J\2\2\u0222\u0223\7G\2\2\u0223\u0224"+ + "\7O\2\2\u0224\u0225\7C\2\2\u0225\u0226\7U\2\2\u0226v\3\2\2\2\u0227\u0228"+ + "\7U\2\2\u0228\u0229\7G\2\2\u0229\u022a\7N\2\2\u022a\u022b\7G\2\2\u022b"+ + "\u022c\7E\2\2\u022c\u022d\7V\2\2\u022dx\3\2\2\2\u022e\u022f\7U\2\2\u022f"+ + "\u0230\7J\2\2\u0230\u0231\7Q\2\2\u0231\u0232\7Y\2\2\u0232z\3\2\2\2\u0233"+ + "\u0234\7U\2\2\u0234\u0235\7[\2\2\u0235\u0236\7U\2\2\u0236|\3\2\2\2\u0237"+ + "\u0238\7V\2\2\u0238\u0239\7C\2\2\u0239\u023a\7D\2\2\u023a\u023b\7N\2\2"+ + "\u023b\u023c\7G\2\2\u023c~\3\2\2\2\u023d\u023e\7V\2\2\u023e\u023f\7C\2"+ + "\2\u023f\u0240\7D\2\2\u0240\u0241\7N\2\2\u0241\u0242\7G\2\2\u0242\u0243"+ + "\7U\2\2\u0243\u0080\3\2\2\2\u0244\u0245\7V\2\2\u0245\u0246\7G\2\2\u0246"+ + "\u0247\7Z\2\2\u0247\u0248\7V\2\2\u0248\u0082\3\2\2\2\u0249\u024a\7V\2"+ + "\2\u024a\u024b\7T\2\2\u024b\u024c\7W\2\2\u024c\u024d\7G\2\2\u024d\u0084"+ + "\3\2\2\2\u024e\u024f\7V\2\2\u024f\u0250\7[\2\2\u0250\u0251\7R\2\2\u0251"+ + "\u0252\7G\2\2\u0252\u0086\3\2\2\2\u0253\u0254\7V\2\2\u0254\u0255\7[\2"+ + "\2\u0255\u0256\7R\2\2\u0256\u0257\7G\2\2\u0257\u0258\7U\2\2\u0258\u0088"+ + "\3\2\2\2\u0259\u025a\7W\2\2\u025a\u025b\7U\2\2\u025b\u025c\7K\2\2\u025c"+ + "\u025d\7P\2\2\u025d\u025e\7I\2\2\u025e\u008a\3\2\2\2\u025f\u0260\7X\2"+ + "\2\u0260\u0261\7G\2\2\u0261\u0262\7T\2\2\u0262\u0263\7K\2\2\u0263\u0264"+ + "\7H\2\2\u0264\u0265\7[\2\2\u0265\u008c\3\2\2\2\u0266\u0267\7Y\2\2\u0267"+ + "\u0268\7J\2\2\u0268\u0269\7G\2\2\u0269\u026a\7T\2\2\u026a\u026b\7G\2\2"+ + "\u026b\u008e\3\2\2\2\u026c\u026d\7Y\2\2\u026d\u026e\7K\2\2\u026e\u026f"+ + "\7V\2\2\u026f\u0270\7J\2\2\u0270\u0090\3\2\2\2\u0271\u0272\7?\2\2\u0272"+ + "\u0092\3\2\2\2\u0273\u0274\7>\2\2\u0274\u027b\7@\2\2\u0275\u0276\7#\2"+ + "\2\u0276\u027b\7?\2\2\u0277\u0278\7>\2\2\u0278\u0279\7?\2\2\u0279\u027b"+ + "\7@\2\2\u027a\u0273\3\2\2\2\u027a\u0275\3\2\2\2\u027a\u0277\3\2\2\2\u027b"+ + "\u0094\3\2\2\2\u027c\u027d\7>\2\2\u027d\u0096\3\2\2\2\u027e\u027f\7>\2"+ + "\2\u027f\u0280\7?\2\2\u0280\u0098\3\2\2\2\u0281\u0282\7@\2\2\u0282\u009a"+ + "\3\2\2\2\u0283\u0284\7@\2\2\u0284\u0285\7?\2\2\u0285\u009c\3\2\2\2\u0286"+ + "\u0287\7-\2\2\u0287\u009e\3\2\2\2\u0288\u0289\7/\2\2\u0289\u00a0\3\2\2"+ + "\2\u028a\u028b\7,\2\2\u028b\u00a2\3\2\2\2\u028c\u028d\7\61\2\2\u028d\u00a4"+ + "\3\2\2\2\u028e\u028f\7\'\2\2\u028f\u00a6\3\2\2\2\u0290\u0291\7~\2\2\u0291"+ + "\u0292\7~\2\2\u0292\u00a8\3\2\2\2\u0293\u0294\7\60\2\2\u0294\u00aa\3\2"+ + "\2\2\u0295\u0296\7A\2\2\u0296\u00ac\3\2\2\2\u0297\u029d\7)\2\2\u0298\u029c"+ + "\n\2\2\2\u0299\u029a\7)\2\2\u029a\u029c\7)\2\2\u029b\u0298\3\2\2\2\u029b"+ + "\u0299\3\2\2\2\u029c\u029f\3\2\2\2\u029d\u029b\3\2\2\2\u029d\u029e\3\2"+ + "\2\2\u029e\u02a0\3\2\2\2\u029f\u029d\3\2\2\2\u02a0\u02a1\7)\2\2\u02a1"+ + "\u00ae\3\2\2\2\u02a2\u02a4\5\u00bf`\2\u02a3\u02a2\3\2\2\2\u02a4\u02a5"+ + "\3\2\2\2\u02a5\u02a3\3\2\2\2\u02a5\u02a6\3\2\2\2\u02a6\u00b0\3\2\2\2\u02a7"+ + "\u02a9\5\u00bf`\2\u02a8\u02a7\3\2\2\2\u02a9\u02aa\3\2\2\2\u02aa\u02a8"+ + "\3\2\2\2\u02aa\u02ab\3\2\2\2\u02ab\u02ac\3\2\2\2\u02ac\u02b0\5\u00a9U"+ + "\2\u02ad\u02af\5\u00bf`\2\u02ae\u02ad\3\2\2\2\u02af\u02b2\3\2\2\2\u02b0"+ + "\u02ae\3\2\2\2\u02b0\u02b1\3\2\2\2\u02b1\u02d2\3\2\2\2\u02b2\u02b0\3\2"+ + "\2\2\u02b3\u02b5\5\u00a9U\2\u02b4\u02b6\5\u00bf`\2\u02b5\u02b4\3\2\2\2"+ + "\u02b6\u02b7\3\2\2\2\u02b7\u02b5\3\2\2\2\u02b7\u02b8\3\2\2\2\u02b8\u02d2"+ + "\3\2\2\2\u02b9\u02bb\5\u00bf`\2\u02ba\u02b9\3\2\2\2\u02bb\u02bc\3\2\2"+ + "\2\u02bc\u02ba\3\2\2\2\u02bc\u02bd\3\2\2\2\u02bd\u02c5\3\2\2\2\u02be\u02c2"+ + "\5\u00a9U\2\u02bf\u02c1\5\u00bf`\2\u02c0\u02bf\3\2\2\2\u02c1\u02c4\3\2"+ + "\2\2\u02c2\u02c0\3\2\2\2\u02c2\u02c3\3\2\2\2\u02c3\u02c6\3\2\2\2\u02c4"+ + "\u02c2\3\2\2\2\u02c5\u02be\3\2\2\2\u02c5\u02c6\3\2\2\2\u02c6\u02c7\3\2"+ + "\2\2\u02c7\u02c8\5\u00bd_\2\u02c8\u02d2\3\2\2\2\u02c9\u02cb\5\u00a9U\2"+ + "\u02ca\u02cc\5\u00bf`\2\u02cb\u02ca\3\2\2\2\u02cc\u02cd\3\2\2\2\u02cd"+ + "\u02cb\3\2\2\2\u02cd\u02ce\3\2\2\2\u02ce\u02cf\3\2\2\2\u02cf\u02d0\5\u00bd"+ + "_\2\u02d0\u02d2\3\2\2\2\u02d1\u02a8\3\2\2\2\u02d1\u02b3\3\2\2\2\u02d1"+ + "\u02ba\3\2\2\2\u02d1\u02c9\3\2\2\2\u02d2\u00b2\3\2\2\2\u02d3\u02d6\5\u00c1"+ + "a\2\u02d4\u02d6\7a\2\2\u02d5\u02d3\3\2\2\2\u02d5\u02d4\3\2\2\2\u02d6\u02dc"+ + "\3\2\2\2\u02d7\u02db\5\u00c1a\2\u02d8\u02db\5\u00bf`\2\u02d9\u02db\t\3"+ + "\2\2\u02da\u02d7\3\2\2\2\u02da\u02d8\3\2\2\2\u02da\u02d9\3\2\2\2\u02db"+ + "\u02de\3\2\2\2\u02dc\u02da\3\2\2\2\u02dc\u02dd\3\2\2\2\u02dd\u00b4\3\2"+ + "\2\2\u02de\u02dc\3\2\2\2\u02df\u02e3\5\u00bf`\2\u02e0\u02e4\5\u00c1a\2"+ + "\u02e1\u02e4\5\u00bf`\2\u02e2\u02e4\t\4\2\2\u02e3\u02e0\3\2\2\2\u02e3"+ + "\u02e1\3\2\2\2\u02e3\u02e2\3\2\2\2\u02e4\u02e5\3\2\2\2\u02e5\u02e3\3\2"+ + "\2\2\u02e5\u02e6\3\2\2\2\u02e6\u00b6\3\2\2\2\u02e7\u02ec\5\u00c1a\2\u02e8"+ + "\u02ec\5\u00bf`\2\u02e9\u02ec\t\3\2\2\u02ea\u02ec\5\u00a1Q\2\u02eb\u02e7"+ + "\3\2\2\2\u02eb\u02e8\3\2\2\2\u02eb\u02e9\3\2\2\2\u02eb\u02ea\3\2\2\2\u02ec"+ + "\u02ed\3\2\2\2\u02ed\u02eb\3\2\2\2\u02ed\u02ee\3\2\2\2\u02ee\u00b8\3\2"+ + "\2\2\u02ef\u02f5\7$\2\2\u02f0\u02f4\n\5\2\2\u02f1\u02f2\7$\2\2\u02f2\u02f4"+ + "\7$\2\2\u02f3\u02f0\3\2\2\2\u02f3\u02f1\3\2\2\2\u02f4\u02f7\3\2\2\2\u02f5"+ + "\u02f3\3\2\2\2\u02f5\u02f6\3\2\2\2\u02f6\u02f8\3\2\2\2\u02f7\u02f5\3\2"+ + "\2\2\u02f8\u02f9\7$\2\2\u02f9\u00ba\3\2\2\2\u02fa\u0300\7b\2\2\u02fb\u02ff"+ + "\n\6\2\2\u02fc\u02fd\7b\2\2\u02fd\u02ff\7b\2\2\u02fe\u02fb\3\2\2\2\u02fe"+ + "\u02fc\3\2\2\2\u02ff\u0302\3\2\2\2\u0300\u02fe\3\2\2\2\u0300\u0301\3\2"+ + "\2\2\u0301\u0303\3\2\2\2\u0302\u0300\3\2\2\2\u0303\u0304\7b\2\2\u0304"+ + "\u00bc\3\2\2\2\u0305\u0307\7G\2\2\u0306\u0308\t\7\2\2\u0307\u0306\3\2"+ + "\2\2\u0307\u0308\3\2\2\2\u0308\u030a\3\2\2\2\u0309\u030b\5\u00bf`\2\u030a"+ + "\u0309\3\2\2\2\u030b\u030c\3\2\2\2\u030c\u030a\3\2\2\2\u030c\u030d\3\2"+ + "\2\2\u030d\u00be\3\2\2\2\u030e\u030f\t\b\2\2\u030f\u00c0\3\2\2\2\u0310"+ + "\u0311\t\t\2\2\u0311\u00c2\3\2\2\2\u0312\u0313\7/\2\2\u0313\u0314\7/\2"+ + "\2\u0314\u0318\3\2\2\2\u0315\u0317\n\n\2\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\u031d\7\17\2\2\u031c\u031b\3\2\2\2\u031c\u031d\3"+ + "\2\2\2\u031d\u031f\3\2\2\2\u031e\u0320\7\f\2\2\u031f\u031e\3\2\2\2\u031f"+ + "\u0320\3\2\2\2\u0320\u0321\3\2\2\2\u0321\u0322\bb\2\2\u0322\u00c4\3\2"+ + "\2\2\u0323\u0324\7\61\2\2\u0324\u0325\7,\2\2\u0325\u032a\3\2\2\2\u0326"+ + "\u0329\5\u00c5c\2\u0327\u0329\13\2\2\2\u0328\u0326\3\2\2\2\u0328\u0327"+ + "\3\2\2\2\u0329\u032c\3\2\2\2\u032a\u032b\3\2\2\2\u032a\u0328\3\2\2\2\u032b"+ + "\u032d\3\2\2\2\u032c\u032a\3\2\2\2\u032d\u032e\7,\2\2\u032e\u032f\7\61"+ + "\2\2\u032f\u0330\3\2\2\2\u0330\u0331\bc\2\2\u0331\u00c6\3\2\2\2\u0332"+ + "\u0334\t\13\2\2\u0333\u0332\3\2\2\2\u0334\u0335\3\2\2\2\u0335\u0333\3"+ + "\2\2\2\u0335\u0336\3\2\2\2\u0336\u0337\3\2\2\2\u0337\u0338\bd\2\2\u0338"+ + "\u00c8\3\2\2\2\u0339\u033a\13\2\2\2\u033a\u00ca\3\2\2\2\"\2\u027a\u029b"+ + "\u029d\u02a5\u02aa\u02b0\u02b7\u02bc\u02c2\u02c5\u02cd\u02d1\u02d5\u02da"+ + "\u02dc\u02e3\u02e5\u02eb\u02ed\u02f3\u02f5\u02fe\u0300\u0307\u030c\u0318"+ + "\u031c\u031f\u0328\u032a\u0335\3\2\3\2"; public static final ATN _ATN = new ATNDeserializer().deserialize(_serializedATN.toCharArray()); static { diff --git a/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/parser/SqlBaseListener.java b/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/parser/SqlBaseListener.java index bee88386394..52306f7d716 100644 --- a/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/parser/SqlBaseListener.java +++ b/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/parser/SqlBaseListener.java @@ -718,6 +718,18 @@ interface SqlBaseListener extends ParseTreeListener { * @param ctx the parse tree */ void exitStringLiteral(SqlBaseParser.StringLiteralContext ctx); + /** + * Enter a parse tree produced by the {@code param} + * labeled alternative in {@link SqlBaseParser#constant}. + * @param ctx the parse tree + */ + void enterParam(SqlBaseParser.ParamContext ctx); + /** + * Exit a parse tree produced by the {@code param} + * labeled alternative in {@link SqlBaseParser#constant}. + * @param ctx the parse tree + */ + void exitParam(SqlBaseParser.ParamContext ctx); /** * Enter a parse tree produced by {@link SqlBaseParser#comparisonOperator}. * @param ctx the parse tree diff --git a/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/parser/SqlBaseParser.java b/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/parser/SqlBaseParser.java index b975e203853..4f2d11f69b3 100644 --- a/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/parser/SqlBaseParser.java +++ b/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/parser/SqlBaseParser.java @@ -33,9 +33,9 @@ class SqlBaseParser extends Parser { SHOW=60, SYS=61, TABLE=62, TABLES=63, TEXT=64, TRUE=65, TYPE=66, TYPES=67, USING=68, VERIFY=69, WHERE=70, WITH=71, EQ=72, NEQ=73, LT=74, LTE=75, GT=76, GTE=77, PLUS=78, MINUS=79, ASTERISK=80, SLASH=81, PERCENT=82, CONCAT=83, - DOT=84, STRING=85, INTEGER_VALUE=86, DECIMAL_VALUE=87, IDENTIFIER=88, - DIGIT_IDENTIFIER=89, TABLE_IDENTIFIER=90, QUOTED_IDENTIFIER=91, BACKQUOTED_IDENTIFIER=92, - SIMPLE_COMMENT=93, BRACKETED_COMMENT=94, WS=95, UNRECOGNIZED=96, DELIMITER=97; + DOT=84, PARAM=85, STRING=86, INTEGER_VALUE=87, DECIMAL_VALUE=88, IDENTIFIER=89, + DIGIT_IDENTIFIER=90, TABLE_IDENTIFIER=91, QUOTED_IDENTIFIER=92, BACKQUOTED_IDENTIFIER=93, + SIMPLE_COMMENT=94, BRACKETED_COMMENT=95, WS=96, UNRECOGNIZED=97, DELIMITER=98; public static final int RULE_singleStatement = 0, RULE_singleExpression = 1, RULE_statement = 2, RULE_query = 3, RULE_queryNoWith = 4, RULE_queryTerm = 5, RULE_orderBy = 6, @@ -72,7 +72,7 @@ class SqlBaseParser extends Parser { "'RLIKE'", "'QUERY'", "'SCHEMAS'", "'SELECT'", "'SHOW'", "'SYS'", "'TABLE'", "'TABLES'", "'TEXT'", "'TRUE'", "'TYPE'", "'TYPES'", "'USING'", "'VERIFY'", "'WHERE'", "'WITH'", "'='", null, "'<'", "'<='", "'>'", "'>='", "'+'", - "'-'", "'*'", "'/'", "'%'", "'||'", "'.'" + "'-'", "'*'", "'/'", "'%'", "'||'", "'.'", "'?'" }; private static final String[] _SYMBOLIC_NAMES = { null, null, null, null, null, "ALL", "ANALYZE", "ANALYZED", "AND", "ANY", @@ -85,9 +85,10 @@ class SqlBaseParser extends Parser { "QUERY", "SCHEMAS", "SELECT", "SHOW", "SYS", "TABLE", "TABLES", "TEXT", "TRUE", "TYPE", "TYPES", "USING", "VERIFY", "WHERE", "WITH", "EQ", "NEQ", "LT", "LTE", "GT", "GTE", "PLUS", "MINUS", "ASTERISK", "SLASH", "PERCENT", - "CONCAT", "DOT", "STRING", "INTEGER_VALUE", "DECIMAL_VALUE", "IDENTIFIER", - "DIGIT_IDENTIFIER", "TABLE_IDENTIFIER", "QUOTED_IDENTIFIER", "BACKQUOTED_IDENTIFIER", - "SIMPLE_COMMENT", "BRACKETED_COMMENT", "WS", "UNRECOGNIZED", "DELIMITER" + "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); @@ -342,13 +343,14 @@ class SqlBaseParser extends Parser { public TerminalNode COLUMNS() { return getToken(SqlBaseParser.COLUMNS, 0); } public TerminalNode CATALOG() { return getToken(SqlBaseParser.CATALOG, 0); } public TerminalNode TABLE() { return getToken(SqlBaseParser.TABLE, 0); } - public TerminalNode STRING() { return getToken(SqlBaseParser.STRING, 0); } public List pattern() { return getRuleContexts(PatternContext.class); } public PatternContext pattern(int i) { return getRuleContext(PatternContext.class,i); } + public TerminalNode STRING() { return getToken(SqlBaseParser.STRING, 0); } + public TerminalNode PARAM() { return getToken(SqlBaseParser.PARAM, 0); } public List LIKE() { return getTokens(SqlBaseParser.LIKE); } public TerminalNode LIKE(int i) { return getToken(SqlBaseParser.LIKE, i); @@ -758,7 +760,7 @@ class SqlBaseParser extends Parser { match(TABLES); setState(121); _la = _input.LA(1); - if (_la==LIKE || _la==STRING) { + if (((((_la - 40)) & ~0x3f) == 0 && ((1L << (_la - 40)) & ((1L << (LIKE - 40)) | (1L << (PARAM - 40)) | (1L << (STRING - 40)))) != 0)) { { setState(118); _la = _input.LA(1); @@ -820,7 +822,7 @@ class SqlBaseParser extends Parser { match(FUNCTIONS); setState(135); _la = _input.LA(1); - if (_la==LIKE || _la==STRING) { + if (((((_la - 40)) & ~0x3f) == 0 && ((1L << (_la - 40)) & ((1L << (LIKE - 40)) | (1L << (PARAM - 40)) | (1L << (STRING - 40)))) != 0)) { { setState(132); _la = _input.LA(1); @@ -888,7 +890,7 @@ class SqlBaseParser extends Parser { setState(154); _la = _input.LA(1); - if (_la==LIKE || _la==STRING) { + if (((((_la - 40)) & ~0x3f) == 0 && ((1L << (_la - 40)) & ((1L << (LIKE - 40)) | (1L << (PARAM - 40)) | (1L << (STRING - 40)))) != 0)) { { setState(151); _la = _input.LA(1); @@ -948,7 +950,13 @@ class SqlBaseParser extends Parser { setState(169); match(CATALOG); setState(170); - ((SysColumnsContext)_localctx).cluster = match(STRING); + ((SysColumnsContext)_localctx).cluster = _input.LT(1); + _la = _input.LA(1); + if ( !(_la==PARAM || _la==STRING) ) { + ((SysColumnsContext)_localctx).cluster = (Token)_errHandler.recoverInline(this); + } else { + consume(); + } } } @@ -974,7 +982,7 @@ class SqlBaseParser extends Parser { setState(184); _la = _input.LA(1); - if (_la==LIKE || _la==STRING) { + if (((((_la - 40)) & ~0x3f) == 0 && ((1L << (_la - 40)) & ((1L << (LIKE - 40)) | (1L << (PARAM - 40)) | (1L << (STRING - 40)))) != 0)) { { setState(181); _la = _input.LA(1); @@ -1741,7 +1749,7 @@ class SqlBaseParser extends Parser { match(T__0); setState(293); _la = _input.LA(1); - if ((((_la) & ~0x3f) == 0 && ((1L << _la) & ((1L << T__0) | (1L << ANALYZE) | (1L << ANALYZED) | (1L << CAST) | (1L << CATALOGS) | (1L << COLUMNS) | (1L << DEBUG) | (1L << EXECUTABLE) | (1L << EXISTS) | (1L << EXPLAIN) | (1L << EXTRACT) | (1L << FALSE) | (1L << FORMAT) | (1L << FUNCTIONS) | (1L << GRAPHVIZ) | (1L << MAPPED) | (1L << MATCH) | (1L << NOT) | (1L << NULL) | (1L << OPTIMIZED) | (1L << PARSED) | (1L << PHYSICAL) | (1L << PLAN) | (1L << RLIKE) | (1L << QUERY) | (1L << SCHEMAS) | (1L << SHOW) | (1L << SYS) | (1L << TABLES))) != 0) || ((((_la - 64)) & ~0x3f) == 0 && ((1L << (_la - 64)) & ((1L << (TEXT - 64)) | (1L << (TRUE - 64)) | (1L << (TYPE - 64)) | (1L << (TYPES - 64)) | (1L << (VERIFY - 64)) | (1L << (PLUS - 64)) | (1L << (MINUS - 64)) | (1L << (ASTERISK - 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 << DEBUG) | (1L << EXECUTABLE) | (1L << EXISTS) | (1L << EXPLAIN) | (1L << EXTRACT) | (1L << FALSE) | (1L << FORMAT) | (1L << FUNCTIONS) | (1L << GRAPHVIZ) | (1L << MAPPED) | (1L << MATCH) | (1L << NOT) | (1L << NULL) | (1L << OPTIMIZED) | (1L << PARSED) | (1L << PHYSICAL) | (1L << PLAN) | (1L << RLIKE) | (1L << QUERY) | (1L << SCHEMAS) | (1L << SHOW) | (1L << SYS) | (1L << TABLES))) != 0) || ((((_la - 64)) & ~0x3f) == 0 && ((1L << (_la - 64)) & ((1L << (TEXT - 64)) | (1L << (TRUE - 64)) | (1L << (TYPE - 64)) | (1L << (TYPES - 64)) | (1L << (VERIFY - 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)) { { setState(285); expression(); @@ -3221,6 +3229,7 @@ class SqlBaseParser extends Parser { return getToken(SqlBaseParser.STRING, i); } public TerminalNode ESCAPE() { return getToken(SqlBaseParser.ESCAPE, 0); } + public TerminalNode PARAM() { return getToken(SqlBaseParser.PARAM, 0); } public PatternContext(ParserRuleContext parent, int invokingState) { super(parent, invokingState); } @@ -3244,22 +3253,36 @@ class SqlBaseParser extends Parser { PatternContext _localctx = new PatternContext(_ctx, getState()); enterRule(_localctx, 48, RULE_pattern); try { - enterOuterAlt(_localctx, 1); - { - setState(508); - ((PatternContext)_localctx).value = match(STRING); - setState(511); - _errHandler.sync(this); - switch ( getInterpreter().adaptivePredict(_input,75,_ctx) ) { - case 1: + setState(514); + switch (_input.LA(1)) { + case STRING: + enterOuterAlt(_localctx, 1); { - setState(509); - match(ESCAPE); - setState(510); - ((PatternContext)_localctx).escape = match(STRING); + setState(508); + ((PatternContext)_localctx).value = match(STRING); + setState(511); + _errHandler.sync(this); + switch ( getInterpreter().adaptivePredict(_input,75,_ctx) ) { + case 1: + { + setState(509); + match(ESCAPE); + setState(510); + ((PatternContext)_localctx).escape = match(STRING); + } + break; + } } break; - } + case PARAM: + enterOuterAlt(_localctx, 2); + { + setState(513); + match(PARAM); + } + break; + default: + throw new NoViableAltException(this); } } catch (RecognitionException re) { @@ -3399,7 +3422,7 @@ class SqlBaseParser extends Parser { int _alt; enterOuterAlt(_localctx, 1); { - setState(517); + setState(520); switch (_input.LA(1)) { case T__0: case ANALYZE: @@ -3433,6 +3456,7 @@ class SqlBaseParser extends Parser { case TYPES: case VERIFY: case ASTERISK: + case PARAM: case STRING: case INTEGER_VALUE: case DECIMAL_VALUE: @@ -3445,7 +3469,7 @@ class SqlBaseParser extends Parser { _ctx = _localctx; _prevctx = _localctx; - setState(514); + setState(517); primaryExpression(); } break; @@ -3455,7 +3479,7 @@ class SqlBaseParser extends Parser { _localctx = new ArithmeticUnaryContext(_localctx); _ctx = _localctx; _prevctx = _localctx; - setState(515); + setState(518); ((ArithmeticUnaryContext)_localctx).operator = _input.LT(1); _la = _input.LA(1); if ( !(_la==PLUS || _la==MINUS) ) { @@ -3463,7 +3487,7 @@ class SqlBaseParser extends Parser { } else { consume(); } - setState(516); + setState(519); valueExpression(4); } break; @@ -3471,25 +3495,25 @@ class SqlBaseParser extends Parser { throw new NoViableAltException(this); } _ctx.stop = _input.LT(-1); - setState(531); + setState(534); _errHandler.sync(this); - _alt = getInterpreter().adaptivePredict(_input,78,_ctx); + _alt = getInterpreter().adaptivePredict(_input,79,_ctx); while ( _alt!=2 && _alt!=org.antlr.v4.runtime.atn.ATN.INVALID_ALT_NUMBER ) { if ( _alt==1 ) { if ( _parseListeners!=null ) triggerExitRuleEvent(); _prevctx = _localctx; { - setState(529); + setState(532); _errHandler.sync(this); - switch ( getInterpreter().adaptivePredict(_input,77,_ctx) ) { + switch ( getInterpreter().adaptivePredict(_input,78,_ctx) ) { case 1: { _localctx = new ArithmeticBinaryContext(new ValueExpressionContext(_parentctx, _parentState)); ((ArithmeticBinaryContext)_localctx).left = _prevctx; pushNewRecursionContext(_localctx, _startState, RULE_valueExpression); - setState(519); + setState(522); if (!(precpred(_ctx, 3))) throw new FailedPredicateException(this, "precpred(_ctx, 3)"); - setState(520); + setState(523); ((ArithmeticBinaryContext)_localctx).operator = _input.LT(1); _la = _input.LA(1); if ( !(((((_la - 80)) & ~0x3f) == 0 && ((1L << (_la - 80)) & ((1L << (ASTERISK - 80)) | (1L << (SLASH - 80)) | (1L << (PERCENT - 80)))) != 0)) ) { @@ -3497,7 +3521,7 @@ class SqlBaseParser extends Parser { } else { consume(); } - setState(521); + setState(524); ((ArithmeticBinaryContext)_localctx).right = valueExpression(4); } break; @@ -3506,9 +3530,9 @@ class SqlBaseParser extends Parser { _localctx = new ArithmeticBinaryContext(new ValueExpressionContext(_parentctx, _parentState)); ((ArithmeticBinaryContext)_localctx).left = _prevctx; pushNewRecursionContext(_localctx, _startState, RULE_valueExpression); - setState(522); + setState(525); if (!(precpred(_ctx, 2))) throw new FailedPredicateException(this, "precpred(_ctx, 2)"); - setState(523); + setState(526); ((ArithmeticBinaryContext)_localctx).operator = _input.LT(1); _la = _input.LA(1); if ( !(_la==PLUS || _la==MINUS) ) { @@ -3516,7 +3540,7 @@ class SqlBaseParser extends Parser { } else { consume(); } - setState(524); + setState(527); ((ArithmeticBinaryContext)_localctx).right = valueExpression(3); } break; @@ -3525,20 +3549,20 @@ class SqlBaseParser extends Parser { _localctx = new ComparisonContext(new ValueExpressionContext(_parentctx, _parentState)); ((ComparisonContext)_localctx).left = _prevctx; pushNewRecursionContext(_localctx, _startState, RULE_valueExpression); - setState(525); + setState(528); if (!(precpred(_ctx, 1))) throw new FailedPredicateException(this, "precpred(_ctx, 1)"); - setState(526); + setState(529); comparisonOperator(); - setState(527); + setState(530); ((ComparisonContext)_localctx).right = valueExpression(2); } break; } } } - setState(533); + setState(536); _errHandler.sync(this); - _alt = getInterpreter().adaptivePredict(_input,78,_ctx); + _alt = getInterpreter().adaptivePredict(_input,79,_ctx); } } } @@ -3763,24 +3787,24 @@ class SqlBaseParser extends Parser { enterRule(_localctx, 52, RULE_primaryExpression); int _la; try { - setState(583); + setState(586); _errHandler.sync(this); - switch ( getInterpreter().adaptivePredict(_input,83,_ctx) ) { + switch ( getInterpreter().adaptivePredict(_input,84,_ctx) ) { case 1: _localctx = new CastContext(_localctx); enterOuterAlt(_localctx, 1); { - setState(534); - match(CAST); - setState(535); - match(T__0); - setState(536); - expression(); setState(537); - match(AS); + match(CAST); setState(538); - dataType(); + match(T__0); setState(539); + expression(); + setState(540); + match(AS); + setState(541); + dataType(); + setState(542); match(T__1); } break; @@ -3788,17 +3812,17 @@ class SqlBaseParser extends Parser { _localctx = new ExtractContext(_localctx); enterOuterAlt(_localctx, 2); { - setState(541); - match(EXTRACT); - setState(542); - match(T__0); - setState(543); - ((ExtractContext)_localctx).field = identifier(); setState(544); - match(FROM); + match(EXTRACT); setState(545); - valueExpression(0); + match(T__0); setState(546); + ((ExtractContext)_localctx).field = identifier(); + setState(547); + match(FROM); + setState(548); + valueExpression(0); + setState(549); match(T__1); } break; @@ -3806,7 +3830,7 @@ class SqlBaseParser extends Parser { _localctx = new ConstantDefaultContext(_localctx); enterOuterAlt(_localctx, 3); { - setState(548); + setState(551); constant(); } break; @@ -3814,7 +3838,7 @@ class SqlBaseParser extends Parser { _localctx = new StarContext(_localctx); enterOuterAlt(_localctx, 4); { - setState(549); + setState(552); match(ASTERISK); } break; @@ -3822,18 +3846,18 @@ class SqlBaseParser extends Parser { _localctx = new StarContext(_localctx); enterOuterAlt(_localctx, 5); { - setState(553); + setState(556); _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) | (1L << SHOW) | (1L << SYS) | (1L << TABLES))) != 0) || ((((_la - 64)) & ~0x3f) == 0 && ((1L << (_la - 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(550); + setState(553); qualifiedName(); - setState(551); + setState(554); match(DOT); } } - setState(555); + setState(558); match(ASTERISK); } break; @@ -3841,45 +3865,45 @@ class SqlBaseParser extends Parser { _localctx = new FunctionCallContext(_localctx); enterOuterAlt(_localctx, 6); { - setState(556); + setState(559); identifier(); - setState(557); + setState(560); match(T__0); - setState(569); + setState(572); _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 << DEBUG) | (1L << DISTINCT) | (1L << EXECUTABLE) | (1L << EXISTS) | (1L << EXPLAIN) | (1L << EXTRACT) | (1L << FALSE) | (1L << FORMAT) | (1L << FUNCTIONS) | (1L << GRAPHVIZ) | (1L << MAPPED) | (1L << MATCH) | (1L << NOT) | (1L << NULL) | (1L << OPTIMIZED) | (1L << PARSED) | (1L << PHYSICAL) | (1L << PLAN) | (1L << RLIKE) | (1L << QUERY) | (1L << SCHEMAS) | (1L << SHOW) | (1L << SYS) | (1L << TABLES))) != 0) || ((((_la - 64)) & ~0x3f) == 0 && ((1L << (_la - 64)) & ((1L << (TEXT - 64)) | (1L << (TRUE - 64)) | (1L << (TYPE - 64)) | (1L << (TYPES - 64)) | (1L << (VERIFY - 64)) | (1L << (PLUS - 64)) | (1L << (MINUS - 64)) | (1L << (ASTERISK - 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 << DEBUG) | (1L << DISTINCT) | (1L << EXECUTABLE) | (1L << EXISTS) | (1L << EXPLAIN) | (1L << EXTRACT) | (1L << FALSE) | (1L << FORMAT) | (1L << FUNCTIONS) | (1L << GRAPHVIZ) | (1L << MAPPED) | (1L << MATCH) | (1L << NOT) | (1L << NULL) | (1L << OPTIMIZED) | (1L << PARSED) | (1L << PHYSICAL) | (1L << PLAN) | (1L << RLIKE) | (1L << QUERY) | (1L << SCHEMAS) | (1L << SHOW) | (1L << SYS) | (1L << TABLES))) != 0) || ((((_la - 64)) & ~0x3f) == 0 && ((1L << (_la - 64)) & ((1L << (TEXT - 64)) | (1L << (TRUE - 64)) | (1L << (TYPE - 64)) | (1L << (TYPES - 64)) | (1L << (VERIFY - 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)) { { - setState(559); + setState(562); _la = _input.LA(1); if (_la==ALL || _la==DISTINCT) { { - setState(558); + setState(561); setQuantifier(); } } - setState(561); + setState(564); expression(); - setState(566); + setState(569); _errHandler.sync(this); _la = _input.LA(1); while (_la==T__2) { { { - setState(562); + setState(565); match(T__2); - setState(563); + setState(566); expression(); } } - setState(568); + setState(571); _errHandler.sync(this); _la = _input.LA(1); } } } - setState(571); + setState(574); match(T__1); } break; @@ -3887,11 +3911,11 @@ class SqlBaseParser extends Parser { _localctx = new SubqueryExpressionContext(_localctx); enterOuterAlt(_localctx, 7); { - setState(573); + setState(576); match(T__0); - setState(574); + setState(577); query(); - setState(575); + setState(578); match(T__1); } break; @@ -3899,7 +3923,7 @@ class SqlBaseParser extends Parser { _localctx = new ColumnReferenceContext(_localctx); enterOuterAlt(_localctx, 8); { - setState(577); + setState(580); identifier(); } break; @@ -3907,7 +3931,7 @@ class SqlBaseParser extends Parser { _localctx = new DereferenceContext(_localctx); enterOuterAlt(_localctx, 9); { - setState(578); + setState(581); qualifiedName(); } break; @@ -3915,11 +3939,11 @@ class SqlBaseParser extends Parser { _localctx = new ParenthesizedExpressionContext(_localctx); enterOuterAlt(_localctx, 10); { - setState(579); + setState(582); match(T__0); - setState(580); + setState(583); expression(); - setState(581); + setState(584); match(T__1); } break; @@ -3984,6 +4008,23 @@ class SqlBaseParser extends Parser { else return visitor.visitChildren(this); } } + public static class ParamContext extends ConstantContext { + public TerminalNode PARAM() { return getToken(SqlBaseParser.PARAM, 0); } + public ParamContext(ConstantContext ctx) { copyFrom(ctx); } + @Override + public void enterRule(ParseTreeListener listener) { + if ( listener instanceof SqlBaseListener ) ((SqlBaseListener)listener).enterParam(this); + } + @Override + public void exitRule(ParseTreeListener listener) { + if ( listener instanceof SqlBaseListener ) ((SqlBaseListener)listener).exitParam(this); + } + @Override + public T accept(ParseTreeVisitor visitor) { + if ( visitor instanceof SqlBaseVisitor ) return ((SqlBaseVisitor)visitor).visitParam(this); + else return visitor.visitChildren(this); + } + } public static class NumericLiteralContext extends ConstantContext { public NumberContext number() { return getRuleContext(NumberContext.class,0); @@ -4028,13 +4069,13 @@ class SqlBaseParser extends Parser { enterRule(_localctx, 54, RULE_constant); try { int _alt; - setState(593); + setState(597); switch (_input.LA(1)) { case NULL: _localctx = new NullLiteralContext(_localctx); enterOuterAlt(_localctx, 1); { - setState(585); + setState(588); match(NULL); } break; @@ -4043,7 +4084,7 @@ class SqlBaseParser extends Parser { _localctx = new NumericLiteralContext(_localctx); enterOuterAlt(_localctx, 2); { - setState(586); + setState(589); number(); } break; @@ -4052,7 +4093,7 @@ class SqlBaseParser extends Parser { _localctx = new BooleanLiteralContext(_localctx); enterOuterAlt(_localctx, 3); { - setState(587); + setState(590); booleanValue(); } break; @@ -4060,7 +4101,7 @@ class SqlBaseParser extends Parser { _localctx = new StringLiteralContext(_localctx); enterOuterAlt(_localctx, 4); { - setState(589); + setState(592); _errHandler.sync(this); _alt = 1; do { @@ -4068,7 +4109,7 @@ class SqlBaseParser extends Parser { case 1: { { - setState(588); + setState(591); match(STRING); } } @@ -4076,12 +4117,20 @@ class SqlBaseParser extends Parser { default: throw new NoViableAltException(this); } - setState(591); + setState(594); _errHandler.sync(this); - _alt = getInterpreter().adaptivePredict(_input,84,_ctx); + _alt = getInterpreter().adaptivePredict(_input,85,_ctx); } while ( _alt!=2 && _alt!=org.antlr.v4.runtime.atn.ATN.INVALID_ALT_NUMBER ); } break; + case PARAM: + _localctx = new ParamContext(_localctx); + enterOuterAlt(_localctx, 5); + { + setState(596); + match(PARAM); + } + break; default: throw new NoViableAltException(this); } @@ -4130,7 +4179,7 @@ class SqlBaseParser extends Parser { try { enterOuterAlt(_localctx, 1); { - setState(595); + setState(599); _la = _input.LA(1); if ( !(((((_la - 72)) & ~0x3f) == 0 && ((1L << (_la - 72)) & ((1L << (EQ - 72)) | (1L << (NEQ - 72)) | (1L << (LT - 72)) | (1L << (LTE - 72)) | (1L << (GT - 72)) | (1L << (GTE - 72)))) != 0)) ) { _errHandler.recoverInline(this); @@ -4179,7 +4228,7 @@ class SqlBaseParser extends Parser { try { enterOuterAlt(_localctx, 1); { - setState(597); + setState(601); _la = _input.LA(1); if ( !(_la==FALSE || _la==TRUE) ) { _errHandler.recoverInline(this); @@ -4237,7 +4286,7 @@ class SqlBaseParser extends Parser { _localctx = new PrimitiveDataTypeContext(_localctx); enterOuterAlt(_localctx, 1); { - setState(599); + setState(603); identifier(); } } @@ -4289,25 +4338,25 @@ class SqlBaseParser extends Parser { int _alt; enterOuterAlt(_localctx, 1); { - setState(606); + setState(610); _errHandler.sync(this); - _alt = getInterpreter().adaptivePredict(_input,86,_ctx); + _alt = getInterpreter().adaptivePredict(_input,87,_ctx); while ( _alt!=2 && _alt!=org.antlr.v4.runtime.atn.ATN.INVALID_ALT_NUMBER ) { if ( _alt==1 ) { { { - setState(601); + setState(605); identifier(); - setState(602); + setState(606); match(DOT); } } } - setState(608); + setState(612); _errHandler.sync(this); - _alt = getInterpreter().adaptivePredict(_input,86,_ctx); + _alt = getInterpreter().adaptivePredict(_input,87,_ctx); } - setState(609); + setState(613); identifier(); } } @@ -4352,13 +4401,13 @@ class SqlBaseParser extends Parser { IdentifierContext _localctx = new IdentifierContext(_ctx, getState()); enterRule(_localctx, 64, RULE_identifier); try { - setState(613); + setState(617); switch (_input.LA(1)) { case QUOTED_IDENTIFIER: case BACKQUOTED_IDENTIFIER: enterOuterAlt(_localctx, 1); { - setState(611); + setState(615); quoteIdentifier(); } break; @@ -4391,7 +4440,7 @@ class SqlBaseParser extends Parser { case DIGIT_IDENTIFIER: enterOuterAlt(_localctx, 2); { - setState(612); + setState(616); unquoteIdentifier(); } break; @@ -4444,43 +4493,43 @@ class SqlBaseParser extends Parser { enterRule(_localctx, 66, RULE_tableIdentifier); int _la; try { - setState(627); + setState(631); _errHandler.sync(this); - switch ( getInterpreter().adaptivePredict(_input,90,_ctx) ) { + switch ( getInterpreter().adaptivePredict(_input,91,_ctx) ) { case 1: enterOuterAlt(_localctx, 1); { - setState(618); + setState(622); _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) | (1L << SHOW) | (1L << SYS) | (1L << TABLES))) != 0) || ((((_la - 64)) & ~0x3f) == 0 && ((1L << (_la - 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(615); + setState(619); ((TableIdentifierContext)_localctx).catalog = identifier(); - setState(616); + setState(620); match(T__3); } } - setState(620); + setState(624); match(TABLE_IDENTIFIER); } break; case 2: enterOuterAlt(_localctx, 2); { - setState(624); + setState(628); _errHandler.sync(this); - switch ( getInterpreter().adaptivePredict(_input,89,_ctx) ) { + switch ( getInterpreter().adaptivePredict(_input,90,_ctx) ) { case 1: { - setState(621); + setState(625); ((TableIdentifierContext)_localctx).catalog = identifier(); - setState(622); + setState(626); match(T__3); } break; } - setState(626); + setState(630); ((TableIdentifierContext)_localctx).name = identifier(); } break; @@ -4547,13 +4596,13 @@ class SqlBaseParser extends Parser { QuoteIdentifierContext _localctx = new QuoteIdentifierContext(_ctx, getState()); enterRule(_localctx, 68, RULE_quoteIdentifier); try { - setState(631); + setState(635); switch (_input.LA(1)) { case QUOTED_IDENTIFIER: _localctx = new QuotedIdentifierContext(_localctx); enterOuterAlt(_localctx, 1); { - setState(629); + setState(633); match(QUOTED_IDENTIFIER); } break; @@ -4561,7 +4610,7 @@ class SqlBaseParser extends Parser { _localctx = new BackQuotedIdentifierContext(_localctx); enterOuterAlt(_localctx, 2); { - setState(630); + setState(634); match(BACKQUOTED_IDENTIFIER); } break; @@ -4633,13 +4682,13 @@ class SqlBaseParser extends Parser { UnquoteIdentifierContext _localctx = new UnquoteIdentifierContext(_ctx, getState()); enterRule(_localctx, 70, RULE_unquoteIdentifier); try { - setState(636); + setState(640); switch (_input.LA(1)) { case IDENTIFIER: _localctx = new UnquotedIdentifierContext(_localctx); enterOuterAlt(_localctx, 1); { - setState(633); + setState(637); match(IDENTIFIER); } break; @@ -4671,7 +4720,7 @@ class SqlBaseParser extends Parser { _localctx = new UnquotedIdentifierContext(_localctx); enterOuterAlt(_localctx, 2); { - setState(634); + setState(638); nonReserved(); } break; @@ -4679,7 +4728,7 @@ class SqlBaseParser extends Parser { _localctx = new DigitIdentifierContext(_localctx); enterOuterAlt(_localctx, 3); { - setState(635); + setState(639); match(DIGIT_IDENTIFIER); } break; @@ -4748,13 +4797,13 @@ class SqlBaseParser extends Parser { NumberContext _localctx = new NumberContext(_ctx, getState()); enterRule(_localctx, 72, RULE_number); try { - setState(640); + setState(644); switch (_input.LA(1)) { case DECIMAL_VALUE: _localctx = new DecimalLiteralContext(_localctx); enterOuterAlt(_localctx, 1); { - setState(638); + setState(642); match(DECIMAL_VALUE); } break; @@ -4762,7 +4811,7 @@ class SqlBaseParser extends Parser { _localctx = new IntegerLiteralContext(_localctx); enterOuterAlt(_localctx, 2); { - setState(639); + setState(643); match(INTEGER_VALUE); } break; @@ -4833,7 +4882,7 @@ class SqlBaseParser extends Parser { try { enterOuterAlt(_localctx, 1); { - setState(642); + setState(646); _la = _input.LA(1); if ( !(((((_la - 6)) & ~0x3f) == 0 && ((1L << (_la - 6)) & ((1L << (ANALYZE - 6)) | (1L << (ANALYZED - 6)) | (1L << (CATALOGS - 6)) | (1L << (COLUMNS - 6)) | (1L << (DEBUG - 6)) | (1L << (EXECUTABLE - 6)) | (1L << (EXPLAIN - 6)) | (1L << (FORMAT - 6)) | (1L << (FUNCTIONS - 6)) | (1L << (GRAPHVIZ - 6)) | (1L << (MAPPED - 6)) | (1L << (OPTIMIZED - 6)) | (1L << (PARSED - 6)) | (1L << (PHYSICAL - 6)) | (1L << (PLAN - 6)) | (1L << (RLIKE - 6)) | (1L << (QUERY - 6)) | (1L << (SCHEMAS - 6)) | (1L << (SHOW - 6)) | (1L << (SYS - 6)) | (1L << (TABLES - 6)) | (1L << (TEXT - 6)) | (1L << (TYPE - 6)) | (1L << (TYPES - 6)) | (1L << (VERIFY - 6)))) != 0)) ) { _errHandler.recoverInline(this); @@ -4884,7 +4933,7 @@ class SqlBaseParser extends Parser { } public static final String _serializedATN = - "\3\u0430\ud6d1\u8206\uad2d\u4417\uaef1\u8d80\uaadd\3c\u0287\4\2\t\2\4"+ + "\3\u0430\ud6d1\u8206\uad2d\u4417\uaef1\u8d80\uaadd\3d\u028b\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"+ @@ -4925,144 +4974,144 @@ class SqlBaseParser extends Parser { "\7\31\u01df\n\31\f\31\16\31\u01e2\13\31\3\31\3\31\3\31\5\31\u01e7\n\31"+ "\3\31\3\31\3\31\3\31\3\31\3\31\5\31\u01ef\n\31\3\31\3\31\3\31\5\31\u01f4"+ "\n\31\3\31\3\31\3\31\3\31\5\31\u01fa\n\31\3\31\5\31\u01fd\n\31\3\32\3"+ - "\32\3\32\5\32\u0202\n\32\3\33\3\33\3\33\3\33\5\33\u0208\n\33\3\33\3\33"+ - "\3\33\3\33\3\33\3\33\3\33\3\33\3\33\3\33\7\33\u0214\n\33\f\33\16\33\u0217"+ - "\13\33\3\34\3\34\3\34\3\34\3\34\3\34\3\34\3\34\3\34\3\34\3\34\3\34\3\34"+ - "\3\34\3\34\3\34\3\34\3\34\3\34\5\34\u022c\n\34\3\34\3\34\3\34\3\34\5\34"+ - "\u0232\n\34\3\34\3\34\3\34\7\34\u0237\n\34\f\34\16\34\u023a\13\34\5\34"+ - "\u023c\n\34\3\34\3\34\3\34\3\34\3\34\3\34\3\34\3\34\3\34\3\34\3\34\3\34"+ - "\5\34\u024a\n\34\3\35\3\35\3\35\3\35\6\35\u0250\n\35\r\35\16\35\u0251"+ - "\5\35\u0254\n\35\3\36\3\36\3\37\3\37\3 \3 \3!\3!\3!\7!\u025f\n!\f!\16"+ - "!\u0262\13!\3!\3!\3\"\3\"\5\"\u0268\n\"\3#\3#\3#\5#\u026d\n#\3#\3#\3#"+ - "\3#\5#\u0273\n#\3#\5#\u0276\n#\3$\3$\5$\u027a\n$\3%\3%\3%\5%\u027f\n%"+ - "\3&\3&\5&\u0283\n&\3\'\3\'\3\'\2\4,\64(\2\4\6\b\n\f\16\20\22\24\26\30"+ - "\32\34\36 \"$&(*,.\60\62\64\668:<>@BDFHJL\2\17\b\2\7\7\t\t\31\31,,\62"+ - "\62\66\66\4\2\"\"BB\4\2\t\t\62\62\4\2\37\37%%\3\2\25\26\4\2\7\7XX\4\2"+ - "\r\r\25\25\4\2\7\7\27\27\3\2PQ\3\2RT\3\2JO\4\2\35\35CC\20\2\b\t\22\24"+ - "\31\31\33\33\36\36!\",,\62\62\668:<>?ABDEGG\u02e1\2N\3\2\2\2\4Q\3\2\2"+ - "\2\6\u00c1\3\2\2\2\b\u00cc\3\2\2\2\n\u00d0\3\2\2\2\f\u00e6\3\2\2\2\16"+ - "\u00e8\3\2\2\2\20\u00ec\3\2\2\2\22\u0108\3\2\2\2\24\u0112\3\2\2\2\26\u011c"+ - "\3\2\2\2\30\u012b\3\2\2\2\32\u012d\3\2\2\2\34\u0133\3\2\2\2\36\u0135\3"+ - "\2\2\2 \u013c\3\2\2\2\"\u014e\3\2\2\2$\u015f\3\2\2\2&\u016f\3\2\2\2(\u018a"+ - "\3\2\2\2*\u018c\3\2\2\2,\u01bd\3\2\2\2.\u01ca\3\2\2\2\60\u01fc\3\2\2\2"+ - "\62\u01fe\3\2\2\2\64\u0207\3\2\2\2\66\u0249\3\2\2\28\u0253\3\2\2\2:\u0255"+ - "\3\2\2\2<\u0257\3\2\2\2>\u0259\3\2\2\2@\u0260\3\2\2\2B\u0267\3\2\2\2D"+ - "\u0275\3\2\2\2F\u0279\3\2\2\2H\u027e\3\2\2\2J\u0282\3\2\2\2L\u0284\3\2"+ - "\2\2NO\5\6\4\2OP\7\2\2\3P\3\3\2\2\2QR\5*\26\2RS\7\2\2\3S\5\3\2\2\2T\u00c2"+ - "\5\b\5\2Uc\7\33\2\2V_\7\3\2\2WX\78\2\2X^\t\2\2\2YZ\7\36\2\2Z^\t\3\2\2"+ - "[\\\7G\2\2\\^\5<\37\2]W\3\2\2\2]Y\3\2\2\2][\3\2\2\2^a\3\2\2\2_]\3\2\2"+ - "\2_`\3\2\2\2`b\3\2\2\2a_\3\2\2\2bd\7\4\2\2cV\3\2\2\2cd\3\2\2\2de\3\2\2"+ - "\2e\u00c2\5\6\4\2fr\7\24\2\2gn\7\3\2\2hi\78\2\2im\t\4\2\2jk\7\36\2\2k"+ - "m\t\3\2\2lh\3\2\2\2lj\3\2\2\2mp\3\2\2\2nl\3\2\2\2no\3\2\2\2oq\3\2\2\2"+ - "pn\3\2\2\2qs\7\4\2\2rg\3\2\2\2rs\3\2\2\2st\3\2\2\2t\u00c2\5\6\4\2uv\7"+ - ">\2\2v{\7A\2\2wy\7*\2\2xw\3\2\2\2xy\3\2\2\2yz\3\2\2\2z|\5\62\32\2{x\3"+ - "\2\2\2{|\3\2\2\2|\u00c2\3\2\2\2}~\7>\2\2~\177\7\23\2\2\177\u0080\t\5\2"+ - "\2\u0080\u00c2\5D#\2\u0081\u0082\t\6\2\2\u0082\u00c2\5D#\2\u0083\u0084"+ - "\7>\2\2\u0084\u0089\7!\2\2\u0085\u0087\7*\2\2\u0086\u0085\3\2\2\2\u0086"+ - "\u0087\3\2\2\2\u0087\u0088\3\2\2\2\u0088\u008a\5\62\32\2\u0089\u0086\3"+ - "\2\2\2\u0089\u008a\3\2\2\2\u008a\u00c2\3\2\2\2\u008b\u008c\7>\2\2\u008c"+ - "\u00c2\7<\2\2\u008d\u008e\7?\2\2\u008e\u00c2\7\22\2\2\u008f\u0090\7?\2"+ - "\2\u0090\u0096\7A\2\2\u0091\u0093\7\21\2\2\u0092\u0094\7*\2\2\u0093\u0092"+ - "\3\2\2\2\u0093\u0094\3\2\2\2\u0094\u0095\3\2\2\2\u0095\u0097\5\62\32\2"+ - "\u0096\u0091\3\2\2\2\u0096\u0097\3\2\2\2\u0097\u009c\3\2\2\2\u0098\u009a"+ - "\7*\2\2\u0099\u0098\3\2\2\2\u0099\u009a\3\2\2\2\u009a\u009b\3\2\2\2\u009b"+ - "\u009d\5\62\32\2\u009c\u0099\3\2\2\2\u009c\u009d\3\2\2\2\u009d\u00a7\3"+ - "\2\2\2\u009e\u009f\7D\2\2\u009f\u00a4\7W\2\2\u00a0\u00a1\7\5\2\2\u00a1"+ - "\u00a3\7W\2\2\u00a2\u00a0\3\2\2\2\u00a3\u00a6\3\2\2\2\u00a4\u00a2\3\2"+ - "\2\2\u00a4\u00a5\3\2\2\2\u00a5\u00a8\3\2\2\2\u00a6\u00a4\3\2\2\2\u00a7"+ - "\u009e\3\2\2\2\u00a7\u00a8\3\2\2\2\u00a8\u00c2\3\2\2\2\u00a9\u00aa\7?"+ - "\2\2\u00aa\u00ad\7\23\2\2\u00ab\u00ac\7\21\2\2\u00ac\u00ae\7W\2\2\u00ad"+ - "\u00ab\3\2\2\2\u00ad\u00ae\3\2\2\2\u00ae\u00b4\3\2\2\2\u00af\u00b1\7@"+ - "\2\2\u00b0\u00b2\7*\2\2\u00b1\u00b0\3\2\2\2\u00b1\u00b2\3\2\2\2\u00b2"+ - "\u00b3\3\2\2\2\u00b3\u00b5\5\62\32\2\u00b4\u00af\3\2\2\2\u00b4\u00b5\3"+ - "\2\2\2\u00b5\u00ba\3\2\2\2\u00b6\u00b8\7*\2\2\u00b7\u00b6\3\2\2\2\u00b7"+ - "\u00b8\3\2\2\2\u00b8\u00b9\3\2\2\2\u00b9\u00bb\5\62\32\2\u00ba\u00b7\3"+ - "\2\2\2\u00ba\u00bb\3\2\2\2\u00bb\u00c2\3\2\2\2\u00bc\u00bd\7?\2\2\u00bd"+ - "\u00c2\7E\2\2\u00be\u00bf\7?\2\2\u00bf\u00c0\7@\2\2\u00c0\u00c2\7E\2\2"+ - "\u00c1T\3\2\2\2\u00c1U\3\2\2\2\u00c1f\3\2\2\2\u00c1u\3\2\2\2\u00c1}\3"+ - "\2\2\2\u00c1\u0081\3\2\2\2\u00c1\u0083\3\2\2\2\u00c1\u008b\3\2\2\2\u00c1"+ - "\u008d\3\2\2\2\u00c1\u008f\3\2\2\2\u00c1\u00a9\3\2\2\2\u00c1\u00bc\3\2"+ - "\2\2\u00c1\u00be\3\2\2\2\u00c2\7\3\2\2\2\u00c3\u00c4\7I\2\2\u00c4\u00c9"+ - "\5\32\16\2\u00c5\u00c6\7\5\2\2\u00c6\u00c8\5\32\16\2\u00c7\u00c5\3\2\2"+ - "\2\u00c8\u00cb\3\2\2\2\u00c9\u00c7\3\2\2\2\u00c9\u00ca\3\2\2\2\u00ca\u00cd"+ - "\3\2\2\2\u00cb\u00c9\3\2\2\2\u00cc\u00c3\3\2\2\2\u00cc\u00cd\3\2\2\2\u00cd"+ - "\u00ce\3\2\2\2\u00ce\u00cf\5\n\6\2\u00cf\t\3\2\2\2\u00d0\u00db\5\f\7\2"+ - "\u00d1\u00d2\7\64\2\2\u00d2\u00d3\7\17\2\2\u00d3\u00d8\5\16\b\2\u00d4"+ - "\u00d5\7\5\2\2\u00d5\u00d7\5\16\b\2\u00d6\u00d4\3\2\2\2\u00d7\u00da\3"+ - "\2\2\2\u00d8\u00d6\3\2\2\2\u00d8\u00d9\3\2\2\2\u00d9\u00dc\3\2\2\2\u00da"+ - "\u00d8\3\2\2\2\u00db\u00d1\3\2\2\2\u00db\u00dc\3\2\2\2\u00dc\u00df\3\2"+ - "\2\2\u00dd\u00de\7+\2\2\u00de\u00e0\t\7\2\2\u00df\u00dd\3\2\2\2\u00df"+ - "\u00e0\3\2\2\2\u00e0\13\3\2\2\2\u00e1\u00e7\5\20\t\2\u00e2\u00e3\7\3\2"+ - "\2\u00e3\u00e4\5\n\6\2\u00e4\u00e5\7\4\2\2\u00e5\u00e7\3\2\2\2\u00e6\u00e1"+ - "\3\2\2\2\u00e6\u00e2\3\2\2\2\u00e7\r\3\2\2\2\u00e8\u00ea\5*\26\2\u00e9"+ - "\u00eb\t\b\2\2\u00ea\u00e9\3\2\2\2\u00ea\u00eb\3\2\2\2\u00eb\17\3\2\2"+ - "\2\u00ec\u00ee\7=\2\2\u00ed\u00ef\5\34\17\2\u00ee\u00ed\3\2\2\2\u00ee"+ - "\u00ef\3\2\2\2\u00ef\u00f0\3\2\2\2\u00f0\u00f5\5\36\20\2\u00f1\u00f2\7"+ - "\5\2\2\u00f2\u00f4\5\36\20\2\u00f3\u00f1\3\2\2\2\u00f4\u00f7\3\2\2\2\u00f5"+ - "\u00f3\3\2\2\2\u00f5\u00f6\3\2\2\2\u00f6\u00f9\3\2\2\2\u00f7\u00f5\3\2"+ - "\2\2\u00f8\u00fa\5\22\n\2\u00f9\u00f8\3\2\2\2\u00f9\u00fa\3\2\2\2\u00fa"+ - "\u00fd\3\2\2\2\u00fb\u00fc\7H\2\2\u00fc\u00fe\5,\27\2\u00fd\u00fb\3\2"+ - "\2\2\u00fd\u00fe\3\2\2\2\u00fe\u0102\3\2\2\2\u00ff\u0100\7#\2\2\u0100"+ - "\u0101\7\17\2\2\u0101\u0103\5\24\13\2\u0102\u00ff\3\2\2\2\u0102\u0103"+ - "\3\2\2\2\u0103\u0106\3\2\2\2\u0104\u0105\7$\2\2\u0105\u0107\5,\27\2\u0106"+ - "\u0104\3\2\2\2\u0106\u0107\3\2\2\2\u0107\21\3\2\2\2\u0108\u0109\7\37\2"+ - "\2\u0109\u010e\5 \21\2\u010a\u010b\7\5\2\2\u010b\u010d\5 \21\2\u010c\u010a"+ - "\3\2\2\2\u010d\u0110\3\2\2\2\u010e\u010c\3\2\2\2\u010e\u010f\3\2\2\2\u010f"+ - "\23\3\2\2\2\u0110\u010e\3\2\2\2\u0111\u0113\5\34\17\2\u0112\u0111\3\2"+ - "\2\2\u0112\u0113\3\2\2\2\u0113\u0114\3\2\2\2\u0114\u0119\5\26\f\2\u0115"+ - "\u0116\7\5\2\2\u0116\u0118\5\26\f\2\u0117\u0115\3\2\2\2\u0118\u011b\3"+ - "\2\2\2\u0119\u0117\3\2\2\2\u0119\u011a\3\2\2\2\u011a\25\3\2\2\2\u011b"+ - "\u0119\3\2\2\2\u011c\u011d\5\30\r\2\u011d\27\3\2\2\2\u011e\u0127\7\3\2"+ - "\2\u011f\u0124\5*\26\2\u0120\u0121\7\5\2\2\u0121\u0123\5*\26\2\u0122\u0120"+ - "\3\2\2\2\u0123\u0126\3\2\2\2\u0124\u0122\3\2\2\2\u0124\u0125\3\2\2\2\u0125"+ - "\u0128\3\2\2\2\u0126\u0124\3\2\2\2\u0127\u011f\3\2\2\2\u0127\u0128\3\2"+ - "\2\2\u0128\u0129\3\2\2\2\u0129\u012c\7\4\2\2\u012a\u012c\5*\26\2\u012b"+ - "\u011e\3\2\2\2\u012b\u012a\3\2\2\2\u012c\31\3\2\2\2\u012d\u012e\5B\"\2"+ - "\u012e\u012f\7\f\2\2\u012f\u0130\7\3\2\2\u0130\u0131\5\n\6\2\u0131\u0132"+ - "\7\4\2\2\u0132\33\3\2\2\2\u0133\u0134\t\t\2\2\u0134\35\3\2\2\2\u0135\u013a"+ - "\5*\26\2\u0136\u0138\7\f\2\2\u0137\u0136\3\2\2\2\u0137\u0138\3\2\2\2\u0138"+ - "\u0139\3\2\2\2\u0139\u013b\5B\"\2\u013a\u0137\3\2\2\2\u013a\u013b\3\2"+ - "\2\2\u013b\37\3\2\2\2\u013c\u0140\5(\25\2\u013d\u013f\5\"\22\2\u013e\u013d"+ - "\3\2\2\2\u013f\u0142\3\2\2\2\u0140\u013e\3\2\2\2\u0140\u0141\3\2\2\2\u0141"+ - "!\3\2\2\2\u0142\u0140\3\2\2\2\u0143\u0144\5$\23\2\u0144\u0145\7(\2\2\u0145"+ - "\u0147\5(\25\2\u0146\u0148\5&\24\2\u0147\u0146\3\2\2\2\u0147\u0148\3\2"+ - "\2\2\u0148\u014f\3\2\2\2\u0149\u014a\7.\2\2\u014a\u014b\5$\23\2\u014b"+ - "\u014c\7(\2\2\u014c\u014d\5(\25\2\u014d\u014f\3\2\2\2\u014e\u0143\3\2"+ - "\2\2\u014e\u0149\3\2\2\2\u014f#\3\2\2\2\u0150\u0152\7&\2\2\u0151\u0150"+ - "\3\2\2\2\u0151\u0152\3\2\2\2\u0152\u0160\3\2\2\2\u0153\u0155\7)\2\2\u0154"+ - "\u0156\7\65\2\2\u0155\u0154\3\2\2\2\u0155\u0156\3\2\2\2\u0156\u0160\3"+ - "\2\2\2\u0157\u0159\79\2\2\u0158\u015a\7\65\2\2\u0159\u0158\3\2\2\2\u0159"+ - "\u015a\3\2\2\2\u015a\u0160\3\2\2\2\u015b\u015d\7 \2\2\u015c\u015e\7\65"+ - "\2\2\u015d\u015c\3\2\2\2\u015d\u015e\3\2\2\2\u015e\u0160\3\2\2\2\u015f"+ - "\u0151\3\2\2\2\u015f\u0153\3\2\2\2\u015f\u0157\3\2\2\2\u015f\u015b\3\2"+ - "\2\2\u0160%\3\2\2\2\u0161\u0162\7\61\2\2\u0162\u0170\5,\27\2\u0163\u0164"+ - "\7F\2\2\u0164\u0165\7\3\2\2\u0165\u016a\5B\"\2\u0166\u0167\7\5\2\2\u0167"+ - "\u0169\5B\"\2\u0168\u0166\3\2\2\2\u0169\u016c\3\2\2\2\u016a\u0168\3\2"+ - "\2\2\u016a\u016b\3\2\2\2\u016b\u016d\3\2\2\2\u016c\u016a\3\2\2\2\u016d"+ - "\u016e\7\4\2\2\u016e\u0170\3\2\2\2\u016f\u0161\3\2\2\2\u016f\u0163\3\2"+ - "\2\2\u0170\'\3\2\2\2\u0171\u0176\5D#\2\u0172\u0174\7\f\2\2\u0173\u0172"+ - "\3\2\2\2\u0173\u0174\3\2\2\2\u0174\u0175\3\2\2\2\u0175\u0177\5@!\2\u0176"+ - "\u0173\3\2\2\2\u0176\u0177\3\2\2\2\u0177\u018b\3\2\2\2\u0178\u0179\7\3"+ - "\2\2\u0179\u017a\5\n\6\2\u017a\u017f\7\4\2\2\u017b\u017d\7\f\2\2\u017c"+ - "\u017b\3\2\2\2\u017c\u017d\3\2\2\2\u017d\u017e\3\2\2\2\u017e\u0180\5@"+ - "!\2\u017f\u017c\3\2\2\2\u017f\u0180\3\2\2\2\u0180\u018b\3\2\2\2\u0181"+ - "\u0182\7\3\2\2\u0182\u0183\5 \21\2\u0183\u0188\7\4\2\2\u0184\u0186\7\f"+ - "\2\2\u0185\u0184\3\2\2\2\u0185\u0186\3\2\2\2\u0186\u0187\3\2\2\2\u0187"+ - "\u0189\5@!\2\u0188\u0185\3\2\2\2\u0188\u0189\3\2\2\2\u0189\u018b\3\2\2"+ - "\2\u018a\u0171\3\2\2\2\u018a\u0178\3\2\2\2\u018a\u0181\3\2\2\2\u018b)"+ - "\3\2\2\2\u018c\u018d\5,\27\2\u018d+\3\2\2\2\u018e\u018f\b\27\1\2\u018f"+ + "\32\3\32\5\32\u0202\n\32\3\32\5\32\u0205\n\32\3\33\3\33\3\33\3\33\5\33"+ + "\u020b\n\33\3\33\3\33\3\33\3\33\3\33\3\33\3\33\3\33\3\33\3\33\7\33\u0217"+ + "\n\33\f\33\16\33\u021a\13\33\3\34\3\34\3\34\3\34\3\34\3\34\3\34\3\34\3"+ + "\34\3\34\3\34\3\34\3\34\3\34\3\34\3\34\3\34\3\34\3\34\5\34\u022f\n\34"+ + "\3\34\3\34\3\34\3\34\5\34\u0235\n\34\3\34\3\34\3\34\7\34\u023a\n\34\f"+ + "\34\16\34\u023d\13\34\5\34\u023f\n\34\3\34\3\34\3\34\3\34\3\34\3\34\3"+ + "\34\3\34\3\34\3\34\3\34\3\34\5\34\u024d\n\34\3\35\3\35\3\35\3\35\6\35"+ + "\u0253\n\35\r\35\16\35\u0254\3\35\5\35\u0258\n\35\3\36\3\36\3\37\3\37"+ + "\3 \3 \3!\3!\3!\7!\u0263\n!\f!\16!\u0266\13!\3!\3!\3\"\3\"\5\"\u026c\n"+ + "\"\3#\3#\3#\5#\u0271\n#\3#\3#\3#\3#\5#\u0277\n#\3#\5#\u027a\n#\3$\3$\5"+ + "$\u027e\n$\3%\3%\3%\5%\u0283\n%\3&\3&\5&\u0287\n&\3\'\3\'\3\'\2\4,\64"+ + "(\2\4\6\b\n\f\16\20\22\24\26\30\32\34\36 \"$&(*,.\60\62\64\668:<>@BDF"+ + "HJL\2\20\b\2\7\7\t\t\31\31,,\62\62\66\66\4\2\"\"BB\4\2\t\t\62\62\4\2\37"+ + "\37%%\3\2\25\26\3\2WX\4\2\7\7YY\4\2\r\r\25\25\4\2\7\7\27\27\3\2PQ\3\2"+ + "RT\3\2JO\4\2\35\35CC\20\2\b\t\22\24\31\31\33\33\36\36!\",,\62\62\668:"+ + "<>?ABDEGG\u02e7\2N\3\2\2\2\4Q\3\2\2\2\6\u00c1\3\2\2\2\b\u00cc\3\2\2\2"+ + "\n\u00d0\3\2\2\2\f\u00e6\3\2\2\2\16\u00e8\3\2\2\2\20\u00ec\3\2\2\2\22"+ + "\u0108\3\2\2\2\24\u0112\3\2\2\2\26\u011c\3\2\2\2\30\u012b\3\2\2\2\32\u012d"+ + "\3\2\2\2\34\u0133\3\2\2\2\36\u0135\3\2\2\2 \u013c\3\2\2\2\"\u014e\3\2"+ + "\2\2$\u015f\3\2\2\2&\u016f\3\2\2\2(\u018a\3\2\2\2*\u018c\3\2\2\2,\u01bd"+ + "\3\2\2\2.\u01ca\3\2\2\2\60\u01fc\3\2\2\2\62\u0204\3\2\2\2\64\u020a\3\2"+ + "\2\2\66\u024c\3\2\2\28\u0257\3\2\2\2:\u0259\3\2\2\2<\u025b\3\2\2\2>\u025d"+ + "\3\2\2\2@\u0264\3\2\2\2B\u026b\3\2\2\2D\u0279\3\2\2\2F\u027d\3\2\2\2H"+ + "\u0282\3\2\2\2J\u0286\3\2\2\2L\u0288\3\2\2\2NO\5\6\4\2OP\7\2\2\3P\3\3"+ + "\2\2\2QR\5*\26\2RS\7\2\2\3S\5\3\2\2\2T\u00c2\5\b\5\2Uc\7\33\2\2V_\7\3"+ + "\2\2WX\78\2\2X^\t\2\2\2YZ\7\36\2\2Z^\t\3\2\2[\\\7G\2\2\\^\5<\37\2]W\3"+ + "\2\2\2]Y\3\2\2\2][\3\2\2\2^a\3\2\2\2_]\3\2\2\2_`\3\2\2\2`b\3\2\2\2a_\3"+ + "\2\2\2bd\7\4\2\2cV\3\2\2\2cd\3\2\2\2de\3\2\2\2e\u00c2\5\6\4\2fr\7\24\2"+ + "\2gn\7\3\2\2hi\78\2\2im\t\4\2\2jk\7\36\2\2km\t\3\2\2lh\3\2\2\2lj\3\2\2"+ + "\2mp\3\2\2\2nl\3\2\2\2no\3\2\2\2oq\3\2\2\2pn\3\2\2\2qs\7\4\2\2rg\3\2\2"+ + "\2rs\3\2\2\2st\3\2\2\2t\u00c2\5\6\4\2uv\7>\2\2v{\7A\2\2wy\7*\2\2xw\3\2"+ + "\2\2xy\3\2\2\2yz\3\2\2\2z|\5\62\32\2{x\3\2\2\2{|\3\2\2\2|\u00c2\3\2\2"+ + "\2}~\7>\2\2~\177\7\23\2\2\177\u0080\t\5\2\2\u0080\u00c2\5D#\2\u0081\u0082"+ + "\t\6\2\2\u0082\u00c2\5D#\2\u0083\u0084\7>\2\2\u0084\u0089\7!\2\2\u0085"+ + "\u0087\7*\2\2\u0086\u0085\3\2\2\2\u0086\u0087\3\2\2\2\u0087\u0088\3\2"+ + "\2\2\u0088\u008a\5\62\32\2\u0089\u0086\3\2\2\2\u0089\u008a\3\2\2\2\u008a"+ + "\u00c2\3\2\2\2\u008b\u008c\7>\2\2\u008c\u00c2\7<\2\2\u008d\u008e\7?\2"+ + "\2\u008e\u00c2\7\22\2\2\u008f\u0090\7?\2\2\u0090\u0096\7A\2\2\u0091\u0093"+ + "\7\21\2\2\u0092\u0094\7*\2\2\u0093\u0092\3\2\2\2\u0093\u0094\3\2\2\2\u0094"+ + "\u0095\3\2\2\2\u0095\u0097\5\62\32\2\u0096\u0091\3\2\2\2\u0096\u0097\3"+ + "\2\2\2\u0097\u009c\3\2\2\2\u0098\u009a\7*\2\2\u0099\u0098\3\2\2\2\u0099"+ + "\u009a\3\2\2\2\u009a\u009b\3\2\2\2\u009b\u009d\5\62\32\2\u009c\u0099\3"+ + "\2\2\2\u009c\u009d\3\2\2\2\u009d\u00a7\3\2\2\2\u009e\u009f\7D\2\2\u009f"+ + "\u00a4\7X\2\2\u00a0\u00a1\7\5\2\2\u00a1\u00a3\7X\2\2\u00a2\u00a0\3\2\2"+ + "\2\u00a3\u00a6\3\2\2\2\u00a4\u00a2\3\2\2\2\u00a4\u00a5\3\2\2\2\u00a5\u00a8"+ + "\3\2\2\2\u00a6\u00a4\3\2\2\2\u00a7\u009e\3\2\2\2\u00a7\u00a8\3\2\2\2\u00a8"+ + "\u00c2\3\2\2\2\u00a9\u00aa\7?\2\2\u00aa\u00ad\7\23\2\2\u00ab\u00ac\7\21"+ + "\2\2\u00ac\u00ae\t\7\2\2\u00ad\u00ab\3\2\2\2\u00ad\u00ae\3\2\2\2\u00ae"+ + "\u00b4\3\2\2\2\u00af\u00b1\7@\2\2\u00b0\u00b2\7*\2\2\u00b1\u00b0\3\2\2"+ + "\2\u00b1\u00b2\3\2\2\2\u00b2\u00b3\3\2\2\2\u00b3\u00b5\5\62\32\2\u00b4"+ + "\u00af\3\2\2\2\u00b4\u00b5\3\2\2\2\u00b5\u00ba\3\2\2\2\u00b6\u00b8\7*"+ + "\2\2\u00b7\u00b6\3\2\2\2\u00b7\u00b8\3\2\2\2\u00b8\u00b9\3\2\2\2\u00b9"+ + "\u00bb\5\62\32\2\u00ba\u00b7\3\2\2\2\u00ba\u00bb\3\2\2\2\u00bb\u00c2\3"+ + "\2\2\2\u00bc\u00bd\7?\2\2\u00bd\u00c2\7E\2\2\u00be\u00bf\7?\2\2\u00bf"+ + "\u00c0\7@\2\2\u00c0\u00c2\7E\2\2\u00c1T\3\2\2\2\u00c1U\3\2\2\2\u00c1f"+ + "\3\2\2\2\u00c1u\3\2\2\2\u00c1}\3\2\2\2\u00c1\u0081\3\2\2\2\u00c1\u0083"+ + "\3\2\2\2\u00c1\u008b\3\2\2\2\u00c1\u008d\3\2\2\2\u00c1\u008f\3\2\2\2\u00c1"+ + "\u00a9\3\2\2\2\u00c1\u00bc\3\2\2\2\u00c1\u00be\3\2\2\2\u00c2\7\3\2\2\2"+ + "\u00c3\u00c4\7I\2\2\u00c4\u00c9\5\32\16\2\u00c5\u00c6\7\5\2\2\u00c6\u00c8"+ + "\5\32\16\2\u00c7\u00c5\3\2\2\2\u00c8\u00cb\3\2\2\2\u00c9\u00c7\3\2\2\2"+ + "\u00c9\u00ca\3\2\2\2\u00ca\u00cd\3\2\2\2\u00cb\u00c9\3\2\2\2\u00cc\u00c3"+ + "\3\2\2\2\u00cc\u00cd\3\2\2\2\u00cd\u00ce\3\2\2\2\u00ce\u00cf\5\n\6\2\u00cf"+ + "\t\3\2\2\2\u00d0\u00db\5\f\7\2\u00d1\u00d2\7\64\2\2\u00d2\u00d3\7\17\2"+ + "\2\u00d3\u00d8\5\16\b\2\u00d4\u00d5\7\5\2\2\u00d5\u00d7\5\16\b\2\u00d6"+ + "\u00d4\3\2\2\2\u00d7\u00da\3\2\2\2\u00d8\u00d6\3\2\2\2\u00d8\u00d9\3\2"+ + "\2\2\u00d9\u00dc\3\2\2\2\u00da\u00d8\3\2\2\2\u00db\u00d1\3\2\2\2\u00db"+ + "\u00dc\3\2\2\2\u00dc\u00df\3\2\2\2\u00dd\u00de\7+\2\2\u00de\u00e0\t\b"+ + "\2\2\u00df\u00dd\3\2\2\2\u00df\u00e0\3\2\2\2\u00e0\13\3\2\2\2\u00e1\u00e7"+ + "\5\20\t\2\u00e2\u00e3\7\3\2\2\u00e3\u00e4\5\n\6\2\u00e4\u00e5\7\4\2\2"+ + "\u00e5\u00e7\3\2\2\2\u00e6\u00e1\3\2\2\2\u00e6\u00e2\3\2\2\2\u00e7\r\3"+ + "\2\2\2\u00e8\u00ea\5*\26\2\u00e9\u00eb\t\t\2\2\u00ea\u00e9\3\2\2\2\u00ea"+ + "\u00eb\3\2\2\2\u00eb\17\3\2\2\2\u00ec\u00ee\7=\2\2\u00ed\u00ef\5\34\17"+ + "\2\u00ee\u00ed\3\2\2\2\u00ee\u00ef\3\2\2\2\u00ef\u00f0\3\2\2\2\u00f0\u00f5"+ + "\5\36\20\2\u00f1\u00f2\7\5\2\2\u00f2\u00f4\5\36\20\2\u00f3\u00f1\3\2\2"+ + "\2\u00f4\u00f7\3\2\2\2\u00f5\u00f3\3\2\2\2\u00f5\u00f6\3\2\2\2\u00f6\u00f9"+ + "\3\2\2\2\u00f7\u00f5\3\2\2\2\u00f8\u00fa\5\22\n\2\u00f9\u00f8\3\2\2\2"+ + "\u00f9\u00fa\3\2\2\2\u00fa\u00fd\3\2\2\2\u00fb\u00fc\7H\2\2\u00fc\u00fe"+ + "\5,\27\2\u00fd\u00fb\3\2\2\2\u00fd\u00fe\3\2\2\2\u00fe\u0102\3\2\2\2\u00ff"+ + "\u0100\7#\2\2\u0100\u0101\7\17\2\2\u0101\u0103\5\24\13\2\u0102\u00ff\3"+ + "\2\2\2\u0102\u0103\3\2\2\2\u0103\u0106\3\2\2\2\u0104\u0105\7$\2\2\u0105"+ + "\u0107\5,\27\2\u0106\u0104\3\2\2\2\u0106\u0107\3\2\2\2\u0107\21\3\2\2"+ + "\2\u0108\u0109\7\37\2\2\u0109\u010e\5 \21\2\u010a\u010b\7\5\2\2\u010b"+ + "\u010d\5 \21\2\u010c\u010a\3\2\2\2\u010d\u0110\3\2\2\2\u010e\u010c\3\2"+ + "\2\2\u010e\u010f\3\2\2\2\u010f\23\3\2\2\2\u0110\u010e\3\2\2\2\u0111\u0113"+ + "\5\34\17\2\u0112\u0111\3\2\2\2\u0112\u0113\3\2\2\2\u0113\u0114\3\2\2\2"+ + "\u0114\u0119\5\26\f\2\u0115\u0116\7\5\2\2\u0116\u0118\5\26\f\2\u0117\u0115"+ + "\3\2\2\2\u0118\u011b\3\2\2\2\u0119\u0117\3\2\2\2\u0119\u011a\3\2\2\2\u011a"+ + "\25\3\2\2\2\u011b\u0119\3\2\2\2\u011c\u011d\5\30\r\2\u011d\27\3\2\2\2"+ + "\u011e\u0127\7\3\2\2\u011f\u0124\5*\26\2\u0120\u0121\7\5\2\2\u0121\u0123"+ + "\5*\26\2\u0122\u0120\3\2\2\2\u0123\u0126\3\2\2\2\u0124\u0122\3\2\2\2\u0124"+ + "\u0125\3\2\2\2\u0125\u0128\3\2\2\2\u0126\u0124\3\2\2\2\u0127\u011f\3\2"+ + "\2\2\u0127\u0128\3\2\2\2\u0128\u0129\3\2\2\2\u0129\u012c\7\4\2\2\u012a"+ + "\u012c\5*\26\2\u012b\u011e\3\2\2\2\u012b\u012a\3\2\2\2\u012c\31\3\2\2"+ + "\2\u012d\u012e\5B\"\2\u012e\u012f\7\f\2\2\u012f\u0130\7\3\2\2\u0130\u0131"+ + "\5\n\6\2\u0131\u0132\7\4\2\2\u0132\33\3\2\2\2\u0133\u0134\t\n\2\2\u0134"+ + "\35\3\2\2\2\u0135\u013a\5*\26\2\u0136\u0138\7\f\2\2\u0137\u0136\3\2\2"+ + "\2\u0137\u0138\3\2\2\2\u0138\u0139\3\2\2\2\u0139\u013b\5B\"\2\u013a\u0137"+ + "\3\2\2\2\u013a\u013b\3\2\2\2\u013b\37\3\2\2\2\u013c\u0140\5(\25\2\u013d"+ + "\u013f\5\"\22\2\u013e\u013d\3\2\2\2\u013f\u0142\3\2\2\2\u0140\u013e\3"+ + "\2\2\2\u0140\u0141\3\2\2\2\u0141!\3\2\2\2\u0142\u0140\3\2\2\2\u0143\u0144"+ + "\5$\23\2\u0144\u0145\7(\2\2\u0145\u0147\5(\25\2\u0146\u0148\5&\24\2\u0147"+ + "\u0146\3\2\2\2\u0147\u0148\3\2\2\2\u0148\u014f\3\2\2\2\u0149\u014a\7."+ + "\2\2\u014a\u014b\5$\23\2\u014b\u014c\7(\2\2\u014c\u014d\5(\25\2\u014d"+ + "\u014f\3\2\2\2\u014e\u0143\3\2\2\2\u014e\u0149\3\2\2\2\u014f#\3\2\2\2"+ + "\u0150\u0152\7&\2\2\u0151\u0150\3\2\2\2\u0151\u0152\3\2\2\2\u0152\u0160"+ + "\3\2\2\2\u0153\u0155\7)\2\2\u0154\u0156\7\65\2\2\u0155\u0154\3\2\2\2\u0155"+ + "\u0156\3\2\2\2\u0156\u0160\3\2\2\2\u0157\u0159\79\2\2\u0158\u015a\7\65"+ + "\2\2\u0159\u0158\3\2\2\2\u0159\u015a\3\2\2\2\u015a\u0160\3\2\2\2\u015b"+ + "\u015d\7 \2\2\u015c\u015e\7\65\2\2\u015d\u015c\3\2\2\2\u015d\u015e\3\2"+ + "\2\2\u015e\u0160\3\2\2\2\u015f\u0151\3\2\2\2\u015f\u0153\3\2\2\2\u015f"+ + "\u0157\3\2\2\2\u015f\u015b\3\2\2\2\u0160%\3\2\2\2\u0161\u0162\7\61\2\2"+ + "\u0162\u0170\5,\27\2\u0163\u0164\7F\2\2\u0164\u0165\7\3\2\2\u0165\u016a"+ + "\5B\"\2\u0166\u0167\7\5\2\2\u0167\u0169\5B\"\2\u0168\u0166\3\2\2\2\u0169"+ + "\u016c\3\2\2\2\u016a\u0168\3\2\2\2\u016a\u016b\3\2\2\2\u016b\u016d\3\2"+ + "\2\2\u016c\u016a\3\2\2\2\u016d\u016e\7\4\2\2\u016e\u0170\3\2\2\2\u016f"+ + "\u0161\3\2\2\2\u016f\u0163\3\2\2\2\u0170\'\3\2\2\2\u0171\u0176\5D#\2\u0172"+ + "\u0174\7\f\2\2\u0173\u0172\3\2\2\2\u0173\u0174\3\2\2\2\u0174\u0175\3\2"+ + "\2\2\u0175\u0177\5@!\2\u0176\u0173\3\2\2\2\u0176\u0177\3\2\2\2\u0177\u018b"+ + "\3\2\2\2\u0178\u0179\7\3\2\2\u0179\u017a\5\n\6\2\u017a\u017f\7\4\2\2\u017b"+ + "\u017d\7\f\2\2\u017c\u017b\3\2\2\2\u017c\u017d\3\2\2\2\u017d\u017e\3\2"+ + "\2\2\u017e\u0180\5@!\2\u017f\u017c\3\2\2\2\u017f\u0180\3\2\2\2\u0180\u018b"+ + "\3\2\2\2\u0181\u0182\7\3\2\2\u0182\u0183\5 \21\2\u0183\u0188\7\4\2\2\u0184"+ + "\u0186\7\f\2\2\u0185\u0184\3\2\2\2\u0185\u0186\3\2\2\2\u0186\u0187\3\2"+ + "\2\2\u0187\u0189\5@!\2\u0188\u0185\3\2\2\2\u0188\u0189\3\2\2\2\u0189\u018b"+ + "\3\2\2\2\u018a\u0171\3\2\2\2\u018a\u0178\3\2\2\2\u018a\u0181\3\2\2\2\u018b"+ + ")\3\2\2\2\u018c\u018d\5,\27\2\u018d+\3\2\2\2\u018e\u018f\b\27\1\2\u018f"+ "\u0190\7/\2\2\u0190\u01be\5,\27\n\u0191\u0192\7\32\2\2\u0192\u0193\7\3"+ "\2\2\u0193\u0194\5\b\5\2\u0194\u0195\7\4\2\2\u0195\u01be\3\2\2\2\u0196"+ - "\u0197\7;\2\2\u0197\u0198\7\3\2\2\u0198\u019d\7W\2\2\u0199\u019a\7\5\2"+ - "\2\u019a\u019c\7W\2\2\u019b\u0199\3\2\2\2\u019c\u019f\3\2\2\2\u019d\u019b"+ + "\u0197\7;\2\2\u0197\u0198\7\3\2\2\u0198\u019d\7X\2\2\u0199\u019a\7\5\2"+ + "\2\u019a\u019c\7X\2\2\u019b\u0199\3\2\2\2\u019c\u019f\3\2\2\2\u019d\u019b"+ "\3\2\2\2\u019d\u019e\3\2\2\2\u019e\u01a0\3\2\2\2\u019f\u019d\3\2\2\2\u01a0"+ "\u01be\7\4\2\2\u01a1\u01a2\7-\2\2\u01a2\u01a3\7\3\2\2\u01a3\u01a4\5@!"+ - "\2\u01a4\u01a5\7\5\2\2\u01a5\u01aa\7W\2\2\u01a6\u01a7\7\5\2\2\u01a7\u01a9"+ - "\7W\2\2\u01a8\u01a6\3\2\2\2\u01a9\u01ac\3\2\2\2\u01aa\u01a8\3\2\2\2\u01aa"+ + "\2\u01a4\u01a5\7\5\2\2\u01a5\u01aa\7X\2\2\u01a6\u01a7\7\5\2\2\u01a7\u01a9"+ + "\7X\2\2\u01a8\u01a6\3\2\2\2\u01a9\u01ac\3\2\2\2\u01aa\u01a8\3\2\2\2\u01aa"+ "\u01ab\3\2\2\2\u01ab\u01ad\3\2\2\2\u01ac\u01aa\3\2\2\2\u01ad\u01ae\7\4"+ "\2\2\u01ae\u01be\3\2\2\2\u01af\u01b0\7-\2\2\u01b0\u01b1\7\3\2\2\u01b1"+ - "\u01b2\7W\2\2\u01b2\u01b3\7\5\2\2\u01b3\u01b8\7W\2\2\u01b4\u01b5\7\5\2"+ - "\2\u01b5\u01b7\7W\2\2\u01b6\u01b4\3\2\2\2\u01b7\u01ba\3\2\2\2\u01b8\u01b6"+ + "\u01b2\7X\2\2\u01b2\u01b3\7\5\2\2\u01b3\u01b8\7X\2\2\u01b4\u01b5\7\5\2"+ + "\2\u01b5\u01b7\7X\2\2\u01b6\u01b4\3\2\2\2\u01b7\u01ba\3\2\2\2\u01b8\u01b6"+ "\3\2\2\2\u01b8\u01b9\3\2\2\2\u01b9\u01bb\3\2\2\2\u01ba\u01b8\3\2\2\2\u01bb"+ "\u01be\7\4\2\2\u01bc\u01be\5.\30\2\u01bd\u018e\3\2\2\2\u01bd\u0191\3\2"+ "\2\2\u01bd\u0196\3\2\2\2\u01bd\u01a1\3\2\2\2\u01bd\u01af\3\2\2\2\u01bd"+ @@ -5084,64 +5133,66 @@ class SqlBaseParser extends Parser { "\2\2\u01ec\u01fd\3\2\2\2\u01ed\u01ef\7/\2\2\u01ee\u01ed\3\2\2\2\u01ee"+ "\u01ef\3\2\2\2\u01ef\u01f0\3\2\2\2\u01f0\u01f1\7*\2\2\u01f1\u01fd\5\62"+ "\32\2\u01f2\u01f4\7/\2\2\u01f3\u01f2\3\2\2\2\u01f3\u01f4\3\2\2\2\u01f4"+ - "\u01f5\3\2\2\2\u01f5\u01f6\7:\2\2\u01f6\u01fd\7W\2\2\u01f7\u01f9\7\'\2"+ + "\u01f5\3\2\2\2\u01f5\u01f6\7:\2\2\u01f6\u01fd\7X\2\2\u01f7\u01f9\7\'\2"+ "\2\u01f8\u01fa\7/\2\2\u01f9\u01f8\3\2\2\2\u01f9\u01fa\3\2\2\2\u01fa\u01fb"+ "\3\2\2\2\u01fb\u01fd\7\60\2\2\u01fc\u01cf\3\2\2\2\u01fc\u01d7\3\2\2\2"+ "\u01fc\u01e6\3\2\2\2\u01fc\u01ee\3\2\2\2\u01fc\u01f3\3\2\2\2\u01fc\u01f7"+ - "\3\2\2\2\u01fd\61\3\2\2\2\u01fe\u0201\7W\2\2\u01ff\u0200\7\30\2\2\u0200"+ - "\u0202\7W\2\2\u0201\u01ff\3\2\2\2\u0201\u0202\3\2\2\2\u0202\63\3\2\2\2"+ - "\u0203\u0204\b\33\1\2\u0204\u0208\5\66\34\2\u0205\u0206\t\n\2\2\u0206"+ - "\u0208\5\64\33\6\u0207\u0203\3\2\2\2\u0207\u0205\3\2\2\2\u0208\u0215\3"+ - "\2\2\2\u0209\u020a\f\5\2\2\u020a\u020b\t\13\2\2\u020b\u0214\5\64\33\6"+ - "\u020c\u020d\f\4\2\2\u020d\u020e\t\n\2\2\u020e\u0214\5\64\33\5\u020f\u0210"+ - "\f\3\2\2\u0210\u0211\5:\36\2\u0211\u0212\5\64\33\4\u0212\u0214\3\2\2\2"+ - "\u0213\u0209\3\2\2\2\u0213\u020c\3\2\2\2\u0213\u020f\3\2\2\2\u0214\u0217"+ - "\3\2\2\2\u0215\u0213\3\2\2\2\u0215\u0216\3\2\2\2\u0216\65\3\2\2\2\u0217"+ - "\u0215\3\2\2\2\u0218\u0219\7\20\2\2\u0219\u021a\7\3\2\2\u021a\u021b\5"+ - "*\26\2\u021b\u021c\7\f\2\2\u021c\u021d\5> \2\u021d\u021e\7\4\2\2\u021e"+ - "\u024a\3\2\2\2\u021f\u0220\7\34\2\2\u0220\u0221\7\3\2\2\u0221\u0222\5"+ - "B\"\2\u0222\u0223\7\37\2\2\u0223\u0224\5\64\33\2\u0224\u0225\7\4\2\2\u0225"+ - "\u024a\3\2\2\2\u0226\u024a\58\35\2\u0227\u024a\7R\2\2\u0228\u0229\5@!"+ - "\2\u0229\u022a\7V\2\2\u022a\u022c\3\2\2\2\u022b\u0228\3\2\2\2\u022b\u022c"+ - "\3\2\2\2\u022c\u022d\3\2\2\2\u022d\u024a\7R\2\2\u022e\u022f\5B\"\2\u022f"+ - "\u023b\7\3\2\2\u0230\u0232\5\34\17\2\u0231\u0230\3\2\2\2\u0231\u0232\3"+ - "\2\2\2\u0232\u0233\3\2\2\2\u0233\u0238\5*\26\2\u0234\u0235\7\5\2\2\u0235"+ - "\u0237\5*\26\2\u0236\u0234\3\2\2\2\u0237\u023a\3\2\2\2\u0238\u0236\3\2"+ - "\2\2\u0238\u0239\3\2\2\2\u0239\u023c\3\2\2\2\u023a\u0238\3\2\2\2\u023b"+ - "\u0231\3\2\2\2\u023b\u023c\3\2\2\2\u023c\u023d\3\2\2\2\u023d\u023e\7\4"+ - "\2\2\u023e\u024a\3\2\2\2\u023f\u0240\7\3\2\2\u0240\u0241\5\b\5\2\u0241"+ - "\u0242\7\4\2\2\u0242\u024a\3\2\2\2\u0243\u024a\5B\"\2\u0244\u024a\5@!"+ - "\2\u0245\u0246\7\3\2\2\u0246\u0247\5*\26\2\u0247\u0248\7\4\2\2\u0248\u024a"+ - "\3\2\2\2\u0249\u0218\3\2\2\2\u0249\u021f\3\2\2\2\u0249\u0226\3\2\2\2\u0249"+ - "\u0227\3\2\2\2\u0249\u022b\3\2\2\2\u0249\u022e\3\2\2\2\u0249\u023f\3\2"+ - "\2\2\u0249\u0243\3\2\2\2\u0249\u0244\3\2\2\2\u0249\u0245\3\2\2\2\u024a"+ - "\67\3\2\2\2\u024b\u0254\7\60\2\2\u024c\u0254\5J&\2\u024d\u0254\5<\37\2"+ - "\u024e\u0250\7W\2\2\u024f\u024e\3\2\2\2\u0250\u0251\3\2\2\2\u0251\u024f"+ - "\3\2\2\2\u0251\u0252\3\2\2\2\u0252\u0254\3\2\2\2\u0253\u024b\3\2\2\2\u0253"+ - "\u024c\3\2\2\2\u0253\u024d\3\2\2\2\u0253\u024f\3\2\2\2\u02549\3\2\2\2"+ - "\u0255\u0256\t\f\2\2\u0256;\3\2\2\2\u0257\u0258\t\r\2\2\u0258=\3\2\2\2"+ - "\u0259\u025a\5B\"\2\u025a?\3\2\2\2\u025b\u025c\5B\"\2\u025c\u025d\7V\2"+ - "\2\u025d\u025f\3\2\2\2\u025e\u025b\3\2\2\2\u025f\u0262\3\2\2\2\u0260\u025e"+ - "\3\2\2\2\u0260\u0261\3\2\2\2\u0261\u0263\3\2\2\2\u0262\u0260\3\2\2\2\u0263"+ - "\u0264\5B\"\2\u0264A\3\2\2\2\u0265\u0268\5F$\2\u0266\u0268\5H%\2\u0267"+ - "\u0265\3\2\2\2\u0267\u0266\3\2\2\2\u0268C\3\2\2\2\u0269\u026a\5B\"\2\u026a"+ - "\u026b\7\6\2\2\u026b\u026d\3\2\2\2\u026c\u0269\3\2\2\2\u026c\u026d\3\2"+ - "\2\2\u026d\u026e\3\2\2\2\u026e\u0276\7\\\2\2\u026f\u0270\5B\"\2\u0270"+ - "\u0271\7\6\2\2\u0271\u0273\3\2\2\2\u0272\u026f\3\2\2\2\u0272\u0273\3\2"+ - "\2\2\u0273\u0274\3\2\2\2\u0274\u0276\5B\"\2\u0275\u026c\3\2\2\2\u0275"+ - "\u0272\3\2\2\2\u0276E\3\2\2\2\u0277\u027a\7]\2\2\u0278\u027a\7^\2\2\u0279"+ - "\u0277\3\2\2\2\u0279\u0278\3\2\2\2\u027aG\3\2\2\2\u027b\u027f\7Z\2\2\u027c"+ - "\u027f\5L\'\2\u027d\u027f\7[\2\2\u027e\u027b\3\2\2\2\u027e\u027c\3\2\2"+ - "\2\u027e\u027d\3\2\2\2\u027fI\3\2\2\2\u0280\u0283\7Y\2\2\u0281\u0283\7"+ - "X\2\2\u0282\u0280\3\2\2\2\u0282\u0281\3\2\2\2\u0283K\3\2\2\2\u0284\u0285"+ - "\t\16\2\2\u0285M\3\2\2\2`]_clnrx{\u0086\u0089\u0093\u0096\u0099\u009c"+ - "\u00a4\u00a7\u00ad\u00b1\u00b4\u00b7\u00ba\u00c1\u00c9\u00cc\u00d8\u00db"+ - "\u00df\u00e6\u00ea\u00ee\u00f5\u00f9\u00fd\u0102\u0106\u010e\u0112\u0119"+ - "\u0124\u0127\u012b\u0137\u013a\u0140\u0147\u014e\u0151\u0155\u0159\u015d"+ - "\u015f\u016a\u016f\u0173\u0176\u017c\u017f\u0185\u0188\u018a\u019d\u01aa"+ - "\u01b8\u01bd\u01c5\u01c7\u01cc\u01cf\u01d7\u01e0\u01e6\u01ee\u01f3\u01f9"+ - "\u01fc\u0201\u0207\u0213\u0215\u022b\u0231\u0238\u023b\u0249\u0251\u0253"+ - "\u0260\u0267\u026c\u0272\u0275\u0279\u027e\u0282"; + "\3\2\2\2\u01fd\61\3\2\2\2\u01fe\u0201\7X\2\2\u01ff\u0200\7\30\2\2\u0200"+ + "\u0202\7X\2\2\u0201\u01ff\3\2\2\2\u0201\u0202\3\2\2\2\u0202\u0205\3\2"+ + "\2\2\u0203\u0205\7W\2\2\u0204\u01fe\3\2\2\2\u0204\u0203\3\2\2\2\u0205"+ + "\63\3\2\2\2\u0206\u0207\b\33\1\2\u0207\u020b\5\66\34\2\u0208\u0209\t\13"+ + "\2\2\u0209\u020b\5\64\33\6\u020a\u0206\3\2\2\2\u020a\u0208\3\2\2\2\u020b"+ + "\u0218\3\2\2\2\u020c\u020d\f\5\2\2\u020d\u020e\t\f\2\2\u020e\u0217\5\64"+ + "\33\6\u020f\u0210\f\4\2\2\u0210\u0211\t\13\2\2\u0211\u0217\5\64\33\5\u0212"+ + "\u0213\f\3\2\2\u0213\u0214\5:\36\2\u0214\u0215\5\64\33\4\u0215\u0217\3"+ + "\2\2\2\u0216\u020c\3\2\2\2\u0216\u020f\3\2\2\2\u0216\u0212\3\2\2\2\u0217"+ + "\u021a\3\2\2\2\u0218\u0216\3\2\2\2\u0218\u0219\3\2\2\2\u0219\65\3\2\2"+ + "\2\u021a\u0218\3\2\2\2\u021b\u021c\7\20\2\2\u021c\u021d\7\3\2\2\u021d"+ + "\u021e\5*\26\2\u021e\u021f\7\f\2\2\u021f\u0220\5> \2\u0220\u0221\7\4\2"+ + "\2\u0221\u024d\3\2\2\2\u0222\u0223\7\34\2\2\u0223\u0224\7\3\2\2\u0224"+ + "\u0225\5B\"\2\u0225\u0226\7\37\2\2\u0226\u0227\5\64\33\2\u0227\u0228\7"+ + "\4\2\2\u0228\u024d\3\2\2\2\u0229\u024d\58\35\2\u022a\u024d\7R\2\2\u022b"+ + "\u022c\5@!\2\u022c\u022d\7V\2\2\u022d\u022f\3\2\2\2\u022e\u022b\3\2\2"+ + "\2\u022e\u022f\3\2\2\2\u022f\u0230\3\2\2\2\u0230\u024d\7R\2\2\u0231\u0232"+ + "\5B\"\2\u0232\u023e\7\3\2\2\u0233\u0235\5\34\17\2\u0234\u0233\3\2\2\2"+ + "\u0234\u0235\3\2\2\2\u0235\u0236\3\2\2\2\u0236\u023b\5*\26\2\u0237\u0238"+ + "\7\5\2\2\u0238\u023a\5*\26\2\u0239\u0237\3\2\2\2\u023a\u023d\3\2\2\2\u023b"+ + "\u0239\3\2\2\2\u023b\u023c\3\2\2\2\u023c\u023f\3\2\2\2\u023d\u023b\3\2"+ + "\2\2\u023e\u0234\3\2\2\2\u023e\u023f\3\2\2\2\u023f\u0240\3\2\2\2\u0240"+ + "\u0241\7\4\2\2\u0241\u024d\3\2\2\2\u0242\u0243\7\3\2\2\u0243\u0244\5\b"+ + "\5\2\u0244\u0245\7\4\2\2\u0245\u024d\3\2\2\2\u0246\u024d\5B\"\2\u0247"+ + "\u024d\5@!\2\u0248\u0249\7\3\2\2\u0249\u024a\5*\26\2\u024a\u024b\7\4\2"+ + "\2\u024b\u024d\3\2\2\2\u024c\u021b\3\2\2\2\u024c\u0222\3\2\2\2\u024c\u0229"+ + "\3\2\2\2\u024c\u022a\3\2\2\2\u024c\u022e\3\2\2\2\u024c\u0231\3\2\2\2\u024c"+ + "\u0242\3\2\2\2\u024c\u0246\3\2\2\2\u024c\u0247\3\2\2\2\u024c\u0248\3\2"+ + "\2\2\u024d\67\3\2\2\2\u024e\u0258\7\60\2\2\u024f\u0258\5J&\2\u0250\u0258"+ + "\5<\37\2\u0251\u0253\7X\2\2\u0252\u0251\3\2\2\2\u0253\u0254\3\2\2\2\u0254"+ + "\u0252\3\2\2\2\u0254\u0255\3\2\2\2\u0255\u0258\3\2\2\2\u0256\u0258\7W"+ + "\2\2\u0257\u024e\3\2\2\2\u0257\u024f\3\2\2\2\u0257\u0250\3\2\2\2\u0257"+ + "\u0252\3\2\2\2\u0257\u0256\3\2\2\2\u02589\3\2\2\2\u0259\u025a\t\r\2\2"+ + "\u025a;\3\2\2\2\u025b\u025c\t\16\2\2\u025c=\3\2\2\2\u025d\u025e\5B\"\2"+ + "\u025e?\3\2\2\2\u025f\u0260\5B\"\2\u0260\u0261\7V\2\2\u0261\u0263\3\2"+ + "\2\2\u0262\u025f\3\2\2\2\u0263\u0266\3\2\2\2\u0264\u0262\3\2\2\2\u0264"+ + "\u0265\3\2\2\2\u0265\u0267\3\2\2\2\u0266\u0264\3\2\2\2\u0267\u0268\5B"+ + "\"\2\u0268A\3\2\2\2\u0269\u026c\5F$\2\u026a\u026c\5H%\2\u026b\u0269\3"+ + "\2\2\2\u026b\u026a\3\2\2\2\u026cC\3\2\2\2\u026d\u026e\5B\"\2\u026e\u026f"+ + "\7\6\2\2\u026f\u0271\3\2\2\2\u0270\u026d\3\2\2\2\u0270\u0271\3\2\2\2\u0271"+ + "\u0272\3\2\2\2\u0272\u027a\7]\2\2\u0273\u0274\5B\"\2\u0274\u0275\7\6\2"+ + "\2\u0275\u0277\3\2\2\2\u0276\u0273\3\2\2\2\u0276\u0277\3\2\2\2\u0277\u0278"+ + "\3\2\2\2\u0278\u027a\5B\"\2\u0279\u0270\3\2\2\2\u0279\u0276\3\2\2\2\u027a"+ + "E\3\2\2\2\u027b\u027e\7^\2\2\u027c\u027e\7_\2\2\u027d\u027b\3\2\2\2\u027d"+ + "\u027c\3\2\2\2\u027eG\3\2\2\2\u027f\u0283\7[\2\2\u0280\u0283\5L\'\2\u0281"+ + "\u0283\7\\\2\2\u0282\u027f\3\2\2\2\u0282\u0280\3\2\2\2\u0282\u0281\3\2"+ + "\2\2\u0283I\3\2\2\2\u0284\u0287\7Z\2\2\u0285\u0287\7Y\2\2\u0286\u0284"+ + "\3\2\2\2\u0286\u0285\3\2\2\2\u0287K\3\2\2\2\u0288\u0289\t\17\2\2\u0289"+ + "M\3\2\2\2a]_clnrx{\u0086\u0089\u0093\u0096\u0099\u009c\u00a4\u00a7\u00ad"+ + "\u00b1\u00b4\u00b7\u00ba\u00c1\u00c9\u00cc\u00d8\u00db\u00df\u00e6\u00ea"+ + "\u00ee\u00f5\u00f9\u00fd\u0102\u0106\u010e\u0112\u0119\u0124\u0127\u012b"+ + "\u0137\u013a\u0140\u0147\u014e\u0151\u0155\u0159\u015d\u015f\u016a\u016f"+ + "\u0173\u0176\u017c\u017f\u0185\u0188\u018a\u019d\u01aa\u01b8\u01bd\u01c5"+ + "\u01c7\u01cc\u01cf\u01d7\u01e0\u01e6\u01ee\u01f3\u01f9\u01fc\u0201\u0204"+ + "\u020a\u0216\u0218\u022e\u0234\u023b\u023e\u024c\u0254\u0257\u0264\u026b"+ + "\u0270\u0276\u0279\u027d\u0282\u0286"; public static final ATN _ATN = new ATNDeserializer().deserialize(_serializedATN.toCharArray()); static { diff --git a/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/parser/SqlBaseVisitor.java b/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/parser/SqlBaseVisitor.java index 04933d09c7c..0380373dfd5 100644 --- a/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/parser/SqlBaseVisitor.java +++ b/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/parser/SqlBaseVisitor.java @@ -430,6 +430,13 @@ interface SqlBaseVisitor extends ParseTreeVisitor { * @return the visitor result */ T visitStringLiteral(SqlBaseParser.StringLiteralContext ctx); + /** + * Visit a parse tree produced by the {@code param} + * labeled alternative in {@link SqlBaseParser#constant}. + * @param ctx the parse tree + * @return the visitor result + */ + T visitParam(SqlBaseParser.ParamContext ctx); /** * Visit a parse tree produced by {@link SqlBaseParser#comparisonOperator}. * @param ctx the parse tree diff --git a/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/parser/SqlParser.java b/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/parser/SqlParser.java index 27481a47530..12b1d2604fc 100644 --- a/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/parser/SqlParser.java +++ b/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/parser/SqlParser.java @@ -6,6 +6,7 @@ package org.elasticsearch.xpack.sql.parser; import org.antlr.v4.runtime.BaseErrorListener; +import org.antlr.v4.runtime.CharStream; import org.antlr.v4.runtime.CommonToken; import org.antlr.v4.runtime.CommonTokenStream; import org.antlr.v4.runtime.DiagnosticErrorListener; @@ -14,6 +15,8 @@ import org.antlr.v4.runtime.ParserRuleContext; import org.antlr.v4.runtime.RecognitionException; import org.antlr.v4.runtime.Recognizer; import org.antlr.v4.runtime.Token; +import org.antlr.v4.runtime.TokenFactory; +import org.antlr.v4.runtime.TokenSource; import org.antlr.v4.runtime.atn.ATNConfigSet; import org.antlr.v4.runtime.atn.PredictionMode; import org.antlr.v4.runtime.dfa.DFA; @@ -23,11 +26,14 @@ import org.apache.logging.log4j.Logger; import org.elasticsearch.common.logging.Loggers; import org.elasticsearch.xpack.sql.expression.Expression; import org.elasticsearch.xpack.sql.plan.logical.LogicalPlan; -import org.joda.time.DateTimeZone; +import org.elasticsearch.xpack.sql.plugin.SqlTypedParamValue; import java.util.Arrays; import java.util.BitSet; +import java.util.Collections; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.function.BiFunction; import java.util.function.Function; @@ -38,36 +44,57 @@ public class SqlParser { * Time zone in which the SQL is parsed. This is attached to functions * that deal with dates and times. */ - private final DateTimeZone timeZone; private final boolean DEBUG = false; - public SqlParser(DateTimeZone timeZone) { - this.timeZone = timeZone; + /** + * Used only in tests + */ + public LogicalPlan createStatement(String sql) { + return createStatement(sql, Collections.emptyList()); } - public LogicalPlan createStatement(String sql) { + /** + * Parses an SQL statement into execution plan + * @param sql - the SQL statement + * @param params - a list of parameters for the statement if the statement is parametrized + * @return logical plan + */ + public LogicalPlan createStatement(String sql, List params) { if (log.isDebugEnabled()) { log.debug("Parsing as statement: {}", sql); } - return invokeParser("statement", sql, SqlBaseParser::singleStatement, AstBuilder::plan); + return invokeParser(sql, params, SqlBaseParser::singleStatement, AstBuilder::plan); } + /** + * Parses an expression - used only in tests + */ public Expression createExpression(String expression) { + return createExpression(expression, Collections.emptyList()); + } + + /** + * Parses an expression - Used only in tests + */ + public Expression createExpression(String expression, List params) { if (log.isDebugEnabled()) { log.debug("Parsing as expression: {}", expression); } - return invokeParser("expression", expression, SqlBaseParser::singleExpression, AstBuilder::expression); + return invokeParser(expression, params, SqlBaseParser::singleExpression, AstBuilder::expression); } - private T invokeParser(String name, String sql, Function parseFunction, - BiFunction visitor) { + private T invokeParser(String sql, List params, Function parseFunction, + BiFunction visitor) { SqlBaseLexer lexer = new SqlBaseLexer(new CaseInsensitiveStream(sql)); lexer.removeErrorListeners(); lexer.addErrorListener(ERROR_LISTENER); - CommonTokenStream tokenStream = new CommonTokenStream(lexer); + Map paramTokens = new HashMap<>(); + TokenSource tokenSource = new ParametrizedTokenSource(lexer, paramTokens, params); + + CommonTokenStream tokenStream = new CommonTokenStream(tokenSource); SqlBaseParser parser = new SqlBaseParser(tokenStream); parser.addParseListener(new PostProcessor(Arrays.asList(parser.getRuleNames()))); @@ -83,9 +110,7 @@ public class SqlParser { ParserRuleContext tree = parseFunction.apply(parser); - postProcess(lexer, parser, tree); - - return visitor.apply(new AstBuilder(timeZone), tree); + return visitor.apply(new AstBuilder(paramTokens), tree); } private void debug(SqlBaseParser parser) { @@ -105,10 +130,6 @@ public class SqlParser { }); } - protected void postProcess(SqlBaseLexer lexer, SqlBaseParser parser, ParserRuleContext tree) { - // no-op - } - private class PostProcessor extends SqlBaseBaseListener { private final List ruleNames; @@ -178,4 +199,68 @@ public class SqlParser { throw new ParsingException(message, e, line, charPositionInLine); } }; + + /** + * Finds all parameter tokens (?) and associates them with actual parameter values + *

+ * Parameters are positional and we know where parameters occurred in the original stream in order to associate them + * with actual values. + */ + private static class ParametrizedTokenSource implements TokenSource { + + private TokenSource delegate; + private Map paramTokens; + private int param; + private List params; + + ParametrizedTokenSource(TokenSource delegate, Map paramTokens, List params) { + this.delegate = delegate; + this.paramTokens = paramTokens; + this.params = params; + param = 0; + } + + @Override + public Token nextToken() { + Token token = delegate.nextToken(); + if (token.getType() == SqlBaseLexer.PARAM) { + if (param >= params.size()) { + throw new ParsingException("Not enough actual parameters {} ", params.size()); + } + paramTokens.put(token, params.get(param)); + param++; + } + return token; + } + + @Override + public int getLine() { + return delegate.getLine(); + } + + @Override + public int getCharPositionInLine() { + return delegate.getCharPositionInLine(); + } + + @Override + public CharStream getInputStream() { + return delegate.getInputStream(); + } + + @Override + public String getSourceName() { + return delegate.getSourceName(); + } + + @Override + public void setTokenFactory(TokenFactory factory) { + delegate.setTokenFactory(factory); + } + + @Override + public TokenFactory getTokenFactory() { + return delegate.getTokenFactory(); + } + } } diff --git a/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/plugin/TransportSqlQueryAction.java b/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/plugin/TransportSqlQueryAction.java index c1f679d76b2..3aa76f3f450 100644 --- a/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/plugin/TransportSqlQueryAction.java +++ b/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/plugin/TransportSqlQueryAction.java @@ -61,7 +61,7 @@ public class TransportSqlQueryAction extends HandledTransportAction listener.onResponse(createResponse(request, rowSet)), listener::onFailure)); } else { planExecutor.nextPage(cfg, Cursor.decodeFromString(request.cursor()), diff --git a/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/plugin/TransportSqlTranslateAction.java b/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/plugin/TransportSqlTranslateAction.java index d61497afe86..8f494231727 100644 --- a/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/plugin/TransportSqlTranslateAction.java +++ b/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/plugin/TransportSqlTranslateAction.java @@ -39,12 +39,11 @@ public class TransportSqlTranslateAction extends HandledTransportAction listener) { sqlLicenseChecker.checkIfSqlAllowed(request.mode()); - String query = request.query(); Configuration cfg = new Configuration(request.timeZone(), request.fetchSize(), request.requestTimeout(), request.pageTimeout(), request.filter()); - planExecutor.searchSource(query, cfg, ActionListener.wrap( + planExecutor.searchSource(cfg, request.query(), request.params(), ActionListener.wrap( searchSourceBuilder -> listener.onResponse(new SqlTranslateResponse(searchSourceBuilder)), listener::onFailure)); } } diff --git a/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/session/SqlSession.java b/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/session/SqlSession.java index b04bff3810a..33a682cfacf 100644 --- a/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/session/SqlSession.java +++ b/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/session/SqlSession.java @@ -21,8 +21,10 @@ import org.elasticsearch.xpack.sql.plan.TableIdentifier; import org.elasticsearch.xpack.sql.plan.logical.LogicalPlan; import org.elasticsearch.xpack.sql.plan.physical.PhysicalPlan; import org.elasticsearch.xpack.sql.planner.Planner; +import org.elasticsearch.xpack.sql.plugin.SqlTypedParamValue; import org.elasticsearch.xpack.sql.rule.RuleExecutor; +import java.util.List; import java.util.function.Function; import static org.elasticsearch.action.ActionListener.wrap; @@ -81,8 +83,8 @@ public class SqlSession { return optimizer; } - private LogicalPlan doParse(String sql) { - return new SqlParser(settings.timeZone()).createStatement(sql); + private LogicalPlan doParse(String sql, List params) { + return new SqlParser().createStatement(sql, params); } public void analyzedPlan(LogicalPlan parsed, boolean verify, ActionListener listener) { @@ -145,13 +147,13 @@ public class SqlSession { optimizedPlan(optimized, wrap(o -> listener.onResponse(planner.plan(o, verify)), listener::onFailure)); } - public void sql(String sql, ActionListener listener) { - sqlExecutable(sql, wrap(e -> e.execute(this, listener), listener::onFailure)); + public void sql(String sql, List params, ActionListener listener) { + sqlExecutable(sql, params, wrap(e -> e.execute(this, listener), listener::onFailure)); } - public void sqlExecutable(String sql, ActionListener listener) { + public void sqlExecutable(String sql, List params, ActionListener listener) { try { - physicalPlan(doParse(sql), true, listener); + physicalPlan(doParse(sql, params), true, listener); } catch (Exception ex) { listener.onFailure(ex); } diff --git a/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/type/Types.java b/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/type/Types.java index fd537d9f667..e0d3d2b35e9 100644 --- a/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/type/Types.java +++ b/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/type/Types.java @@ -43,7 +43,7 @@ public abstract class Types { private static DataType getType(Map content) { if (content.containsKey("type")) { try { - return DataType.valueOf(content.get("type").toString().toUpperCase(Locale.ROOT)); + return DataType.fromEsType(content.get("type").toString()); } catch (IllegalArgumentException ex) { return DataType.UNSUPPORTED; } diff --git a/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/analysis/analyzer/FieldAttributeTests.java b/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/analysis/analyzer/FieldAttributeTests.java index 73c59f6ef41..ce916b3fcb1 100644 --- a/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/analysis/analyzer/FieldAttributeTests.java +++ b/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/analysis/analyzer/FieldAttributeTests.java @@ -42,7 +42,7 @@ public class FieldAttributeTests extends ESTestCase { private Analyzer analyzer; public FieldAttributeTests() { - parser = new SqlParser(DateTimeZone.UTC); + parser = new SqlParser(); functionRegistry = new FunctionRegistry(); Map mapping = TypesTests.loadMapping("mapping-multi-field-variation.json"); diff --git a/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/analysis/analyzer/PreAnalyzerTests.java b/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/analysis/analyzer/PreAnalyzerTests.java index 44d34f67b3e..43aacd52083 100644 --- a/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/analysis/analyzer/PreAnalyzerTests.java +++ b/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/analysis/analyzer/PreAnalyzerTests.java @@ -9,7 +9,6 @@ import org.elasticsearch.test.ESTestCase; import org.elasticsearch.xpack.sql.analysis.analyzer.PreAnalyzer.PreAnalysis; import org.elasticsearch.xpack.sql.parser.SqlParser; import org.elasticsearch.xpack.sql.plan.logical.LogicalPlan; -import org.joda.time.DateTimeZone; import static org.hamcrest.CoreMatchers.nullValue; import static org.hamcrest.Matchers.hasSize; @@ -17,7 +16,7 @@ import static org.hamcrest.Matchers.is; public class PreAnalyzerTests extends ESTestCase { - private SqlParser parser = new SqlParser(DateTimeZone.UTC); + private SqlParser parser = new SqlParser(); private PreAnalyzer preAnalyzer = new PreAnalyzer(); public void testBasicIndex() { diff --git a/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/analysis/analyzer/VerifierErrorMessagesTests.java b/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/analysis/analyzer/VerifierErrorMessagesTests.java index f123a1b1e2d..750c181d828 100644 --- a/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/analysis/analyzer/VerifierErrorMessagesTests.java +++ b/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/analysis/analyzer/VerifierErrorMessagesTests.java @@ -18,7 +18,7 @@ import org.joda.time.DateTimeZone; import java.util.Map; public class VerifierErrorMessagesTests extends ESTestCase { - private SqlParser parser = new SqlParser(DateTimeZone.UTC); + private SqlParser parser = new SqlParser(); private String verify(String sql) { Map mapping = TypesTests.loadMapping("mapping-multi-field-with-nested.json"); diff --git a/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/expression/ParameterTests.java b/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/expression/ParameterTests.java new file mode 100644 index 00000000000..5e359659859 --- /dev/null +++ b/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/expression/ParameterTests.java @@ -0,0 +1,71 @@ +/* + * 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; + +import org.elasticsearch.test.ESTestCase; +import org.elasticsearch.xpack.sql.expression.function.scalar.arithmetic.Add; +import org.elasticsearch.xpack.sql.expression.function.scalar.arithmetic.Mul; +import org.elasticsearch.xpack.sql.expression.function.scalar.arithmetic.Sub; +import org.elasticsearch.xpack.sql.expression.predicate.Equals; +import org.elasticsearch.xpack.sql.parser.ParsingException; +import org.elasticsearch.xpack.sql.parser.SqlParser; +import org.elasticsearch.xpack.sql.plugin.SqlTypedParamValue; +import org.elasticsearch.xpack.sql.type.DataType; + +import java.util.Arrays; +import java.util.Collections; + +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.instanceOf; + + +public class ParameterTests extends ESTestCase { + + public void testSingleParameter() { + Expression expression = new SqlParser().createExpression("a = \n?", + Collections.singletonList( + new SqlTypedParamValue("foo", DataType.KEYWORD) + )); + logger.info(expression); + assertThat(expression, instanceOf(Equals.class)); + Expression right = ((Equals) expression).right(); + assertThat(right, instanceOf(Literal.class)); + Literal param = (Literal) right; + assertThat(param.dataType(), equalTo(DataType.KEYWORD)); + assertThat(param.dataType(), equalTo(DataType.KEYWORD)); + assertThat(param.value(), equalTo("foo")); + } + + public void testMultipleParameters() { + Expression expression = new SqlParser().createExpression("(? + ? * ?) - ?", Arrays.asList( + new SqlTypedParamValue(1L, DataType.LONG), + new SqlTypedParamValue(2L, DataType.LONG), + new SqlTypedParamValue(3L, DataType.LONG), + new SqlTypedParamValue(4L, DataType.LONG) + )); + assertThat(expression, instanceOf(Sub.class)); + Sub sub = (Sub) expression; + assertThat(((Literal) sub.right()).value(), equalTo(4L)); + assertThat(sub.left(), instanceOf(Add.class)); + Add add = (Add) sub.left(); + assertThat(((Literal) add.left()).value(), equalTo(1L)); + assertThat(add.right(), instanceOf(Mul.class)); + Mul mul = (Mul) add.right(); + assertThat(((Literal) mul.left()).value(), equalTo(2L)); + assertThat(((Literal) mul.right()).value(), equalTo(3L)); + } + + public void testNotEnoughParameters() { + ParsingException ex = expectThrows(ParsingException.class, + () -> new SqlParser().createExpression("(? + ? * ?) - ?", Arrays.asList( + new SqlTypedParamValue(1L, DataType.LONG), + new SqlTypedParamValue(2L, DataType.LONG), + new SqlTypedParamValue(3L, DataType.LONG) + ))); + assertThat(ex.getMessage(), containsString("Not enough actual parameters")); + } +} diff --git a/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/expression/QuotingTests.java b/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/expression/QuotingTests.java index 1c2a0017068..ceb9611a62c 100644 --- a/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/expression/QuotingTests.java +++ b/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/expression/QuotingTests.java @@ -11,7 +11,6 @@ import org.elasticsearch.xpack.sql.parser.SqlParser; import org.elasticsearch.xpack.sql.plan.logical.LogicalPlan; import org.elasticsearch.xpack.sql.plan.logical.OrderBy; import org.elasticsearch.xpack.sql.tree.Location; -import org.joda.time.DateTimeZone; import java.util.ArrayList; import java.util.List; @@ -40,7 +39,7 @@ public class QuotingTests extends ESTestCase { public void testSingleQuoteLiteral() { String name = "@timestamp"; - Expression exp = new SqlParser(DateTimeZone.UTC).createExpression("'" + name + "'"); + Expression exp = new SqlParser().createExpression("'" + name + "'"); assertThat(exp, instanceOf(Literal.class)); Literal l = (Literal) exp; assertThat(l.value(), equalTo(name)); @@ -49,7 +48,7 @@ public class QuotingTests extends ESTestCase { public void testMultiSingleQuotedLiteral() { String first = "bucket"; String second = "head"; - Expression exp = new SqlParser(DateTimeZone.UTC).createExpression(String.format(Locale.ROOT, "'%s' '%s'", first, second)); + Expression exp = new SqlParser().createExpression(String.format(Locale.ROOT, "'%s' '%s'", first, second)); assertThat(exp, instanceOf(Literal.class)); Literal l = (Literal) exp; assertThat(l.value(), equalTo(first + second)); @@ -58,7 +57,7 @@ public class QuotingTests extends ESTestCase { public void testQuotedAttribute() { String quote = "\""; String name = "@timestamp"; - Expression exp = new SqlParser(DateTimeZone.UTC).createExpression(quote + name + quote); + Expression exp = new SqlParser().createExpression(quote + name + quote); assertThat(exp, instanceOf(UnresolvedAttribute.class)); UnresolvedAttribute ua = (UnresolvedAttribute) exp; assertThat(ua.name(), equalTo(name)); @@ -70,7 +69,7 @@ public class QuotingTests extends ESTestCase { String quote = "`"; String name = "@timestamp"; ParsingException ex = expectThrows(ParsingException.class, () -> - new SqlParser(DateTimeZone.UTC).createExpression(quote + name + quote)); + new SqlParser().createExpression(quote + name + quote)); assertThat(ex.getMessage(), equalTo("line 1:1: backquoted indetifiers not supported; please use double quotes instead")); } @@ -78,7 +77,7 @@ public class QuotingTests extends ESTestCase { String quote = "\""; String qualifier = "table"; String name = "@timestamp"; - Expression exp = new SqlParser(DateTimeZone.UTC).createExpression(quote + qualifier + quote + "." + quote + name + quote); + Expression exp = new SqlParser().createExpression(quote + qualifier + quote + "." + quote + name + quote); assertThat(exp, instanceOf(UnresolvedAttribute.class)); UnresolvedAttribute ua = (UnresolvedAttribute) exp; assertThat(ua.name(), equalTo(qualifier + "." + name)); @@ -92,12 +91,12 @@ public class QuotingTests extends ESTestCase { String qualifier = "table"; String name = "@timestamp"; ParsingException ex = expectThrows(ParsingException.class, () -> - new SqlParser(DateTimeZone.UTC).createExpression(quote + qualifier + quote + "." + quote + name + quote)); + new SqlParser().createExpression(quote + qualifier + quote + "." + quote + name + quote)); assertThat(ex.getMessage(), equalTo("line 1:1: backquoted indetifiers not supported; please use double quotes instead")); } public void testGreedyQuoting() { - LogicalPlan plan = new SqlParser(DateTimeZone.UTC).createStatement("SELECT * FROM \"table\" ORDER BY \"field\""); + LogicalPlan plan = new SqlParser().createStatement("SELECT * FROM \"table\" ORDER BY \"field\""); final List plans = new ArrayList<>(); plan.forEachDown(plans::add); assertThat(plans, hasSize(4)); diff --git a/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/parser/LikeEscapingParsingTests.java b/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/parser/LikeEscapingParsingTests.java index 3d17fdd4931..b0b19aaa94c 100644 --- a/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/parser/LikeEscapingParsingTests.java +++ b/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/parser/LikeEscapingParsingTests.java @@ -9,7 +9,6 @@ import org.elasticsearch.test.ESTestCase; import org.elasticsearch.xpack.sql.expression.Expression; import org.elasticsearch.xpack.sql.expression.regex.Like; import org.elasticsearch.xpack.sql.expression.regex.LikePattern; -import org.joda.time.DateTimeZone; import java.util.Locale; @@ -18,7 +17,7 @@ import static org.hamcrest.Matchers.is; public class LikeEscapingParsingTests extends ESTestCase { - private final SqlParser parser = new SqlParser(DateTimeZone.UTC); + private final SqlParser parser = new SqlParser(); private String error(String pattern) { ParsingException ex = expectThrows(ParsingException.class, diff --git a/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/parser/SqlParserTests.java b/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/parser/SqlParserTests.java index 58051da050b..f7f03e5e4b7 100644 --- a/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/parser/SqlParserTests.java +++ b/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/parser/SqlParserTests.java @@ -14,7 +14,6 @@ import org.elasticsearch.xpack.sql.expression.function.UnresolvedFunction; import org.elasticsearch.xpack.sql.plan.logical.LogicalPlan; import org.elasticsearch.xpack.sql.plan.logical.OrderBy; import org.elasticsearch.xpack.sql.plan.logical.Project; -import org.joda.time.DateTimeZone; import java.util.ArrayList; import java.util.List; @@ -88,7 +87,7 @@ public class SqlParserTests extends ESTestCase { } private LogicalPlan parseStatement(String sql) { - return new SqlParser(DateTimeZone.UTC).createStatement(sql); + return new SqlParser().createStatement(sql); } private Project project(LogicalPlan plan) { diff --git a/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/plan/logical/command/sys/SysCatalogTests.java b/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/plan/logical/command/sys/SysCatalogTests.java index 84d73337271..8748e85815b 100644 --- a/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/plan/logical/command/sys/SysCatalogTests.java +++ b/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/plan/logical/command/sys/SysCatalogTests.java @@ -23,7 +23,7 @@ import static org.mockito.Mockito.mock; public class SysCatalogTests extends ESTestCase { - private final SqlParser parser = new SqlParser(DateTimeZone.UTC); + private final SqlParser parser = new SqlParser(); private Tuple sql(String sql) { EsIndex test = new EsIndex("test", TypesTests.loadMapping("mapping-multi-field-with-nested.json", true)); diff --git a/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/plan/logical/command/sys/SysParserTests.java b/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/plan/logical/command/sys/SysParserTests.java index e09da4a8858..aa49c782c86 100644 --- a/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/plan/logical/command/sys/SysParserTests.java +++ b/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/plan/logical/command/sys/SysParserTests.java @@ -33,7 +33,7 @@ import static org.mockito.Mockito.when; public class SysParserTests extends ESTestCase { - private final SqlParser parser = new SqlParser(DateTimeZone.UTC); + private final SqlParser parser = new SqlParser(); private final Map mapping = TypesTests.loadMapping("mapping-multi-field-with-nested.json", true); @SuppressWarnings({ "rawtypes", "unchecked" }) diff --git a/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/plan/logical/command/sys/SysTableTypesTests.java b/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/plan/logical/command/sys/SysTableTypesTests.java index 8568fed6867..2703e440803 100644 --- a/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/plan/logical/command/sys/SysTableTypesTests.java +++ b/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/plan/logical/command/sys/SysTableTypesTests.java @@ -27,7 +27,7 @@ import static org.mockito.Mockito.when; public class SysTableTypesTests extends ESTestCase { - private final SqlParser parser = new SqlParser(DateTimeZone.UTC); + private final SqlParser parser = new SqlParser(); @SuppressWarnings({ "rawtypes", "unchecked" }) private Tuple sql(String sql) { diff --git a/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/plan/logical/command/sys/SysTablesTests.java b/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/plan/logical/command/sys/SysTablesTests.java index 0f3b2bcb771..ac53cdc8b60 100644 --- a/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/plan/logical/command/sys/SysTablesTests.java +++ b/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/plan/logical/command/sys/SysTablesTests.java @@ -37,7 +37,7 @@ import static org.mockito.Mockito.when; public class SysTablesTests extends ESTestCase { - private final SqlParser parser = new SqlParser(DateTimeZone.UTC); + private final SqlParser parser = new SqlParser(); private final Map mapping = TypesTests.loadMapping("mapping-multi-field-with-nested.json", true); private final IndexInfo index = new IndexInfo("test", IndexType.INDEX); private final IndexInfo alias = new IndexInfo("alias", IndexType.ALIAS); diff --git a/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/planner/QueryTranslatorTests.java b/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/planner/QueryTranslatorTests.java index 953790defb8..be25c16fe80 100644 --- a/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/planner/QueryTranslatorTests.java +++ b/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/planner/QueryTranslatorTests.java @@ -33,7 +33,7 @@ public class QueryTranslatorTests extends ESTestCase { private Analyzer analyzer; public QueryTranslatorTests() { - parser = new SqlParser(DateTimeZone.UTC); + parser = new SqlParser(); functionRegistry = new FunctionRegistry(); Map mapping = TypesTests.loadMapping("mapping-multi-field-variation.json"); diff --git a/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/planner/VerifierErrorMessagesTests.java b/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/planner/VerifierErrorMessagesTests.java index 27c715c258d..621a0fc1af9 100644 --- a/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/planner/VerifierErrorMessagesTests.java +++ b/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/planner/VerifierErrorMessagesTests.java @@ -25,7 +25,7 @@ import java.util.Map; public class VerifierErrorMessagesTests extends ESTestCase { - private SqlParser parser = new SqlParser(DateTimeZone.UTC); + private SqlParser parser = new SqlParser(); private Optimizer optimizer = new Optimizer(); private Planner planner = new Planner(); diff --git a/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/type/DataTypeConversionTests.java b/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/type/DataTypeConversionTests.java index d6d550ead4e..a9a40167a36 100644 --- a/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/type/DataTypeConversionTests.java +++ b/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/type/DataTypeConversionTests.java @@ -191,4 +191,10 @@ public class DataTypeConversionTests extends ESTestCase { assertEquals(DataType.FLOAT, DataTypeConversion.commonType(DataType.FLOAT, DataType.INTEGER)); assertEquals(DataType.DOUBLE, DataTypeConversion.commonType(DataType.DOUBLE, DataType.FLOAT)); } + + public void testEsDataTypes() { + for (DataType type : DataType.values()) { + assertEquals(type, DataType.fromEsType(type.esType)); + } + } } diff --git a/qa/sql/multinode/src/test/java/org/elasticsearch/xpack/qa/sql/multinode/JdbcPreparedStatementIT.java b/qa/sql/multinode/src/test/java/org/elasticsearch/xpack/qa/sql/multinode/JdbcPreparedStatementIT.java new file mode 100644 index 00000000000..155e9bf161b --- /dev/null +++ b/qa/sql/multinode/src/test/java/org/elasticsearch/xpack/qa/sql/multinode/JdbcPreparedStatementIT.java @@ -0,0 +1,11 @@ +/* + * 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.qa.sql.multinode; + +import org.elasticsearch.xpack.qa.sql.jdbc.PreparedStatementTestCase; + +public class JdbcPreparedStatementIT extends PreparedStatementTestCase { +} diff --git a/qa/sql/no-security/src/test/java/org/elasticsearch/xpack/qa/sql/nosecurity/JdbcPreparedStatementIT.java b/qa/sql/no-security/src/test/java/org/elasticsearch/xpack/qa/sql/nosecurity/JdbcPreparedStatementIT.java new file mode 100644 index 00000000000..0d711c27986 --- /dev/null +++ b/qa/sql/no-security/src/test/java/org/elasticsearch/xpack/qa/sql/nosecurity/JdbcPreparedStatementIT.java @@ -0,0 +1,11 @@ +/* + * 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.qa.sql.nosecurity; + +import org.elasticsearch.xpack.qa.sql.jdbc.PreparedStatementTestCase; + +public class JdbcPreparedStatementIT extends PreparedStatementTestCase { +} diff --git a/qa/sql/security/src/test/java/org/elasticsearch/xpack/qa/sql/security/JdbcPreparedStatementIT.java b/qa/sql/security/src/test/java/org/elasticsearch/xpack/qa/sql/security/JdbcPreparedStatementIT.java new file mode 100644 index 00000000000..3ecb0d388c2 --- /dev/null +++ b/qa/sql/security/src/test/java/org/elasticsearch/xpack/qa/sql/security/JdbcPreparedStatementIT.java @@ -0,0 +1,30 @@ +/* + * 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.qa.sql.security; + +import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.xpack.qa.sql.jdbc.PreparedStatementTestCase; + +import java.util.Properties; + +public class JdbcPreparedStatementIT extends PreparedStatementTestCase { + @Override + protected Settings restClientSettings() { + return RestSqlIT.securitySettings(); + } + + @Override + protected String getProtocol() { + return RestSqlIT.SSL_ENABLED ? "https" : "http"; + } + + @Override + protected Properties connectionProperties() { + Properties sp = super.connectionProperties(); + sp.putAll(JdbcSecurityIT.adminProperties()); + return sp; + } +} diff --git a/qa/sql/src/main/java/org/elasticsearch/xpack/qa/sql/jdbc/PreparedStatementTestCase.java b/qa/sql/src/main/java/org/elasticsearch/xpack/qa/sql/jdbc/PreparedStatementTestCase.java new file mode 100644 index 00000000000..c4ac31120a3 --- /dev/null +++ b/qa/sql/src/main/java/org/elasticsearch/xpack/qa/sql/jdbc/PreparedStatementTestCase.java @@ -0,0 +1,202 @@ +/* + * 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.qa.sql.jdbc; + +import org.elasticsearch.common.collect.Tuple; + +import java.sql.Connection; +import java.sql.JDBCType; +import java.sql.ParameterMetaData; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.ResultSetMetaData; +import java.sql.SQLException; +import java.sql.SQLSyntaxErrorException; + +import static org.hamcrest.Matchers.startsWith; + +public class PreparedStatementTestCase extends JdbcIntegrationTestCase { + + public void testSupportedTypes() throws Exception { + index("library", builder -> { + builder.field("name", "Don Quixote"); + builder.field("page_count", 1072); + }); + + String stringVal = randomAlphaOfLength(randomIntBetween(0, 1000)); + int intVal = randomInt(); + long longVal = randomLong(); + double doubleVal = randomDouble(); + float floatVal = randomFloat(); + boolean booleanVal = randomBoolean(); + byte byteVal = randomByte(); + short shortVal = randomShort(); + + try (Connection connection = esJdbc()) { + try (PreparedStatement statement = connection.prepareStatement( + "SELECT ?, ?, ?, ?, ?, ?, ?, ?, ?, name FROM library WHERE page_count=?")) { + statement.setString(1, stringVal); + statement.setInt(2, intVal); + statement.setLong(3, longVal); + statement.setFloat(4, floatVal); + statement.setDouble(5, doubleVal); + statement.setNull(6, JDBCType.DOUBLE.getVendorTypeNumber()); + statement.setBoolean(7, booleanVal); + statement.setByte(8, byteVal); + statement.setShort(9, shortVal); + statement.setInt(10, 1072); + + try (ResultSet results = statement.executeQuery()) { + ResultSetMetaData resultSetMetaData = results.getMetaData(); + ParameterMetaData parameterMetaData = statement.getParameterMetaData(); + assertEquals(resultSetMetaData.getColumnCount(), parameterMetaData.getParameterCount()); + for (int i = 1; i < resultSetMetaData.getColumnCount(); i++) { + // Makes sure that column types survived the round trip + assertEquals(parameterMetaData.getParameterType(i), resultSetMetaData.getColumnType(i)); + } + assertTrue(results.next()); + assertEquals(stringVal, results.getString(1)); + assertEquals(intVal, results.getInt(2)); + assertEquals(longVal, results.getLong(3)); + assertEquals(floatVal, results.getFloat(4), 0.00001f); + assertEquals(doubleVal, results.getDouble(5), 0.00001f); + assertNull(results.getObject(6)); + assertEquals(booleanVal, results.getBoolean(7)); + assertEquals(byteVal, results.getByte(8)); + assertEquals(shortVal, results.getShort(9)); + assertEquals("Don Quixote", results.getString(10)); + assertFalse(results.next()); + } + } + } + } + + public void testUnsupportedParameterUse() throws Exception { + index("library", builder -> { + builder.field("name", "Don Quixote"); + builder.field("page_count", 1072); + }); + + try (Connection connection = esJdbc()) { + // This is the current limitation of JDBC parser that it cannot detect improper use of '?' + try (PreparedStatement statement = connection.prepareStatement("SELECT name FROM ? WHERE page_count=?")) { + statement.setString(1, "library"); + statement.setInt(2, 1072); + SQLSyntaxErrorException exception = expectThrows(SQLSyntaxErrorException.class, statement::executeQuery); + assertThat(exception.getMessage(), startsWith("line 1:18: mismatched input '?' expecting ")); + + } + } + } + + public void testTooMayParameters() throws Exception { + index("library", builder -> { + builder.field("name", "Don Quixote"); + builder.field("page_count", 1072); + }); + + try (Connection connection = esJdbc()) { + try (PreparedStatement statement = connection.prepareStatement("SELECT name FROM library WHERE page_count=?")) { + statement.setInt(1, 1072); + int tooBig = randomIntBetween(2, 10); + SQLException tooBigEx = expectThrows(SQLException.class, () -> statement.setInt(tooBig, 1072)); + assertThat(tooBigEx.getMessage(), startsWith("Invalid parameter index [")); + int tooSmall = randomIntBetween(-10, 0); + SQLException tooSmallEx = expectThrows(SQLException.class, () -> statement.setInt(tooSmall, 1072)); + assertThat(tooSmallEx.getMessage(), startsWith("Invalid parameter index [")); + } + } + } + + public void testStringEscaping() throws Exception { + try (Connection connection = esJdbc()) { + try (PreparedStatement statement = connection.prepareStatement( + "SELECT ?, ?, ?, ?")) { + statement.setString(1, "foo --"); + statement.setString(2, "/* foo */"); + statement.setString(3, "\"foo"); + statement.setString(4, "'foo'"); + try (ResultSet results = statement.executeQuery()) { + ResultSetMetaData resultSetMetaData = results.getMetaData(); + assertEquals(4, resultSetMetaData.getColumnCount()); + for (int i = 1; i < resultSetMetaData.getColumnCount(); i++) { + assertEquals(JDBCType.VARCHAR.getVendorTypeNumber().intValue(), resultSetMetaData.getColumnType(i)); + } + assertTrue(results.next()); + assertEquals("foo --", results.getString(1)); + assertEquals("/* foo */", results.getString(2)); + assertEquals("\"foo", results.getString(3)); + assertEquals("'foo'", results.getString(4)); + assertFalse(results.next()); + } + } + } + } + + public void testCommentsHandling() throws Exception { + try (Connection connection = esJdbc()) { + try (PreparedStatement statement = connection.prepareStatement( + "SELECT ?, /* ?, */ ? -- ?")) { + assertEquals(2, statement.getParameterMetaData().getParameterCount()); + statement.setString(1, "foo"); + statement.setString(2, "bar"); + try (ResultSet results = statement.executeQuery()) { + ResultSetMetaData resultSetMetaData = results.getMetaData(); + assertEquals(2, resultSetMetaData.getColumnCount()); + assertTrue(results.next()); + assertEquals("foo", results.getString(1)); + assertEquals("bar", results.getString(2)); + assertFalse(results.next()); + } + } + } + } + + public void testSingleParameterMultipleTypes() throws Exception { + String stringVal = randomAlphaOfLength(randomIntBetween(0, 1000)); + int intVal = randomInt(); + long longVal = randomLong(); + double doubleVal = randomDouble(); + float floatVal = randomFloat(); + boolean booleanVal = randomBoolean(); + byte byteVal = randomByte(); + short shortVal = randomShort(); + + try (Connection connection = esJdbc()) { + try (PreparedStatement statement = connection.prepareStatement("SELECT ?")) { + + statement.setString(1, stringVal); + assertEquals(new Tuple<>(JDBCType.VARCHAR.getVendorTypeNumber(), stringVal), execute(statement)); + statement.setInt(1, intVal); + assertEquals(new Tuple<>(JDBCType.INTEGER.getVendorTypeNumber(), intVal), execute(statement)); + statement.setLong(1, longVal); + assertEquals(new Tuple<>(JDBCType.BIGINT.getVendorTypeNumber(), longVal), execute(statement)); + statement.setFloat(1, floatVal); + assertEquals(new Tuple<>(JDBCType.REAL.getVendorTypeNumber(), floatVal), execute(statement)); + statement.setDouble(1, doubleVal); + assertEquals(new Tuple<>(JDBCType.DOUBLE.getVendorTypeNumber(), doubleVal), execute(statement)); + statement.setNull(1, JDBCType.DOUBLE.getVendorTypeNumber()); + assertEquals(new Tuple<>(JDBCType.DOUBLE.getVendorTypeNumber(), null), execute(statement)); + statement.setBoolean(1, booleanVal); + assertEquals(new Tuple<>(JDBCType.BOOLEAN.getVendorTypeNumber(), booleanVal), execute(statement)); + statement.setByte(1, byteVal); + assertEquals(new Tuple<>(JDBCType.TINYINT.getVendorTypeNumber(), byteVal), execute(statement)); + statement.setShort(1, shortVal); + assertEquals(new Tuple<>(JDBCType.SMALLINT.getVendorTypeNumber(), shortVal), execute(statement)); + } + } + } + + private Tuple execute(PreparedStatement statement) throws SQLException { + try (ResultSet results = statement.executeQuery()) { + ResultSetMetaData resultSetMetaData = results.getMetaData(); + assertTrue(results.next()); + Tuple result = new Tuple<>(resultSetMetaData.getColumnType(1), results.getObject(1)); + assertFalse(results.next()); + return result; + } + } +} diff --git a/qa/sql/src/main/java/org/elasticsearch/xpack/qa/sql/rest/RestSqlTestCase.java b/qa/sql/src/main/java/org/elasticsearch/xpack/qa/sql/rest/RestSqlTestCase.java index 788f7ae955f..c038e058b2f 100644 --- a/qa/sql/src/main/java/org/elasticsearch/xpack/qa/sql/rest/RestSqlTestCase.java +++ b/qa/sql/src/main/java/org/elasticsearch/xpack/qa/sql/rest/RestSqlTestCase.java @@ -380,6 +380,22 @@ public abstract class RestSqlTestCase extends ESRestTestCase implements ErrorsTe ContentType.APPLICATION_JSON))); } + public void testBasicQueryWithParameters() throws IOException { + String mode = randomMode(); + index("{\"test\":\"foo\"}", + "{\"test\":\"bar\"}"); + + Map expected = new HashMap<>(); + expected.put("columns", Arrays.asList( + columnInfo(mode, "test", "text", JDBCType.VARCHAR, 0), + columnInfo(mode, "param", "integer", JDBCType.INTEGER, 11) + )); + expected.put("rows", singletonList(Arrays.asList("foo", 10))); + assertResponse(expected, runSql(mode, new StringEntity("{\"query\":\"SELECT test, ? param FROM test WHERE test = ?\", " + + "\"params\":[{\"type\": \"integer\", \"value\": 10}, {\"type\": \"keyword\", \"value\": \"foo\"}]}", + ContentType.APPLICATION_JSON))); + } + public void testBasicTranslateQueryWithFilter() throws IOException { index("{\"test\":\"foo\"}", "{\"test\":\"bar\"}");