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:
Costin Leau 2018-04-13 18:16:47 +03:00 committed by GitHub
parent 0d83edbca5
commit fc5e1631f1
20 changed files with 1051 additions and 893 deletions

View File

@ -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();
}

View File

@ -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 + "]";
}
}

View File

@ -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

View File

@ -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.
*/

View File

@ -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));
}

View File

@ -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);
}
}

View File

@ -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());

View File

@ -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}
*

View File

@ -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}
*

View File

@ -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

View File

@ -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

View File

@ -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) {}

View File

@ -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());

View File

@ -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"));

View File

@ -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())));
}
}

View File

@ -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())));
}
}

View File

@ -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();

View File

@ -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
;