SQL: generalize the use of ? for STRING (elastic/x-pack-elasticsearch#4359)
Improve grammar to allow use of ? as an alternative to STRING through-out all commands Add various parsing tests checking the ? usage for SYS commands Original commit: elastic/x-pack-elasticsearch@d0d1feeb4c
This commit is contained in:
parent
0d83edbca5
commit
fc5e1631f1
|
@ -5,9 +5,9 @@
|
||||||
*/
|
*/
|
||||||
package org.elasticsearch.xpack.sql.jdbc.jdbc;
|
package org.elasticsearch.xpack.sql.jdbc.jdbc;
|
||||||
|
|
||||||
|
import org.elasticsearch.xpack.sql.client.shared.Version;
|
||||||
import org.elasticsearch.xpack.sql.jdbc.JdbcSQLException;
|
import org.elasticsearch.xpack.sql.jdbc.JdbcSQLException;
|
||||||
import org.elasticsearch.xpack.sql.jdbc.debug.Debug;
|
import org.elasticsearch.xpack.sql.jdbc.debug.Debug;
|
||||||
import org.elasticsearch.xpack.sql.client.shared.Version;
|
|
||||||
|
|
||||||
import java.io.PrintWriter;
|
import java.io.PrintWriter;
|
||||||
import java.sql.Connection;
|
import java.sql.Connection;
|
||||||
|
@ -16,11 +16,8 @@ import java.sql.DriverPropertyInfo;
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
import java.sql.SQLFeatureNotSupportedException;
|
import java.sql.SQLFeatureNotSupportedException;
|
||||||
import java.util.Properties;
|
import java.util.Properties;
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
import static org.elasticsearch.xpack.sql.client.shared.ConnectionConfiguration.CONNECT_TIMEOUT;
|
|
||||||
|
|
||||||
public class JdbcDriver implements java.sql.Driver {
|
public class JdbcDriver implements java.sql.Driver {
|
||||||
|
|
||||||
private static final JdbcDriver INSTANCE = new JdbcDriver();
|
private static final JdbcDriver INSTANCE = new JdbcDriver();
|
||||||
|
@ -67,6 +64,7 @@ public class JdbcDriver implements java.sql.Driver {
|
||||||
//
|
//
|
||||||
// Jdbc 4.0
|
// Jdbc 4.0
|
||||||
//
|
//
|
||||||
|
@Override
|
||||||
public Connection connect(String url, Properties props) throws SQLException {
|
public Connection connect(String url, Properties props) throws SQLException {
|
||||||
if (url == null) {
|
if (url == null) {
|
||||||
throw new JdbcSQLException("Non-null url required");
|
throw new JdbcSQLException("Non-null url required");
|
||||||
|
@ -116,6 +114,7 @@ public class JdbcDriver implements java.sql.Driver {
|
||||||
// Jdbc 4.1
|
// Jdbc 4.1
|
||||||
//
|
//
|
||||||
|
|
||||||
|
@Override
|
||||||
public Logger getParentLogger() throws SQLFeatureNotSupportedException {
|
public Logger getParentLogger() throws SQLFeatureNotSupportedException {
|
||||||
throw new SQLFeatureNotSupportedException();
|
throw new SQLFeatureNotSupportedException();
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,7 +13,6 @@ import org.elasticsearch.common.xcontent.ConstructingObjectParser;
|
||||||
import org.elasticsearch.common.xcontent.ObjectParser;
|
import org.elasticsearch.common.xcontent.ObjectParser;
|
||||||
import org.elasticsearch.common.xcontent.ToXContentObject;
|
import org.elasticsearch.common.xcontent.ToXContentObject;
|
||||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||||
import org.elasticsearch.common.xcontent.XContentHelper;
|
|
||||||
import org.elasticsearch.common.xcontent.XContentParser;
|
import org.elasticsearch.common.xcontent.XContentParser;
|
||||||
import org.elasticsearch.common.xcontent.XContentParserUtils;
|
import org.elasticsearch.common.xcontent.XContentParserUtils;
|
||||||
import org.elasticsearch.xpack.sql.type.DataType;
|
import org.elasticsearch.xpack.sql.type.DataType;
|
||||||
|
@ -75,8 +74,12 @@ public class SqlTypedParamValue implements ToXContentObject, Writeable {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Object o) {
|
public boolean equals(Object o) {
|
||||||
if (this == o) return true;
|
if (this == o) {
|
||||||
if (o == null || getClass() != o.getClass()) return false;
|
return true;
|
||||||
|
}
|
||||||
|
if (o == null || getClass() != o.getClass()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
SqlTypedParamValue that = (SqlTypedParamValue) o;
|
SqlTypedParamValue that = (SqlTypedParamValue) o;
|
||||||
return Objects.equals(value, that.value) &&
|
return Objects.equals(value, that.value) &&
|
||||||
dataType == that.dataType;
|
dataType == that.dataType;
|
||||||
|
@ -84,7 +87,11 @@ public class SqlTypedParamValue implements ToXContentObject, Writeable {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
|
|
||||||
return Objects.hash(value, dataType);
|
return Objects.hash(value, dataType);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return String.valueOf(value) + "[" + dataType + "]";
|
||||||
|
}
|
||||||
|
}
|
|
@ -58,10 +58,10 @@ statement
|
||||||
| SYS CATALOGS #sysCatalogs
|
| SYS CATALOGS #sysCatalogs
|
||||||
| SYS TABLES (CATALOG LIKE? clusterPattern=pattern)?
|
| SYS TABLES (CATALOG LIKE? clusterPattern=pattern)?
|
||||||
(LIKE? tablePattern=pattern)?
|
(LIKE? tablePattern=pattern)?
|
||||||
(TYPE STRING (',' STRING)* )? #sysTables
|
(TYPE string (',' string)* )? #sysTables
|
||||||
| SYS COLUMNS (CATALOG cluster=(STRING | PARAM))?
|
| SYS COLUMNS (CATALOG cluster=string)?
|
||||||
(TABLE LIKE? indexPattern=pattern)?
|
(TABLE LIKE? indexPattern=pattern)?
|
||||||
(LIKE? columnPattern=pattern)? #sysColumns
|
(LIKE? columnPattern=pattern)? #sysColumns
|
||||||
| SYS TYPES #sysTypes
|
| SYS TYPES #sysTypes
|
||||||
| SYS TABLE TYPES #sysTableTypes
|
| SYS TABLE TYPES #sysTableTypes
|
||||||
;
|
;
|
||||||
|
@ -158,9 +158,9 @@ expression
|
||||||
booleanExpression
|
booleanExpression
|
||||||
: NOT booleanExpression #logicalNot
|
: NOT booleanExpression #logicalNot
|
||||||
| EXISTS '(' query ')' #exists
|
| EXISTS '(' query ')' #exists
|
||||||
| QUERY '(' queryString=STRING (',' options=STRING)* ')' #stringQuery
|
| QUERY '(' queryString=string (',' options=string)* ')' #stringQuery
|
||||||
| MATCH '(' singleField=qualifiedName ',' queryString=STRING (',' options=STRING)* ')' #matchQuery
|
| MATCH '(' singleField=qualifiedName ',' queryString=string (',' options=string)* ')' #matchQuery
|
||||||
| MATCH '(' multiFields=STRING ',' queryString=STRING (',' options=STRING)* ')' #multiMatchQuery
|
| MATCH '(' multiFields=string ',' queryString=string (',' options=string)* ')' #multiMatchQuery
|
||||||
| predicated #booleanDefault
|
| predicated #booleanDefault
|
||||||
| left=booleanExpression operator=AND right=booleanExpression #logicalBinary
|
| left=booleanExpression operator=AND right=booleanExpression #logicalBinary
|
||||||
| left=booleanExpression operator=OR right=booleanExpression #logicalBinary
|
| left=booleanExpression operator=OR right=booleanExpression #logicalBinary
|
||||||
|
@ -180,13 +180,12 @@ predicate
|
||||||
| NOT? kind=IN '(' expression (',' expression)* ')'
|
| NOT? kind=IN '(' expression (',' expression)* ')'
|
||||||
| NOT? kind=IN '(' query ')'
|
| NOT? kind=IN '(' query ')'
|
||||||
| NOT? kind=LIKE pattern
|
| NOT? kind=LIKE pattern
|
||||||
| NOT? kind=RLIKE regex=STRING
|
| NOT? kind=RLIKE regex=string
|
||||||
| IS NOT? kind=NULL
|
| IS NOT? kind=NULL
|
||||||
;
|
;
|
||||||
|
|
||||||
pattern
|
pattern
|
||||||
: value=STRING (ESCAPE escape=STRING)?
|
: value=string (ESCAPE escape=string)?
|
||||||
| PARAM
|
|
||||||
;
|
;
|
||||||
|
|
||||||
valueExpression
|
valueExpression
|
||||||
|
@ -216,7 +215,7 @@ constant
|
||||||
| number #numericLiteral
|
| number #numericLiteral
|
||||||
| booleanValue #booleanLiteral
|
| booleanValue #booleanLiteral
|
||||||
| STRING+ #stringLiteral
|
| STRING+ #stringLiteral
|
||||||
| PARAM #param
|
| PARAM #paramLiteral
|
||||||
;
|
;
|
||||||
|
|
||||||
comparisonOperator
|
comparisonOperator
|
||||||
|
@ -261,6 +260,11 @@ number
|
||||||
| INTEGER_VALUE #integerLiteral
|
| INTEGER_VALUE #integerLiteral
|
||||||
;
|
;
|
||||||
|
|
||||||
|
string
|
||||||
|
: PARAM
|
||||||
|
| STRING
|
||||||
|
;
|
||||||
|
|
||||||
// http://developer.mimer.se/validator/sql-reserved-words.tml
|
// http://developer.mimer.se/validator/sql-reserved-words.tml
|
||||||
nonReserved
|
nonReserved
|
||||||
: ANALYZE | ANALYZED
|
: ANALYZE | ANALYZED
|
||||||
|
|
|
@ -92,13 +92,6 @@ abstract class AbstractBuilder extends SqlBaseBaseVisitor<Object> {
|
||||||
return node == null ? null : node.getText();
|
return node == null ? null : node.getText();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Extracts the actual unescaped string (literal) value of a token.
|
|
||||||
*/
|
|
||||||
static String string(Token token) {
|
|
||||||
return token == null ? null : unquoteString(token.getText());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Extracts the actual unescaped string (literal) value of a terminal node.
|
* Extracts the actual unescaped string (literal) value of a terminal node.
|
||||||
*/
|
*/
|
||||||
|
@ -115,4 +108,4 @@ abstract class AbstractBuilder extends SqlBaseBaseVisitor<Object> {
|
||||||
public Object visitTerminal(TerminalNode node) {
|
public Object visitTerminal(TerminalNode node) {
|
||||||
throw new ParsingException(source(node), "Does not know how to handle {}", node.getText());
|
throw new ParsingException(source(node), "Does not know how to handle {}", node.getText());
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -6,7 +6,6 @@
|
||||||
package org.elasticsearch.xpack.sql.parser;
|
package org.elasticsearch.xpack.sql.parser;
|
||||||
|
|
||||||
import org.antlr.v4.runtime.Token;
|
import org.antlr.v4.runtime.Token;
|
||||||
import org.antlr.v4.runtime.tree.TerminalNode;
|
|
||||||
import org.elasticsearch.common.Booleans;
|
import org.elasticsearch.common.Booleans;
|
||||||
import org.elasticsearch.xpack.sql.analysis.index.IndexResolver.IndexType;
|
import org.elasticsearch.xpack.sql.analysis.index.IndexResolver.IndexType;
|
||||||
import org.elasticsearch.xpack.sql.parser.SqlBaseParser.DebugContext;
|
import org.elasticsearch.xpack.sql.parser.SqlBaseParser.DebugContext;
|
||||||
|
@ -15,6 +14,7 @@ import org.elasticsearch.xpack.sql.parser.SqlBaseParser.ShowColumnsContext;
|
||||||
import org.elasticsearch.xpack.sql.parser.SqlBaseParser.ShowFunctionsContext;
|
import org.elasticsearch.xpack.sql.parser.SqlBaseParser.ShowFunctionsContext;
|
||||||
import org.elasticsearch.xpack.sql.parser.SqlBaseParser.ShowSchemasContext;
|
import org.elasticsearch.xpack.sql.parser.SqlBaseParser.ShowSchemasContext;
|
||||||
import org.elasticsearch.xpack.sql.parser.SqlBaseParser.ShowTablesContext;
|
import org.elasticsearch.xpack.sql.parser.SqlBaseParser.ShowTablesContext;
|
||||||
|
import org.elasticsearch.xpack.sql.parser.SqlBaseParser.StringContext;
|
||||||
import org.elasticsearch.xpack.sql.parser.SqlBaseParser.SysCatalogsContext;
|
import org.elasticsearch.xpack.sql.parser.SqlBaseParser.SysCatalogsContext;
|
||||||
import org.elasticsearch.xpack.sql.parser.SqlBaseParser.SysColumnsContext;
|
import org.elasticsearch.xpack.sql.parser.SqlBaseParser.SysColumnsContext;
|
||||||
import org.elasticsearch.xpack.sql.parser.SqlBaseParser.SysTableTypesContext;
|
import org.elasticsearch.xpack.sql.parser.SqlBaseParser.SysTableTypesContext;
|
||||||
|
@ -43,6 +43,7 @@ import java.util.Locale;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
abstract class CommandBuilder extends LogicalPlanBuilder {
|
abstract class CommandBuilder extends LogicalPlanBuilder {
|
||||||
|
|
||||||
protected CommandBuilder(Map<Token, SqlTypedParamValue> params) {
|
protected CommandBuilder(Map<Token, SqlTypedParamValue> params) {
|
||||||
super(params);
|
super(params);
|
||||||
}
|
}
|
||||||
|
@ -146,7 +147,7 @@ abstract class CommandBuilder extends LogicalPlanBuilder {
|
||||||
@Override
|
@Override
|
||||||
public SysTables visitSysTables(SysTablesContext ctx) {
|
public SysTables visitSysTables(SysTablesContext ctx) {
|
||||||
List<IndexType> types = new ArrayList<>();
|
List<IndexType> types = new ArrayList<>();
|
||||||
for (TerminalNode string : ctx.STRING()) {
|
for (StringContext string : ctx.string()) {
|
||||||
String value = string(string);
|
String value = string(string);
|
||||||
if (value != null) {
|
if (value != null) {
|
||||||
IndexType type = IndexType.from(value);
|
IndexType type = IndexType.from(value);
|
||||||
|
@ -163,7 +164,7 @@ abstract class CommandBuilder extends LogicalPlanBuilder {
|
||||||
@Override
|
@Override
|
||||||
public Object visitSysColumns(SysColumnsContext ctx) {
|
public Object visitSysColumns(SysColumnsContext ctx) {
|
||||||
Location loc = source(ctx);
|
Location loc = source(ctx);
|
||||||
return new SysColumns(loc, stringOrParam(ctx.cluster, loc), visitPattern(ctx.indexPattern), visitPattern(ctx.columnPattern));
|
return new SysColumns(loc, string(ctx.cluster), visitPattern(ctx.indexPattern), visitPattern(ctx.columnPattern));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -171,6 +172,7 @@ abstract class CommandBuilder extends LogicalPlanBuilder {
|
||||||
return new SysTypes(source(ctx));
|
return new SysTypes(source(ctx));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public Object visitSysTableTypes(SysTableTypesContext ctx) {
|
public Object visitSysTableTypes(SysTableTypesContext ctx) {
|
||||||
return new SysTableTypes(source(ctx));
|
return new SysTableTypes(source(ctx));
|
||||||
}
|
}
|
||||||
|
|
|
@ -63,6 +63,7 @@ import org.elasticsearch.xpack.sql.parser.SqlBaseParser.MatchQueryContext;
|
||||||
import org.elasticsearch.xpack.sql.parser.SqlBaseParser.MultiMatchQueryContext;
|
import org.elasticsearch.xpack.sql.parser.SqlBaseParser.MultiMatchQueryContext;
|
||||||
import org.elasticsearch.xpack.sql.parser.SqlBaseParser.NullLiteralContext;
|
import org.elasticsearch.xpack.sql.parser.SqlBaseParser.NullLiteralContext;
|
||||||
import org.elasticsearch.xpack.sql.parser.SqlBaseParser.OrderByContext;
|
import org.elasticsearch.xpack.sql.parser.SqlBaseParser.OrderByContext;
|
||||||
|
import org.elasticsearch.xpack.sql.parser.SqlBaseParser.ParamLiteralContext;
|
||||||
import org.elasticsearch.xpack.sql.parser.SqlBaseParser.ParenthesizedExpressionContext;
|
import org.elasticsearch.xpack.sql.parser.SqlBaseParser.ParenthesizedExpressionContext;
|
||||||
import org.elasticsearch.xpack.sql.parser.SqlBaseParser.PatternContext;
|
import org.elasticsearch.xpack.sql.parser.SqlBaseParser.PatternContext;
|
||||||
import org.elasticsearch.xpack.sql.parser.SqlBaseParser.PredicateContext;
|
import org.elasticsearch.xpack.sql.parser.SqlBaseParser.PredicateContext;
|
||||||
|
@ -71,6 +72,7 @@ import org.elasticsearch.xpack.sql.parser.SqlBaseParser.PrimitiveDataTypeContext
|
||||||
import org.elasticsearch.xpack.sql.parser.SqlBaseParser.SelectExpressionContext;
|
import org.elasticsearch.xpack.sql.parser.SqlBaseParser.SelectExpressionContext;
|
||||||
import org.elasticsearch.xpack.sql.parser.SqlBaseParser.SingleExpressionContext;
|
import org.elasticsearch.xpack.sql.parser.SqlBaseParser.SingleExpressionContext;
|
||||||
import org.elasticsearch.xpack.sql.parser.SqlBaseParser.StarContext;
|
import org.elasticsearch.xpack.sql.parser.SqlBaseParser.StarContext;
|
||||||
|
import org.elasticsearch.xpack.sql.parser.SqlBaseParser.StringContext;
|
||||||
import org.elasticsearch.xpack.sql.parser.SqlBaseParser.StringLiteralContext;
|
import org.elasticsearch.xpack.sql.parser.SqlBaseParser.StringLiteralContext;
|
||||||
import org.elasticsearch.xpack.sql.parser.SqlBaseParser.StringQueryContext;
|
import org.elasticsearch.xpack.sql.parser.SqlBaseParser.StringQueryContext;
|
||||||
import org.elasticsearch.xpack.sql.parser.SqlBaseParser.SubqueryExpressionContext;
|
import org.elasticsearch.xpack.sql.parser.SqlBaseParser.SubqueryExpressionContext;
|
||||||
|
@ -88,14 +90,10 @@ import static java.util.Collections.singletonList;
|
||||||
import static org.elasticsearch.xpack.sql.type.DataTypeConversion.conversionFor;
|
import static org.elasticsearch.xpack.sql.type.DataTypeConversion.conversionFor;
|
||||||
|
|
||||||
abstract class ExpressionBuilder extends IdentifierBuilder {
|
abstract class ExpressionBuilder extends IdentifierBuilder {
|
||||||
|
|
||||||
private final Map<Token, SqlTypedParamValue> params;
|
private final Map<Token, SqlTypedParamValue> params;
|
||||||
|
|
||||||
/**
|
ExpressionBuilder(Map<Token, SqlTypedParamValue> params) {
|
||||||
* ExpressionBuilder constructor
|
|
||||||
*
|
|
||||||
* @param params a map between '?' tokens that represent parameters and the actual parameter values
|
|
||||||
*/
|
|
||||||
protected ExpressionBuilder(Map<Token, SqlTypedParamValue> params) {
|
|
||||||
this.params = params;
|
this.params = params;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -215,11 +213,6 @@ abstract class ExpressionBuilder extends IdentifierBuilder {
|
||||||
return null;
|
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);
|
String pattern = string(ctx.value);
|
||||||
int pos = pattern.indexOf('*');
|
int pos = pattern.indexOf('*');
|
||||||
if (pos >= 0) {
|
if (pos >= 0) {
|
||||||
|
@ -452,16 +445,21 @@ abstract class ExpressionBuilder extends IdentifierBuilder {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object visitParam(SqlBaseParser.ParamContext ctx) {
|
public Object visitDecimalLiteral(DecimalLiteralContext ctx) {
|
||||||
Token token = ctx.PARAM().getSymbol();
|
return new Literal(source(ctx), new BigDecimal(ctx.getText()).doubleValue(), DataType.DOUBLE);
|
||||||
return paramValue(token, source(ctx));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private Object paramValue(Token token, Location loc) {
|
@Override
|
||||||
if (params.containsKey(token) == false) {
|
public Object visitIntegerLiteral(IntegerLiteralContext ctx) {
|
||||||
throw new ParsingException(loc, "Unexpected parameter");
|
BigDecimal bigD = new BigDecimal(ctx.getText());
|
||||||
}
|
// TODO: this can be improved to use the smallest type available
|
||||||
SqlTypedParamValue param = params.get(token);
|
return new Literal(source(ctx), bigD.longValueExact(), DataType.INTEGER);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object visitParamLiteral(ParamLiteralContext ctx) {
|
||||||
|
SqlTypedParamValue param = param(ctx.PARAM());
|
||||||
|
Location loc = source(ctx);
|
||||||
if (param.value == null) {
|
if (param.value == null) {
|
||||||
// no conversion is required for null values
|
// no conversion is required for null values
|
||||||
return new Literal(loc, null, param.dataType);
|
return new Literal(loc, null, param.dataType);
|
||||||
|
@ -470,8 +468,8 @@ abstract class ExpressionBuilder extends IdentifierBuilder {
|
||||||
try {
|
try {
|
||||||
sourceType = DataTypes.fromJava(param.value);
|
sourceType = DataTypes.fromJava(param.value);
|
||||||
} catch (SqlIllegalArgumentException ex) {
|
} catch (SqlIllegalArgumentException ex) {
|
||||||
throw new ParsingException(ex, loc, "Unexpected actual parameter type [{}] for type [{}]",
|
throw new ParsingException(ex, loc, "Unexpected actual parameter type [{}] for type [{}]", param.value.getClass().getName(),
|
||||||
param.value.getClass().getName(), param.dataType);
|
param.dataType);
|
||||||
}
|
}
|
||||||
if (sourceType == param.dataType) {
|
if (sourceType == param.dataType) {
|
||||||
// no conversion is required if the value is already have correct type
|
// no conversion is required if the value is already have correct type
|
||||||
|
@ -485,32 +483,37 @@ abstract class ExpressionBuilder extends IdentifierBuilder {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String visitString(StringContext ctx) {
|
||||||
|
return string(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Extracts the actual unescaped string (literal) value of a token or a parameter value
|
* Extracts the string (either as unescaped literal) or parameter.
|
||||||
*/
|
*/
|
||||||
public String stringOrParam(Token token, Location loc) {
|
String string(StringContext ctx) {
|
||||||
if (token == null) {
|
if (ctx == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
if (token.getType() == SqlBaseLexer.PARAM) {
|
SqlTypedParamValue param = param(ctx.PARAM());
|
||||||
if (params.containsKey(token) == false) {
|
if (param != null) {
|
||||||
throw new ParsingException(loc, "Unexpected parameter");
|
return param.value != null ? param.value.toString() : null;
|
||||||
}
|
} else {
|
||||||
SqlTypedParamValue param = params.get(token);
|
return unquoteString(ctx.getText());
|
||||||
return param.value == null ? null : param.value.toString();
|
|
||||||
}
|
}
|
||||||
return unquoteString(token.getText());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
private SqlTypedParamValue param(TerminalNode node) {
|
||||||
public Object visitDecimalLiteral(DecimalLiteralContext ctx) {
|
if (node == null) {
|
||||||
return new Literal(source(ctx), new BigDecimal(ctx.getText()).doubleValue(), DataType.DOUBLE);
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
Token token = node.getSymbol();
|
||||||
public Object visitIntegerLiteral(IntegerLiteralContext ctx) {
|
|
||||||
BigDecimal bigD = new BigDecimal(ctx.getText());
|
if (params.containsKey(token) == false) {
|
||||||
// TODO: this can be improved to use the smallest type available
|
throw new ParsingException(source(node), "Unexpected parameter");
|
||||||
return new Literal(source(ctx), bigD.longValueExact(), DataType.INTEGER);
|
}
|
||||||
|
|
||||||
|
return params.get(token);
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -53,6 +53,7 @@ import static java.util.Collections.emptyList;
|
||||||
import static java.util.stream.Collectors.toList;
|
import static java.util.stream.Collectors.toList;
|
||||||
|
|
||||||
abstract class LogicalPlanBuilder extends ExpressionBuilder {
|
abstract class LogicalPlanBuilder extends ExpressionBuilder {
|
||||||
|
|
||||||
protected LogicalPlanBuilder(Map<Token, SqlTypedParamValue> params) {
|
protected LogicalPlanBuilder(Map<Token, SqlTypedParamValue> params) {
|
||||||
super(params);
|
super(params);
|
||||||
}
|
}
|
||||||
|
@ -80,6 +81,7 @@ abstract class LogicalPlanBuilder extends ExpressionBuilder {
|
||||||
return new SubQueryAlias(source(ctx), plan(ctx.queryNoWith()), ctx.name.getText());
|
return new SubQueryAlias(source(ctx), plan(ctx.queryNoWith()), ctx.name.getText());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public LogicalPlan visitQueryNoWith(QueryNoWithContext ctx) {
|
public LogicalPlan visitQueryNoWith(QueryNoWithContext ctx) {
|
||||||
LogicalPlan plan = plan(ctx.queryTerm());
|
LogicalPlan plan = plan(ctx.queryTerm());
|
||||||
|
|
||||||
|
|
|
@ -765,13 +765,13 @@ class SqlBaseBaseListener implements SqlBaseListener {
|
||||||
*
|
*
|
||||||
* <p>The default implementation does nothing.</p>
|
* <p>The default implementation does nothing.</p>
|
||||||
*/
|
*/
|
||||||
@Override public void enterParam(SqlBaseParser.ParamContext ctx) { }
|
@Override public void enterParamLiteral(SqlBaseParser.ParamLiteralContext ctx) { }
|
||||||
/**
|
/**
|
||||||
* {@inheritDoc}
|
* {@inheritDoc}
|
||||||
*
|
*
|
||||||
* <p>The default implementation does nothing.</p>
|
* <p>The default implementation does nothing.</p>
|
||||||
*/
|
*/
|
||||||
@Override public void exitParam(SqlBaseParser.ParamContext ctx) { }
|
@Override public void exitParamLiteral(SqlBaseParser.ParamLiteralContext ctx) { }
|
||||||
/**
|
/**
|
||||||
* {@inheritDoc}
|
* {@inheritDoc}
|
||||||
*
|
*
|
||||||
|
@ -916,6 +916,18 @@ class SqlBaseBaseListener implements SqlBaseListener {
|
||||||
* <p>The default implementation does nothing.</p>
|
* <p>The default implementation does nothing.</p>
|
||||||
*/
|
*/
|
||||||
@Override public void exitIntegerLiteral(SqlBaseParser.IntegerLiteralContext ctx) { }
|
@Override public void exitIntegerLiteral(SqlBaseParser.IntegerLiteralContext ctx) { }
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*
|
||||||
|
* <p>The default implementation does nothing.</p>
|
||||||
|
*/
|
||||||
|
@Override public void enterString(SqlBaseParser.StringContext ctx) { }
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*
|
||||||
|
* <p>The default implementation does nothing.</p>
|
||||||
|
*/
|
||||||
|
@Override public void exitString(SqlBaseParser.StringContext ctx) { }
|
||||||
/**
|
/**
|
||||||
* {@inheritDoc}
|
* {@inheritDoc}
|
||||||
*
|
*
|
||||||
|
|
|
@ -456,7 +456,7 @@ class SqlBaseBaseVisitor<T> extends AbstractParseTreeVisitor<T> implements SqlBa
|
||||||
* <p>The default implementation returns the result of calling
|
* <p>The default implementation returns the result of calling
|
||||||
* {@link #visitChildren} on {@code ctx}.</p>
|
* {@link #visitChildren} on {@code ctx}.</p>
|
||||||
*/
|
*/
|
||||||
@Override public T visitParam(SqlBaseParser.ParamContext ctx) { return visitChildren(ctx); }
|
@Override public T visitParamLiteral(SqlBaseParser.ParamLiteralContext ctx) { return visitChildren(ctx); }
|
||||||
/**
|
/**
|
||||||
* {@inheritDoc}
|
* {@inheritDoc}
|
||||||
*
|
*
|
||||||
|
@ -541,6 +541,13 @@ class SqlBaseBaseVisitor<T> extends AbstractParseTreeVisitor<T> implements SqlBa
|
||||||
* {@link #visitChildren} on {@code ctx}.</p>
|
* {@link #visitChildren} on {@code ctx}.</p>
|
||||||
*/
|
*/
|
||||||
@Override public T visitIntegerLiteral(SqlBaseParser.IntegerLiteralContext ctx) { return visitChildren(ctx); }
|
@Override public T visitIntegerLiteral(SqlBaseParser.IntegerLiteralContext ctx) { return visitChildren(ctx); }
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*
|
||||||
|
* <p>The default implementation returns the result of calling
|
||||||
|
* {@link #visitChildren} on {@code ctx}.</p>
|
||||||
|
*/
|
||||||
|
@Override public T visitString(SqlBaseParser.StringContext ctx) { return visitChildren(ctx); }
|
||||||
/**
|
/**
|
||||||
* {@inheritDoc}
|
* {@inheritDoc}
|
||||||
*
|
*
|
||||||
|
|
|
@ -719,17 +719,17 @@ interface SqlBaseListener extends ParseTreeListener {
|
||||||
*/
|
*/
|
||||||
void exitStringLiteral(SqlBaseParser.StringLiteralContext ctx);
|
void exitStringLiteral(SqlBaseParser.StringLiteralContext ctx);
|
||||||
/**
|
/**
|
||||||
* Enter a parse tree produced by the {@code param}
|
* Enter a parse tree produced by the {@code paramLiteral}
|
||||||
* labeled alternative in {@link SqlBaseParser#constant}.
|
* labeled alternative in {@link SqlBaseParser#constant}.
|
||||||
* @param ctx the parse tree
|
* @param ctx the parse tree
|
||||||
*/
|
*/
|
||||||
void enterParam(SqlBaseParser.ParamContext ctx);
|
void enterParamLiteral(SqlBaseParser.ParamLiteralContext ctx);
|
||||||
/**
|
/**
|
||||||
* Exit a parse tree produced by the {@code param}
|
* Exit a parse tree produced by the {@code paramLiteral}
|
||||||
* labeled alternative in {@link SqlBaseParser#constant}.
|
* labeled alternative in {@link SqlBaseParser#constant}.
|
||||||
* @param ctx the parse tree
|
* @param ctx the parse tree
|
||||||
*/
|
*/
|
||||||
void exitParam(SqlBaseParser.ParamContext ctx);
|
void exitParamLiteral(SqlBaseParser.ParamLiteralContext ctx);
|
||||||
/**
|
/**
|
||||||
* Enter a parse tree produced by {@link SqlBaseParser#comparisonOperator}.
|
* Enter a parse tree produced by {@link SqlBaseParser#comparisonOperator}.
|
||||||
* @param ctx the parse tree
|
* @param ctx the parse tree
|
||||||
|
@ -864,6 +864,16 @@ interface SqlBaseListener extends ParseTreeListener {
|
||||||
* @param ctx the parse tree
|
* @param ctx the parse tree
|
||||||
*/
|
*/
|
||||||
void exitIntegerLiteral(SqlBaseParser.IntegerLiteralContext ctx);
|
void exitIntegerLiteral(SqlBaseParser.IntegerLiteralContext ctx);
|
||||||
|
/**
|
||||||
|
* Enter a parse tree produced by {@link SqlBaseParser#string}.
|
||||||
|
* @param ctx the parse tree
|
||||||
|
*/
|
||||||
|
void enterString(SqlBaseParser.StringContext ctx);
|
||||||
|
/**
|
||||||
|
* Exit a parse tree produced by {@link SqlBaseParser#string}.
|
||||||
|
* @param ctx the parse tree
|
||||||
|
*/
|
||||||
|
void exitString(SqlBaseParser.StringContext ctx);
|
||||||
/**
|
/**
|
||||||
* Enter a parse tree produced by {@link SqlBaseParser#nonReserved}.
|
* Enter a parse tree produced by {@link SqlBaseParser#nonReserved}.
|
||||||
* @param ctx the parse tree
|
* @param ctx the parse tree
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -431,12 +431,12 @@ interface SqlBaseVisitor<T> extends ParseTreeVisitor<T> {
|
||||||
*/
|
*/
|
||||||
T visitStringLiteral(SqlBaseParser.StringLiteralContext ctx);
|
T visitStringLiteral(SqlBaseParser.StringLiteralContext ctx);
|
||||||
/**
|
/**
|
||||||
* Visit a parse tree produced by the {@code param}
|
* Visit a parse tree produced by the {@code paramLiteral}
|
||||||
* labeled alternative in {@link SqlBaseParser#constant}.
|
* labeled alternative in {@link SqlBaseParser#constant}.
|
||||||
* @param ctx the parse tree
|
* @param ctx the parse tree
|
||||||
* @return the visitor result
|
* @return the visitor result
|
||||||
*/
|
*/
|
||||||
T visitParam(SqlBaseParser.ParamContext ctx);
|
T visitParamLiteral(SqlBaseParser.ParamLiteralContext ctx);
|
||||||
/**
|
/**
|
||||||
* Visit a parse tree produced by {@link SqlBaseParser#comparisonOperator}.
|
* Visit a parse tree produced by {@link SqlBaseParser#comparisonOperator}.
|
||||||
* @param ctx the parse tree
|
* @param ctx the parse tree
|
||||||
|
@ -516,6 +516,12 @@ interface SqlBaseVisitor<T> extends ParseTreeVisitor<T> {
|
||||||
* @return the visitor result
|
* @return the visitor result
|
||||||
*/
|
*/
|
||||||
T visitIntegerLiteral(SqlBaseParser.IntegerLiteralContext ctx);
|
T visitIntegerLiteral(SqlBaseParser.IntegerLiteralContext ctx);
|
||||||
|
/**
|
||||||
|
* Visit a parse tree produced by {@link SqlBaseParser#string}.
|
||||||
|
* @param ctx the parse tree
|
||||||
|
* @return the visitor result
|
||||||
|
*/
|
||||||
|
T visitString(SqlBaseParser.StringContext ctx);
|
||||||
/**
|
/**
|
||||||
* Visit a parse tree produced by {@link SqlBaseParser#nonReserved}.
|
* Visit a parse tree produced by {@link SqlBaseParser#nonReserved}.
|
||||||
* @param ctx the parse tree
|
* @param ctx the parse tree
|
||||||
|
|
|
@ -40,10 +40,6 @@ import java.util.function.Function;
|
||||||
public class SqlParser {
|
public class SqlParser {
|
||||||
private static final Logger log = Loggers.getLogger(SqlParser.class);
|
private static final Logger log = Loggers.getLogger(SqlParser.class);
|
||||||
|
|
||||||
/**
|
|
||||||
* Time zone in which the SQL is parsed. This is attached to functions
|
|
||||||
* that deal with dates and times.
|
|
||||||
*/
|
|
||||||
private final boolean DEBUG = false;
|
private final boolean DEBUG = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -119,7 +115,7 @@ public class SqlParser {
|
||||||
|
|
||||||
parser.addParseListener(parser.new TraceListener());
|
parser.addParseListener(parser.new TraceListener());
|
||||||
|
|
||||||
parser.addErrorListener(new DiagnosticErrorListener() {
|
parser.addErrorListener(new DiagnosticErrorListener(false) {
|
||||||
@Override
|
@Override
|
||||||
public void reportAttemptingFullContext(Parser recognizer, DFA dfa,
|
public void reportAttemptingFullContext(Parser recognizer, DFA dfa,
|
||||||
int startIndex, int stopIndex, BitSet conflictingAlts, ATNConfigSet configs) {}
|
int startIndex, int stopIndex, BitSet conflictingAlts, ATNConfigSet configs) {}
|
||||||
|
|
|
@ -46,7 +46,7 @@ public abstract class DataTypes {
|
||||||
if (value instanceof DateTime) {
|
if (value instanceof DateTime) {
|
||||||
return DataType.DATE;
|
return DataType.DATE;
|
||||||
}
|
}
|
||||||
if (value instanceof String) {
|
if (value instanceof String || value instanceof Character) {
|
||||||
return DataType.KEYWORD;
|
return DataType.KEYWORD;
|
||||||
}
|
}
|
||||||
throw new SqlIllegalArgumentException("No idea what's the DataType for {}", value.getClass());
|
throw new SqlIllegalArgumentException("No idea what's the DataType for {}", value.getClass());
|
||||||
|
|
|
@ -9,9 +9,12 @@ import org.elasticsearch.test.ESTestCase;
|
||||||
import org.elasticsearch.xpack.sql.expression.Expression;
|
import org.elasticsearch.xpack.sql.expression.Expression;
|
||||||
import org.elasticsearch.xpack.sql.expression.regex.Like;
|
import org.elasticsearch.xpack.sql.expression.regex.Like;
|
||||||
import org.elasticsearch.xpack.sql.expression.regex.LikePattern;
|
import org.elasticsearch.xpack.sql.expression.regex.LikePattern;
|
||||||
|
import org.elasticsearch.xpack.sql.plugin.SqlTypedParamValue;
|
||||||
|
import org.elasticsearch.xpack.sql.type.DataType;
|
||||||
|
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
|
||||||
|
import static java.util.Collections.singletonList;
|
||||||
import static org.hamcrest.Matchers.instanceOf;
|
import static org.hamcrest.Matchers.instanceOf;
|
||||||
import static org.hamcrest.Matchers.is;
|
import static org.hamcrest.Matchers.is;
|
||||||
|
|
||||||
|
@ -27,14 +30,20 @@ public class LikeEscapingParsingTests extends ESTestCase {
|
||||||
}
|
}
|
||||||
|
|
||||||
private LikePattern like(String pattern) {
|
private LikePattern like(String pattern) {
|
||||||
Expression exp = parser.createExpression(String.format(Locale.ROOT, "exp LIKE %s", pattern));
|
Expression exp = null;
|
||||||
|
boolean parameterized = randomBoolean();
|
||||||
|
if (parameterized) {
|
||||||
|
exp = parser.createExpression("exp LIKE ?", singletonList(new SqlTypedParamValue(pattern, DataType.KEYWORD)));
|
||||||
|
} else {
|
||||||
|
exp = parser.createExpression(String.format(Locale.ROOT, "exp LIKE '%s'", pattern));
|
||||||
|
}
|
||||||
assertThat(exp, instanceOf(Like.class));
|
assertThat(exp, instanceOf(Like.class));
|
||||||
Like l = (Like) exp;
|
Like l = (Like) exp;
|
||||||
return l.right();
|
return l.right();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testNoEscaping() {
|
public void testNoEscaping() {
|
||||||
LikePattern like = like("'string'");
|
LikePattern like = like("string");
|
||||||
assertThat(like.pattern(), is("string"));
|
assertThat(like.pattern(), is("string"));
|
||||||
assertThat(like.asJavaRegex(), is("^string$"));
|
assertThat(like.asJavaRegex(), is("^string$"));
|
||||||
assertThat(like.asLuceneWildcard(), is("string"));
|
assertThat(like.asLuceneWildcard(), is("string"));
|
||||||
|
|
|
@ -19,30 +19,40 @@ import org.elasticsearch.xpack.sql.session.SqlSession;
|
||||||
import org.elasticsearch.xpack.sql.type.TypesTests;
|
import org.elasticsearch.xpack.sql.type.TypesTests;
|
||||||
import org.joda.time.DateTimeZone;
|
import org.joda.time.DateTimeZone;
|
||||||
|
|
||||||
|
import static java.util.Collections.singletonList;
|
||||||
|
import static org.mockito.Matchers.any;
|
||||||
|
import static org.mockito.Mockito.doAnswer;
|
||||||
import static org.mockito.Mockito.mock;
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
public class SysCatalogTests extends ESTestCase {
|
public class SysCatalogsTests extends ESTestCase {
|
||||||
|
|
||||||
private final SqlParser parser = new SqlParser();
|
private final SqlParser parser = new SqlParser();
|
||||||
|
|
||||||
|
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||||
private Tuple<Command, SqlSession> sql(String sql) {
|
private Tuple<Command, SqlSession> sql(String sql) {
|
||||||
EsIndex test = new EsIndex("test", TypesTests.loadMapping("mapping-multi-field-with-nested.json", true));
|
EsIndex test = new EsIndex("test", TypesTests.loadMapping("mapping-multi-field-with-nested.json", true));
|
||||||
Analyzer analyzer = new Analyzer(new FunctionRegistry(), IndexResolution.valid(test), DateTimeZone.UTC);
|
Analyzer analyzer = new Analyzer(new FunctionRegistry(), IndexResolution.valid(test), DateTimeZone.UTC);
|
||||||
Command cmd = (Command) analyzer.analyze(parser.createStatement(sql), true);
|
Command cmd = (Command) analyzer.analyze(parser.createStatement(sql), true);
|
||||||
|
|
||||||
IndexResolver resolver = mock(IndexResolver.class);
|
IndexResolver resolver = mock(IndexResolver.class);
|
||||||
|
when(resolver.clusterName()).thenReturn("cluster");
|
||||||
|
|
||||||
|
doAnswer(invocation -> {
|
||||||
|
((ActionListener) invocation.getArguments()[2]).onResponse(singletonList(test));
|
||||||
|
return Void.TYPE;
|
||||||
|
}).when(resolver).resolveAsSeparateMappings(any(), any(), any());
|
||||||
|
|
||||||
SqlSession session = new SqlSession(null, null, null, resolver, null, null, null);
|
SqlSession session = new SqlSession(null, null, null, resolver, null, null, null);
|
||||||
return new Tuple<>(cmd, session);
|
return new Tuple<>(cmd, session);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testSysCatalogs() throws Exception {
|
public void testSysCatalogs() throws Exception {
|
||||||
Tuple<Command, SqlSession> sql = sql("SYS TABLE TYPES");
|
Tuple<Command, SqlSession> sql = sql("SYS CATALOGS");
|
||||||
|
|
||||||
sql.v1().execute(sql.v2(), ActionListener.wrap(r -> {
|
sql.v1().execute(sql.v2(), ActionListener.wrap(r -> {
|
||||||
assertEquals(2, r.size());
|
assertEquals(1, r.size());
|
||||||
assertEquals("BASE TABLE", r.column(0));
|
assertEquals("cluster", r.column(0));
|
||||||
r.advanceRow();
|
|
||||||
assertEquals("ALIAS", r.column(0));
|
|
||||||
}, ex -> fail(ex.getMessage())));
|
}, ex -> fail(ex.getMessage())));
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -19,40 +19,30 @@ import org.elasticsearch.xpack.sql.session.SqlSession;
|
||||||
import org.elasticsearch.xpack.sql.type.TypesTests;
|
import org.elasticsearch.xpack.sql.type.TypesTests;
|
||||||
import org.joda.time.DateTimeZone;
|
import org.joda.time.DateTimeZone;
|
||||||
|
|
||||||
import static java.util.Collections.singletonList;
|
|
||||||
import static org.mockito.Matchers.any;
|
|
||||||
import static org.mockito.Mockito.doAnswer;
|
|
||||||
import static org.mockito.Mockito.mock;
|
import static org.mockito.Mockito.mock;
|
||||||
import static org.mockito.Mockito.when;
|
|
||||||
|
|
||||||
public class SysTableTypesTests extends ESTestCase {
|
public class SysTableTypesTests extends ESTestCase {
|
||||||
|
|
||||||
private final SqlParser parser = new SqlParser();
|
private final SqlParser parser = new SqlParser();
|
||||||
|
|
||||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
|
||||||
private Tuple<Command, SqlSession> sql(String sql) {
|
private Tuple<Command, SqlSession> sql(String sql) {
|
||||||
EsIndex test = new EsIndex("test", TypesTests.loadMapping("mapping-multi-field-with-nested.json", true));
|
EsIndex test = new EsIndex("test", TypesTests.loadMapping("mapping-multi-field-with-nested.json", true));
|
||||||
Analyzer analyzer = new Analyzer(new FunctionRegistry(), IndexResolution.valid(test), DateTimeZone.UTC);
|
Analyzer analyzer = new Analyzer(new FunctionRegistry(), IndexResolution.valid(test), DateTimeZone.UTC);
|
||||||
Command cmd = (Command) analyzer.analyze(parser.createStatement(sql), true);
|
Command cmd = (Command) analyzer.analyze(parser.createStatement(sql), true);
|
||||||
|
|
||||||
IndexResolver resolver = mock(IndexResolver.class);
|
IndexResolver resolver = mock(IndexResolver.class);
|
||||||
when(resolver.clusterName()).thenReturn("cluster");
|
|
||||||
|
|
||||||
doAnswer(invocation -> {
|
|
||||||
((ActionListener) invocation.getArguments()[2]).onResponse(singletonList(test));
|
|
||||||
return Void.TYPE;
|
|
||||||
}).when(resolver).resolveAsSeparateMappings(any(), any(), any());
|
|
||||||
|
|
||||||
SqlSession session = new SqlSession(null, null, null, resolver, null, null, null);
|
SqlSession session = new SqlSession(null, null, null, resolver, null, null, null);
|
||||||
return new Tuple<>(cmd, session);
|
return new Tuple<>(cmd, session);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testSysCatalogs() throws Exception {
|
public void testSysCatalogs() throws Exception {
|
||||||
Tuple<Command, SqlSession> sql = sql("SYS CATALOGS");
|
Tuple<Command, SqlSession> sql = sql("SYS TABLE TYPES");
|
||||||
|
|
||||||
sql.v1().execute(sql.v2(), ActionListener.wrap(r -> {
|
sql.v1().execute(sql.v2(), ActionListener.wrap(r -> {
|
||||||
assertEquals(1, r.size());
|
assertEquals(2, r.size());
|
||||||
assertEquals("cluster", r.column(0));
|
assertEquals("BASE TABLE", r.column(0));
|
||||||
|
r.advanceRow();
|
||||||
|
assertEquals("ALIAS", r.column(0));
|
||||||
}, ex -> fail(ex.getMessage())));
|
}, ex -> fail(ex.getMessage())));
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -18,17 +18,21 @@ import org.elasticsearch.xpack.sql.expression.function.FunctionRegistry;
|
||||||
import org.elasticsearch.xpack.sql.parser.ParsingException;
|
import org.elasticsearch.xpack.sql.parser.ParsingException;
|
||||||
import org.elasticsearch.xpack.sql.parser.SqlParser;
|
import org.elasticsearch.xpack.sql.parser.SqlParser;
|
||||||
import org.elasticsearch.xpack.sql.plan.logical.command.Command;
|
import org.elasticsearch.xpack.sql.plan.logical.command.Command;
|
||||||
|
import org.elasticsearch.xpack.sql.plugin.SqlTypedParamValue;
|
||||||
import org.elasticsearch.xpack.sql.session.SchemaRowSet;
|
import org.elasticsearch.xpack.sql.session.SchemaRowSet;
|
||||||
import org.elasticsearch.xpack.sql.session.SqlSession;
|
import org.elasticsearch.xpack.sql.session.SqlSession;
|
||||||
|
import org.elasticsearch.xpack.sql.type.DataTypes;
|
||||||
import org.elasticsearch.xpack.sql.type.EsField;
|
import org.elasticsearch.xpack.sql.type.EsField;
|
||||||
import org.elasticsearch.xpack.sql.type.TypesTests;
|
import org.elasticsearch.xpack.sql.type.TypesTests;
|
||||||
import org.joda.time.DateTimeZone;
|
import org.joda.time.DateTimeZone;
|
||||||
|
|
||||||
import java.util.LinkedHashSet;
|
import java.util.LinkedHashSet;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
import static java.util.Arrays.asList;
|
import static java.util.Arrays.asList;
|
||||||
|
import static java.util.Collections.emptyList;
|
||||||
import static org.elasticsearch.action.ActionListener.wrap;
|
import static org.elasticsearch.action.ActionListener.wrap;
|
||||||
import static org.mockito.Matchers.any;
|
import static org.mockito.Matchers.any;
|
||||||
import static org.mockito.Mockito.doAnswer;
|
import static org.mockito.Mockito.doAnswer;
|
||||||
|
@ -67,6 +71,16 @@ public class SysTablesTests extends ESTestCase {
|
||||||
}, index, alias);
|
}, index, alias);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void testSysTablesPatternParameterized() throws Exception {
|
||||||
|
List<SqlTypedParamValue> params = asList(param("%"));
|
||||||
|
executeCommand("SYS TABLES LIKE ?", params, r -> {
|
||||||
|
assertEquals(2, r.size());
|
||||||
|
assertEquals("test", r.column(2));
|
||||||
|
assertTrue(r.advanceRow());
|
||||||
|
assertEquals("alias", r.column(2));
|
||||||
|
}, index, alias);
|
||||||
|
}
|
||||||
|
|
||||||
public void testSysTablesOnlyAliases() throws Exception {
|
public void testSysTablesOnlyAliases() throws Exception {
|
||||||
executeCommand("SYS TABLES LIKE 'test' TYPE 'ALIAS'", r -> {
|
executeCommand("SYS TABLES LIKE 'test' TYPE 'ALIAS'", r -> {
|
||||||
assertEquals(1, r.size());
|
assertEquals(1, r.size());
|
||||||
|
@ -74,6 +88,14 @@ public class SysTablesTests extends ESTestCase {
|
||||||
}, alias);
|
}, alias);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void testSysTablesOnlyAliasesParameterized() throws Exception {
|
||||||
|
List<SqlTypedParamValue> params = asList(param("ALIAS"));
|
||||||
|
executeCommand("SYS TABLES LIKE 'test' TYPE ?", params, r -> {
|
||||||
|
assertEquals(1, r.size());
|
||||||
|
assertEquals("alias", r.column(2));
|
||||||
|
}, alias);
|
||||||
|
}
|
||||||
|
|
||||||
public void testSysTablesOnlyIndices() throws Exception {
|
public void testSysTablesOnlyIndices() throws Exception {
|
||||||
executeCommand("SYS TABLES LIKE 'test' TYPE 'BASE TABLE'", r -> {
|
executeCommand("SYS TABLES LIKE 'test' TYPE 'BASE TABLE'", r -> {
|
||||||
assertEquals(1, r.size());
|
assertEquals(1, r.size());
|
||||||
|
@ -81,6 +103,13 @@ public class SysTablesTests extends ESTestCase {
|
||||||
}, index);
|
}, index);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void testSysTablesOnlyIndicesParameterized() throws Exception {
|
||||||
|
executeCommand("SYS TABLES LIKE 'test' TYPE ?", asList(param("ALIAS")), r -> {
|
||||||
|
assertEquals(1, r.size());
|
||||||
|
assertEquals("test", r.column(2));
|
||||||
|
}, index);
|
||||||
|
}
|
||||||
|
|
||||||
public void testSysTablesOnlyIndicesAndAliases() throws Exception {
|
public void testSysTablesOnlyIndicesAndAliases() throws Exception {
|
||||||
executeCommand("SYS TABLES LIKE 'test' TYPE 'ALIAS', 'BASE TABLE'", r -> {
|
executeCommand("SYS TABLES LIKE 'test' TYPE 'ALIAS', 'BASE TABLE'", r -> {
|
||||||
assertEquals(2, r.size());
|
assertEquals(2, r.size());
|
||||||
|
@ -90,15 +119,33 @@ public class SysTablesTests extends ESTestCase {
|
||||||
}, index, alias);
|
}, index, alias);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void testSysTablesOnlyIndicesAndAliasesParameterized() throws Exception {
|
||||||
|
List<SqlTypedParamValue> params = asList(param("ALIAS"), param("BASE TABLE"));
|
||||||
|
executeCommand("SYS TABLES LIKE 'test' TYPE ?, ?", params, r -> {
|
||||||
|
assertEquals(2, r.size());
|
||||||
|
assertEquals("test", r.column(2));
|
||||||
|
assertTrue(r.advanceRow());
|
||||||
|
assertEquals("alias", r.column(2));
|
||||||
|
}, index, alias);
|
||||||
|
}
|
||||||
|
|
||||||
public void testSysTablesWithInvalidType() throws Exception {
|
public void testSysTablesWithInvalidType() throws Exception {
|
||||||
ParsingException pe = expectThrows(ParsingException.class, () -> sql("SYS TABLES LIKE 'test' TYPE 'QUE HORA ES'"));
|
ParsingException pe = expectThrows(ParsingException.class, () -> sql("SYS TABLES LIKE 'test' TYPE 'QUE HORA ES'"));
|
||||||
assertEquals("line 1:2: Invalid table type [QUE HORA ES]", pe.getMessage());
|
assertEquals("line 1:2: Invalid table type [QUE HORA ES]", pe.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private SqlTypedParamValue param(Object value) {
|
||||||
|
return new SqlTypedParamValue(value, DataTypes.fromJava(value));
|
||||||
|
}
|
||||||
|
|
||||||
private Tuple<Command, SqlSession> sql(String sql) {
|
private Tuple<Command, SqlSession> sql(String sql) {
|
||||||
|
return sql(sql, emptyList());
|
||||||
|
}
|
||||||
|
|
||||||
|
private Tuple<Command, SqlSession> sql(String sql, List<SqlTypedParamValue> params) {
|
||||||
EsIndex test = new EsIndex("test", mapping);
|
EsIndex test = new EsIndex("test", mapping);
|
||||||
Analyzer analyzer = new Analyzer(new FunctionRegistry(), IndexResolution.valid(test), DateTimeZone.UTC);
|
Analyzer analyzer = new Analyzer(new FunctionRegistry(), IndexResolution.valid(test), DateTimeZone.UTC);
|
||||||
Command cmd = (Command) analyzer.analyze(parser.createStatement(sql), true);
|
Command cmd = (Command) analyzer.analyze(parser.createStatement(sql, params), true);
|
||||||
|
|
||||||
IndexResolver resolver = mock(IndexResolver.class);
|
IndexResolver resolver = mock(IndexResolver.class);
|
||||||
when(resolver.clusterName()).thenReturn("cluster");
|
when(resolver.clusterName()).thenReturn("cluster");
|
||||||
|
@ -107,9 +154,14 @@ public class SysTablesTests extends ESTestCase {
|
||||||
return new Tuple<>(cmd, session);
|
return new Tuple<>(cmd, session);
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
|
||||||
private void executeCommand(String sql, Consumer<SchemaRowSet> consumer, IndexInfo... infos) throws Exception {
|
private void executeCommand(String sql, Consumer<SchemaRowSet> consumer, IndexInfo... infos) throws Exception {
|
||||||
Tuple<Command, SqlSession> tuple = sql(sql);
|
executeCommand(sql, emptyList(), consumer, infos);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||||
|
private void executeCommand(String sql, List<SqlTypedParamValue> params, Consumer<SchemaRowSet> consumer, IndexInfo... infos)
|
||||||
|
throws Exception {
|
||||||
|
Tuple<Command, SqlSession> tuple = sql(sql, params);
|
||||||
|
|
||||||
IndexResolver resolver = tuple.v2().indexResolver();
|
IndexResolver resolver = tuple.v2().indexResolver();
|
||||||
|
|
||||||
|
|
|
@ -87,4 +87,4 @@ public class DatabaseMetaDataTestCase extends JdbcIntegrationTestCase {
|
||||||
assertResultSets(expected, es.getMetaData().getColumns(null, "%", "%", null));
|
assertResultSets(expected, es.getMetaData().getColumns(null, "%", "%", null));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
CREATE TABLE mock (
|
||||||
|
TABLE_SCHEM VARCHAR,
|
||||||
|
TABLE_NAME VARCHAR,
|
||||||
|
TABLE_TYPE VARCHAR,
|
||||||
|
REMARKS VARCHAR,
|
||||||
|
TYPE_CAT VARCHAR,
|
||||||
|
TYPE_SCHEM VARCHAR,
|
||||||
|
TYPE_NAME VARCHAR,
|
||||||
|
SELF_REFERENCING_COL_NAME VARCHAR,
|
||||||
|
REF_GENERATION VARCHAR
|
||||||
|
) AS
|
||||||
|
SELECT '', 'test1', 'BASE TABLE', '', null, null, null, null, null FROM DUAL
|
||||||
|
UNION ALL
|
||||||
|
SELECT '', 'test2', 'BASE TABLE', '', null, null, null, null, null FROM DUAL
|
||||||
|
;
|
Loading…
Reference in New Issue