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;
|
||||
|
||||
import org.elasticsearch.xpack.sql.client.shared.Version;
|
||||
import org.elasticsearch.xpack.sql.jdbc.JdbcSQLException;
|
||||
import org.elasticsearch.xpack.sql.jdbc.debug.Debug;
|
||||
import org.elasticsearch.xpack.sql.client.shared.Version;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.sql.Connection;
|
||||
|
@ -16,11 +16,8 @@ import java.sql.DriverPropertyInfo;
|
|||
import java.sql.SQLException;
|
||||
import java.sql.SQLFeatureNotSupportedException;
|
||||
import java.util.Properties;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import static org.elasticsearch.xpack.sql.client.shared.ConnectionConfiguration.CONNECT_TIMEOUT;
|
||||
|
||||
public class JdbcDriver implements java.sql.Driver {
|
||||
|
||||
private static final JdbcDriver INSTANCE = new JdbcDriver();
|
||||
|
@ -67,6 +64,7 @@ public class JdbcDriver implements java.sql.Driver {
|
|||
//
|
||||
// Jdbc 4.0
|
||||
//
|
||||
@Override
|
||||
public Connection connect(String url, Properties props) throws SQLException {
|
||||
if (url == null) {
|
||||
throw new JdbcSQLException("Non-null url required");
|
||||
|
@ -116,6 +114,7 @@ public class JdbcDriver implements java.sql.Driver {
|
|||
// Jdbc 4.1
|
||||
//
|
||||
|
||||
@Override
|
||||
public Logger getParentLogger() throws 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.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;
|
||||
|
@ -75,8 +74,12 @@ public class SqlTypedParamValue implements ToXContentObject, Writeable {
|
|||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (o == null || getClass() != o.getClass()) {
|
||||
return false;
|
||||
}
|
||||
SqlTypedParamValue that = (SqlTypedParamValue) o;
|
||||
return Objects.equals(value, that.value) &&
|
||||
dataType == that.dataType;
|
||||
|
@ -84,7 +87,11 @@ public class SqlTypedParamValue implements ToXContentObject, Writeable {
|
|||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
|
||||
return Objects.hash(value, dataType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.valueOf(value) + "[" + dataType + "]";
|
||||
}
|
||||
}
|
|
@ -58,8 +58,8 @@ statement
|
|||
| SYS CATALOGS #sysCatalogs
|
||||
| SYS TABLES (CATALOG LIKE? clusterPattern=pattern)?
|
||||
(LIKE? tablePattern=pattern)?
|
||||
(TYPE STRING (',' STRING)* )? #sysTables
|
||||
| SYS COLUMNS (CATALOG cluster=(STRING | PARAM))?
|
||||
(TYPE string (',' string)* )? #sysTables
|
||||
| SYS COLUMNS (CATALOG cluster=string)?
|
||||
(TABLE LIKE? indexPattern=pattern)?
|
||||
(LIKE? columnPattern=pattern)? #sysColumns
|
||||
| SYS TYPES #sysTypes
|
||||
|
@ -158,9 +158,9 @@ expression
|
|||
booleanExpression
|
||||
: NOT booleanExpression #logicalNot
|
||||
| EXISTS '(' query ')' #exists
|
||||
| QUERY '(' queryString=STRING (',' options=STRING)* ')' #stringQuery
|
||||
| MATCH '(' singleField=qualifiedName ',' queryString=STRING (',' options=STRING)* ')' #matchQuery
|
||||
| MATCH '(' multiFields=STRING ',' queryString=STRING (',' options=STRING)* ')' #multiMatchQuery
|
||||
| QUERY '(' queryString=string (',' options=string)* ')' #stringQuery
|
||||
| MATCH '(' singleField=qualifiedName ',' queryString=string (',' options=string)* ')' #matchQuery
|
||||
| MATCH '(' multiFields=string ',' queryString=string (',' options=string)* ')' #multiMatchQuery
|
||||
| predicated #booleanDefault
|
||||
| left=booleanExpression operator=AND right=booleanExpression #logicalBinary
|
||||
| left=booleanExpression operator=OR right=booleanExpression #logicalBinary
|
||||
|
@ -180,13 +180,12 @@ predicate
|
|||
| NOT? kind=IN '(' expression (',' expression)* ')'
|
||||
| NOT? kind=IN '(' query ')'
|
||||
| NOT? kind=LIKE pattern
|
||||
| NOT? kind=RLIKE regex=STRING
|
||||
| NOT? kind=RLIKE regex=string
|
||||
| IS NOT? kind=NULL
|
||||
;
|
||||
|
||||
pattern
|
||||
: value=STRING (ESCAPE escape=STRING)?
|
||||
| PARAM
|
||||
: value=string (ESCAPE escape=string)?
|
||||
;
|
||||
|
||||
valueExpression
|
||||
|
@ -216,7 +215,7 @@ constant
|
|||
| number #numericLiteral
|
||||
| booleanValue #booleanLiteral
|
||||
| STRING+ #stringLiteral
|
||||
| PARAM #param
|
||||
| PARAM #paramLiteral
|
||||
;
|
||||
|
||||
comparisonOperator
|
||||
|
@ -261,6 +260,11 @@ number
|
|||
| INTEGER_VALUE #integerLiteral
|
||||
;
|
||||
|
||||
string
|
||||
: PARAM
|
||||
| STRING
|
||||
;
|
||||
|
||||
// http://developer.mimer.se/validator/sql-reserved-words.tml
|
||||
nonReserved
|
||||
: ANALYZE | ANALYZED
|
||||
|
|
|
@ -92,13 +92,6 @@ abstract class AbstractBuilder extends SqlBaseBaseVisitor<Object> {
|
|||
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.
|
||||
*/
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
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;
|
||||
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.ShowSchemasContext;
|
||||
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.SysColumnsContext;
|
||||
import org.elasticsearch.xpack.sql.parser.SqlBaseParser.SysTableTypesContext;
|
||||
|
@ -43,6 +43,7 @@ import java.util.Locale;
|
|||
import java.util.Map;
|
||||
|
||||
abstract class CommandBuilder extends LogicalPlanBuilder {
|
||||
|
||||
protected CommandBuilder(Map<Token, SqlTypedParamValue> params) {
|
||||
super(params);
|
||||
}
|
||||
|
@ -146,7 +147,7 @@ abstract class CommandBuilder extends LogicalPlanBuilder {
|
|||
@Override
|
||||
public SysTables visitSysTables(SysTablesContext ctx) {
|
||||
List<IndexType> types = new ArrayList<>();
|
||||
for (TerminalNode string : ctx.STRING()) {
|
||||
for (StringContext string : ctx.string()) {
|
||||
String value = string(string);
|
||||
if (value != null) {
|
||||
IndexType type = IndexType.from(value);
|
||||
|
@ -163,7 +164,7 @@ abstract class CommandBuilder extends LogicalPlanBuilder {
|
|||
@Override
|
||||
public Object visitSysColumns(SysColumnsContext 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
|
||||
|
@ -171,6 +172,7 @@ abstract class CommandBuilder extends LogicalPlanBuilder {
|
|||
return new SysTypes(source(ctx));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object visitSysTableTypes(SysTableTypesContext 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.NullLiteralContext;
|
||||
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.PatternContext;
|
||||
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.SingleExpressionContext;
|
||||
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.StringQueryContext;
|
||||
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;
|
||||
|
||||
abstract class ExpressionBuilder extends IdentifierBuilder {
|
||||
|
||||
private final 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) {
|
||||
ExpressionBuilder(Map<Token, SqlTypedParamValue> params) {
|
||||
this.params = params;
|
||||
}
|
||||
|
||||
|
@ -215,11 +213,6 @@ 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) {
|
||||
|
@ -452,16 +445,21 @@ abstract class ExpressionBuilder extends IdentifierBuilder {
|
|||
}
|
||||
|
||||
@Override
|
||||
public Object visitParam(SqlBaseParser.ParamContext ctx) {
|
||||
Token token = ctx.PARAM().getSymbol();
|
||||
return paramValue(token, source(ctx));
|
||||
public Object visitDecimalLiteral(DecimalLiteralContext ctx) {
|
||||
return new Literal(source(ctx), new BigDecimal(ctx.getText()).doubleValue(), DataType.DOUBLE);
|
||||
}
|
||||
|
||||
private Object paramValue(Token token, Location loc) {
|
||||
if (params.containsKey(token) == false) {
|
||||
throw new ParsingException(loc, "Unexpected parameter");
|
||||
@Override
|
||||
public Object visitIntegerLiteral(IntegerLiteralContext ctx) {
|
||||
BigDecimal bigD = new BigDecimal(ctx.getText());
|
||||
// TODO: this can be improved to use the smallest type available
|
||||
return new Literal(source(ctx), bigD.longValueExact(), DataType.INTEGER);
|
||||
}
|
||||
SqlTypedParamValue param = params.get(token);
|
||||
|
||||
@Override
|
||||
public Object visitParamLiteral(ParamLiteralContext ctx) {
|
||||
SqlTypedParamValue param = param(ctx.PARAM());
|
||||
Location loc = source(ctx);
|
||||
if (param.value == null) {
|
||||
// no conversion is required for null values
|
||||
return new Literal(loc, null, param.dataType);
|
||||
|
@ -470,8 +468,8 @@ abstract class ExpressionBuilder extends IdentifierBuilder {
|
|||
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);
|
||||
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
|
||||
|
@ -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) {
|
||||
if (token == null) {
|
||||
String string(StringContext ctx) {
|
||||
if (ctx == null) {
|
||||
return null;
|
||||
}
|
||||
if (token.getType() == SqlBaseLexer.PARAM) {
|
||||
SqlTypedParamValue param = param(ctx.PARAM());
|
||||
if (param != null) {
|
||||
return param.value != null ? param.value.toString() : null;
|
||||
} else {
|
||||
return unquoteString(ctx.getText());
|
||||
}
|
||||
}
|
||||
|
||||
private SqlTypedParamValue param(TerminalNode node) {
|
||||
if (node == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Token token = node.getSymbol();
|
||||
|
||||
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());
|
||||
throw new ParsingException(source(node), "Unexpected parameter");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object visitDecimalLiteral(DecimalLiteralContext ctx) {
|
||||
return new Literal(source(ctx), new BigDecimal(ctx.getText()).doubleValue(), DataType.DOUBLE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object visitIntegerLiteral(IntegerLiteralContext ctx) {
|
||||
BigDecimal bigD = new BigDecimal(ctx.getText());
|
||||
// TODO: this can be improved to use the smallest type available
|
||||
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;
|
||||
|
||||
abstract class LogicalPlanBuilder extends ExpressionBuilder {
|
||||
|
||||
protected LogicalPlanBuilder(Map<Token, SqlTypedParamValue> params) {
|
||||
super(params);
|
||||
}
|
||||
|
@ -80,6 +81,7 @@ abstract class LogicalPlanBuilder extends ExpressionBuilder {
|
|||
return new SubQueryAlias(source(ctx), plan(ctx.queryNoWith()), ctx.name.getText());
|
||||
}
|
||||
|
||||
@Override
|
||||
public LogicalPlan visitQueryNoWith(QueryNoWithContext ctx) {
|
||||
LogicalPlan plan = plan(ctx.queryTerm());
|
||||
|
||||
|
|
|
@ -765,13 +765,13 @@ class SqlBaseBaseListener implements SqlBaseListener {
|
|||
*
|
||||
* <p>The default implementation does nothing.</p>
|
||||
*/
|
||||
@Override public void enterParam(SqlBaseParser.ParamContext ctx) { }
|
||||
@Override public void enterParamLiteral(SqlBaseParser.ParamLiteralContext ctx) { }
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* <p>The default implementation does nothing.</p>
|
||||
*/
|
||||
@Override public void exitParam(SqlBaseParser.ParamContext ctx) { }
|
||||
@Override public void exitParamLiteral(SqlBaseParser.ParamLiteralContext ctx) { }
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
|
@ -916,6 +916,18 @@ class SqlBaseBaseListener implements SqlBaseListener {
|
|||
* <p>The default implementation does nothing.</p>
|
||||
*/
|
||||
@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}
|
||||
*
|
||||
|
|
|
@ -456,7 +456,7 @@ class SqlBaseBaseVisitor<T> extends AbstractParseTreeVisitor<T> implements SqlBa
|
|||
* <p>The default implementation returns the result of calling
|
||||
* {@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}
|
||||
*
|
||||
|
@ -541,6 +541,13 @@ class SqlBaseBaseVisitor<T> extends AbstractParseTreeVisitor<T> implements SqlBa
|
|||
* {@link #visitChildren} on {@code ctx}.</p>
|
||||
*/
|
||||
@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}
|
||||
*
|
||||
|
|
|
@ -719,17 +719,17 @@ interface SqlBaseListener extends ParseTreeListener {
|
|||
*/
|
||||
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}.
|
||||
* @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}.
|
||||
* @param ctx the parse tree
|
||||
*/
|
||||
void exitParam(SqlBaseParser.ParamContext ctx);
|
||||
void exitParamLiteral(SqlBaseParser.ParamLiteralContext ctx);
|
||||
/**
|
||||
* Enter a parse tree produced by {@link SqlBaseParser#comparisonOperator}.
|
||||
* @param ctx the parse tree
|
||||
|
@ -864,6 +864,16 @@ interface SqlBaseListener extends ParseTreeListener {
|
|||
* @param ctx the parse tree
|
||||
*/
|
||||
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}.
|
||||
* @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);
|
||||
/**
|
||||
* Visit a parse tree produced by the {@code param}
|
||||
* Visit a parse tree produced by the {@code paramLiteral}
|
||||
* labeled alternative in {@link SqlBaseParser#constant}.
|
||||
* @param ctx the parse tree
|
||||
* @return the visitor result
|
||||
*/
|
||||
T visitParam(SqlBaseParser.ParamContext ctx);
|
||||
T visitParamLiteral(SqlBaseParser.ParamLiteralContext ctx);
|
||||
/**
|
||||
* Visit a parse tree produced by {@link SqlBaseParser#comparisonOperator}.
|
||||
* @param ctx the parse tree
|
||||
|
@ -516,6 +516,12 @@ interface SqlBaseVisitor<T> extends ParseTreeVisitor<T> {
|
|||
* @return the visitor result
|
||||
*/
|
||||
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}.
|
||||
* @param ctx the parse tree
|
||||
|
|
|
@ -40,10 +40,6 @@ import java.util.function.Function;
|
|||
public class SqlParser {
|
||||
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;
|
||||
|
||||
/**
|
||||
|
@ -119,7 +115,7 @@ public class SqlParser {
|
|||
|
||||
parser.addParseListener(parser.new TraceListener());
|
||||
|
||||
parser.addErrorListener(new DiagnosticErrorListener() {
|
||||
parser.addErrorListener(new DiagnosticErrorListener(false) {
|
||||
@Override
|
||||
public void reportAttemptingFullContext(Parser recognizer, DFA dfa,
|
||||
int startIndex, int stopIndex, BitSet conflictingAlts, ATNConfigSet configs) {}
|
||||
|
|
|
@ -46,7 +46,7 @@ public abstract class DataTypes {
|
|||
if (value instanceof DateTime) {
|
||||
return DataType.DATE;
|
||||
}
|
||||
if (value instanceof String) {
|
||||
if (value instanceof String || value instanceof Character) {
|
||||
return DataType.KEYWORD;
|
||||
}
|
||||
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.regex.Like;
|
||||
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 static java.util.Collections.singletonList;
|
||||
import static org.hamcrest.Matchers.instanceOf;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
|
||||
|
@ -27,14 +30,20 @@ public class LikeEscapingParsingTests extends ESTestCase {
|
|||
}
|
||||
|
||||
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));
|
||||
Like l = (Like) exp;
|
||||
return l.right();
|
||||
}
|
||||
|
||||
public void testNoEscaping() {
|
||||
LikePattern like = like("'string'");
|
||||
LikePattern like = like("string");
|
||||
assertThat(like.pattern(), is("string"));
|
||||
assertThat(like.asJavaRegex(), 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.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.when;
|
||||
|
||||
public class SysCatalogTests extends ESTestCase {
|
||||
public class SysCatalogsTests extends ESTestCase {
|
||||
|
||||
private final SqlParser parser = new SqlParser();
|
||||
|
||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||
private Tuple<Command, SqlSession> sql(String sql) {
|
||||
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);
|
||||
Command cmd = (Command) analyzer.analyze(parser.createStatement(sql), true);
|
||||
|
||||
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);
|
||||
return new Tuple<>(cmd, session);
|
||||
}
|
||||
|
||||
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 -> {
|
||||
assertEquals(2, r.size());
|
||||
assertEquals("BASE TABLE", r.column(0));
|
||||
r.advanceRow();
|
||||
assertEquals("ALIAS", r.column(0));
|
||||
assertEquals(1, r.size());
|
||||
assertEquals("cluster", r.column(0));
|
||||
}, ex -> fail(ex.getMessage())));
|
||||
}
|
||||
}
|
|
@ -19,40 +19,30 @@ import org.elasticsearch.xpack.sql.session.SqlSession;
|
|||
import org.elasticsearch.xpack.sql.type.TypesTests;
|
||||
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.when;
|
||||
|
||||
public class SysTableTypesTests extends ESTestCase {
|
||||
|
||||
private final SqlParser parser = new SqlParser();
|
||||
|
||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||
private Tuple<Command, SqlSession> sql(String sql) {
|
||||
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);
|
||||
Command cmd = (Command) analyzer.analyze(parser.createStatement(sql), true);
|
||||
|
||||
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);
|
||||
return new Tuple<>(cmd, session);
|
||||
}
|
||||
|
||||
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 -> {
|
||||
assertEquals(1, r.size());
|
||||
assertEquals("cluster", r.column(0));
|
||||
assertEquals(2, r.size());
|
||||
assertEquals("BASE TABLE", r.column(0));
|
||||
r.advanceRow();
|
||||
assertEquals("ALIAS", r.column(0));
|
||||
}, 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.SqlParser;
|
||||
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.SqlSession;
|
||||
import org.elasticsearch.xpack.sql.type.DataTypes;
|
||||
import org.elasticsearch.xpack.sql.type.EsField;
|
||||
import org.elasticsearch.xpack.sql.type.TypesTests;
|
||||
import org.joda.time.DateTimeZone;
|
||||
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import static java.util.Arrays.asList;
|
||||
import static java.util.Collections.emptyList;
|
||||
import static org.elasticsearch.action.ActionListener.wrap;
|
||||
import static org.mockito.Matchers.any;
|
||||
import static org.mockito.Mockito.doAnswer;
|
||||
|
@ -67,6 +71,16 @@ public class SysTablesTests extends ESTestCase {
|
|||
}, 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 {
|
||||
executeCommand("SYS TABLES LIKE 'test' TYPE 'ALIAS'", r -> {
|
||||
assertEquals(1, r.size());
|
||||
|
@ -74,6 +88,14 @@ public class SysTablesTests extends ESTestCase {
|
|||
}, 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 {
|
||||
executeCommand("SYS TABLES LIKE 'test' TYPE 'BASE TABLE'", r -> {
|
||||
assertEquals(1, r.size());
|
||||
|
@ -81,6 +103,13 @@ public class SysTablesTests extends ESTestCase {
|
|||
}, 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 {
|
||||
executeCommand("SYS TABLES LIKE 'test' TYPE 'ALIAS', 'BASE TABLE'", r -> {
|
||||
assertEquals(2, r.size());
|
||||
|
@ -90,15 +119,33 @@ public class SysTablesTests extends ESTestCase {
|
|||
}, 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 {
|
||||
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());
|
||||
}
|
||||
|
||||
private SqlTypedParamValue param(Object value) {
|
||||
return new SqlTypedParamValue(value, DataTypes.fromJava(value));
|
||||
}
|
||||
|
||||
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);
|
||||
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);
|
||||
when(resolver.clusterName()).thenReturn("cluster");
|
||||
|
@ -107,9 +154,14 @@ public class SysTablesTests extends ESTestCase {
|
|||
return new Tuple<>(cmd, session);
|
||||
}
|
||||
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
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();
|
||||
|
||||
|
|
|
@ -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