SQL: Add filtering to SYS TYPES (#35852)

Fix #35342
This commit is contained in:
Costin Leau 2018-11-26 11:26:24 +02:00 committed by GitHub
parent 8daa854f90
commit 971299baf4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 919 additions and 751 deletions

View File

@ -62,7 +62,7 @@ statement
| SYS COLUMNS (CATALOG cluster=string)?
(TABLE tableLike=likePattern | tableIdent=tableIdentifier)?
(columnPattern=likePattern)? #sysColumns
| SYS TYPES #sysTypes
| SYS TYPES ((PLUS | MINUS)? type=number)? #sysTypes
| SYS TABLE TYPES #sysTableTypes
;

View File

@ -8,6 +8,7 @@ package org.elasticsearch.xpack.sql.parser;
import org.antlr.v4.runtime.Token;
import org.elasticsearch.common.Booleans;
import org.elasticsearch.xpack.sql.analysis.index.IndexResolver.IndexType;
import org.elasticsearch.xpack.sql.expression.Literal;
import org.elasticsearch.xpack.sql.parser.SqlBaseParser.DebugContext;
import org.elasticsearch.xpack.sql.parser.SqlBaseParser.ExplainContext;
import org.elasticsearch.xpack.sql.parser.SqlBaseParser.ShowColumnsContext;
@ -190,7 +191,13 @@ abstract class CommandBuilder extends LogicalPlanBuilder {
@Override
public SysTypes visitSysTypes(SysTypesContext ctx) {
return new SysTypes(source(ctx));
int type = 0;
if (ctx.type != null) {
Literal value = (Literal) visit(ctx.type);
type = ((Number) value.fold()).intValue();
}
return new SysTypes(source(ctx), Integer.valueOf(type));
}
@Override

View File

@ -26,9 +26,9 @@ import org.elasticsearch.xpack.sql.expression.function.UnresolvedFunction;
import org.elasticsearch.xpack.sql.expression.function.scalar.Cast;
import org.elasticsearch.xpack.sql.expression.literal.Interval;
import org.elasticsearch.xpack.sql.expression.literal.IntervalDayTime;
import org.elasticsearch.xpack.sql.expression.literal.IntervalYearMonth;
import org.elasticsearch.xpack.sql.expression.literal.Intervals;
import org.elasticsearch.xpack.sql.expression.literal.Intervals.TimeUnit;
import org.elasticsearch.xpack.sql.expression.literal.IntervalYearMonth;
import org.elasticsearch.xpack.sql.expression.predicate.Range;
import org.elasticsearch.xpack.sql.expression.predicate.fulltext.MatchQueryPredicate;
import org.elasticsearch.xpack.sql.expression.predicate.fulltext.MultiMatchQueryPredicate;
@ -97,6 +97,7 @@ 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;
import org.elasticsearch.xpack.sql.parser.SqlBaseParser.SysTypesContext;
import org.elasticsearch.xpack.sql.parser.SqlBaseParser.TimeEscapedLiteralContext;
import org.elasticsearch.xpack.sql.parser.SqlBaseParser.TimestampEscapedLiteralContext;
import org.elasticsearch.xpack.sql.proto.SqlTypedParamValue;
@ -653,12 +654,14 @@ abstract class ExpressionBuilder extends IdentifierBuilder {
throw new ParsingException(source(ctx), siae.getMessage());
}
Object val = Long.valueOf(value);
DataType type = DataType.LONG;
// try to downsize to int if possible (since that's the most common type)
if ((int) value == value) {
type = DataType.INTEGER;
val = Integer.valueOf((int) value);
}
return new Literal(source(ctx), value, type);
return new Literal(source(ctx), val, type);
}
@Override
@ -836,6 +839,8 @@ abstract class ExpressionBuilder extends IdentifierBuilder {
} else if (parentCtx instanceof SqlBaseParser.IntervalContext) {
IntervalContext ic = (IntervalContext) parentCtx;
return ic.sign != null && ic.sign.getType() == SqlBaseParser.MINUS;
} else if (parentCtx instanceof SqlBaseParser.SysTypesContext) {
return ((SysTypesContext) parentCtx).MINUS() != null;
}
}
return false;

View File

@ -28,15 +28,23 @@ import static org.elasticsearch.xpack.sql.type.DataType.BOOLEAN;
import static org.elasticsearch.xpack.sql.type.DataType.INTEGER;
import static org.elasticsearch.xpack.sql.type.DataType.SHORT;
/**
* System command designed to be used by JDBC / ODBC for column metadata.
* In JDBC it used for {@link DatabaseMetaData#getTypeInfo()},
* in ODBC for <a href="https://docs.microsoft.com/en-us/sql/odbc/reference/syntax/sqlgettypeinfo-function">SQLGetTypeInfo</a>
*/
public class SysTypes extends Command {
public SysTypes(Location location) {
private final Integer type;
public SysTypes(Location location, int type) {
super(location);
this.type = Integer.valueOf(type);
}
@Override
protected NodeInfo<SysTypes> info() {
return NodeInfo.create(this);
return NodeInfo.create(this, SysTypes::new, type);
}
@Override
@ -66,7 +74,11 @@ public class SysTypes extends Command {
@Override
public final void execute(SqlSession session, ActionListener<SchemaRowSet> listener) {
List<List<?>> rows = Stream.of(DataType.values())
Stream<DataType> values = Stream.of(DataType.values());
if (type.intValue() != 0) {
values = values.filter(t -> type.equals(t.sqlType.getVendorTypeNumber()));
}
List<List<?>> rows = values
// sort by SQL int type (that's what the JDBC/ODBC specs want) followed by name
.sorted(Comparator.comparing((DataType t) -> t.sqlType.getVendorTypeNumber()).thenComparing(DataType::sqlName))
.map(t -> asList(t.esType.toUpperCase(Locale.ROOT),
@ -105,7 +117,7 @@ public class SysTypes extends Command {
@Override
public int hashCode() {
return getClass().hashCode();
return type.hashCode();
}
@Override
@ -118,6 +130,6 @@ public class SysTypes extends Command {
return false;
}
return true;
return type.equals(((SysTypes) obj).type);
}
}

View File

@ -39,14 +39,15 @@ public class SysTableTypesTests extends ESTestCase {
return new Tuple<>(cmd, session);
}
public void testSysCatalogs() throws Exception {
public void testSysTableTypes() throws Exception {
Tuple<Command, SqlSession> sql = sql("SYS TABLE TYPES");
sql.v1().execute(sql.v2(), ActionListener.wrap(r -> {
assertEquals(2, r.size());
assertEquals("ALIAS", r.column(0));
r.advanceRow();
assertTrue(r.advanceRow());
assertEquals("BASE TABLE", r.column(0));
}, ex -> fail(ex.getMessage())));
}
}

View File

@ -0,0 +1,111 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
package org.elasticsearch.xpack.sql.plan.logical.command.sys;
import org.elasticsearch.common.collect.Tuple;
import org.elasticsearch.test.ESTestCase;
import org.elasticsearch.xpack.sql.analysis.analyzer.Analyzer;
import org.elasticsearch.xpack.sql.analysis.index.EsIndex;
import org.elasticsearch.xpack.sql.analysis.index.IndexResolution;
import org.elasticsearch.xpack.sql.analysis.index.IndexResolver;
import org.elasticsearch.xpack.sql.expression.function.FunctionRegistry;
import org.elasticsearch.xpack.sql.parser.SqlParser;
import org.elasticsearch.xpack.sql.plan.logical.command.Command;
import org.elasticsearch.xpack.sql.session.SqlSession;
import org.elasticsearch.xpack.sql.type.DataType;
import org.elasticsearch.xpack.sql.type.TypesTests;
import java.sql.JDBCType;
import java.util.List;
import java.util.TimeZone;
import static java.util.Arrays.asList;
import static org.elasticsearch.action.ActionListener.wrap;
import static org.mockito.Mockito.mock;
public class SysTypesTests extends ESTestCase {
private final SqlParser parser = new SqlParser();
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), TimeZone.getTimeZone("UTC"));
Command cmd = (Command) analyzer.analyze(parser.createStatement(sql), true);
IndexResolver resolver = mock(IndexResolver.class);
SqlSession session = new SqlSession(null, null, null, resolver, null, null, null);
return new Tuple<>(cmd, session);
}
public void testSysTypes() throws Exception {
Command cmd = sql("SYS TYPES").v1();
List<String> names = asList("BYTE", "LONG", "BINARY", "NULL", "INTEGER", "SHORT", "HALF_FLOAT", "SCALED_FLOAT", "FLOAT", "DOUBLE",
"KEYWORD", "TEXT", "IP", "BOOLEAN", "DATE",
"INTERVAL_YEAR", "INTERVAL_MONTH", "INTERVAL_DAY", "INTERVAL_HOUR", "INTERVAL_MINUTE", "INTERVAL_SECOND",
"INTERVAL_YEAR_TO_MONTH", "INTERVAL_DAY_TO_HOUR", "INTERVAL_DAY_TO_MINUTE", "INTERVAL_DAY_TO_SECOND",
"INTERVAL_HOUR_TO_MINUTE", "INTERVAL_HOUR_TO_SECOND", "INTERVAL_MINUTE_TO_SECOND",
"UNSUPPORTED", "OBJECT", "NESTED");
cmd.execute(null, wrap(r -> {
assertEquals(19, r.columnCount());
assertEquals(DataType.values().length, r.size());
assertFalse(r.schema().types().contains(DataType.NULL));
// test numeric as signed
assertFalse(r.column(9, Boolean.class));
// make sure precision is returned as boolean (not int)
assertFalse(r.column(10, Boolean.class));
// no auto-increment
assertFalse(r.column(11, Boolean.class));
for (int i = 0; i < r.size(); i++) {
assertEquals(names.get(i), r.column(0));
r.advanceRow();
}
}, ex -> fail(ex.getMessage())));
}
public void testSysTypesDefaultFiltering() throws Exception {
Command cmd = sql("SYS TYPES 0").v1();
cmd.execute(null, wrap(r -> {
assertEquals(DataType.values().length, r.size());
}, ex -> fail(ex.getMessage())));
}
public void testSysTypesPositiveFiltering() throws Exception {
// boolean = 16
Command cmd = sql("SYS TYPES " + JDBCType.BOOLEAN.getVendorTypeNumber()).v1();
cmd.execute(null, wrap(r -> {
assertEquals(1, r.size());
assertEquals("BOOLEAN", r.column(0));
}, ex -> fail(ex.getMessage())));
}
public void testSysTypesNegativeFiltering() throws Exception {
Command cmd = sql("SYS TYPES " + JDBCType.TINYINT.getVendorTypeNumber()).v1();
cmd.execute(null, wrap(r -> {
assertEquals(1, r.size());
assertEquals("BYTE", r.column(0));
}, ex -> fail(ex.getMessage())));
}
public void testSysTypesMultipleMatches() throws Exception {
Command cmd = sql("SYS TYPES " + JDBCType.VARCHAR.getVendorTypeNumber()).v1();
cmd.execute(null, wrap(r -> {
assertEquals(3, r.size());
assertEquals("KEYWORD", r.column(0));
assertTrue(r.advanceRow());
assertEquals("TEXT", r.column(0));
assertTrue(r.advanceRow());
assertEquals("IP", r.column(0));
}, ex -> fail(ex.getMessage())));
}
}