SQL: ignore UNSUPPORTED fields for JDBC and ODBC modes in 'SYS COLUMNS' (#39518)

* SYS COLUMNS will skip UNSUPPORTED field types in ODBC and JDBC, as well.
NESTED and OBJECT types were already skipped in ODBC mode, now they are
skipped in JDBC mode, as well.

(cherry picked from commit 9e0df64b2d36c9069dfa506570468f0522c86417)
This commit is contained in:
Andrei Stefan 2019-03-01 15:23:15 +02:00 committed by Andrei Stefan
parent 894ecb244d
commit ba44f28340
5 changed files with 255 additions and 31 deletions

View File

@ -7,8 +7,8 @@
=== Nested fields in `SYS COLUMNS` and `DESCRIBE TABLE` === Nested fields in `SYS COLUMNS` and `DESCRIBE TABLE`
{es} has a special type of relationship fields called `nested` fields. In {es-sql} they can be used by referencing their inner {es} has a special type of relationship fields called `nested` fields. In {es-sql} they can be used by referencing their inner
sub-fields. Even though `SYS COLUMNS` and `DESCRIBE TABLE` will still display them as having the type `NESTED`, they cannot sub-fields. Even though `SYS COLUMNS` in non-driver mode (in the CLI and in REST calls) and `DESCRIBE TABLE` will still display
be used in a query. One can only reference its sub-fields in the form: them as having the type `NESTED`, they cannot be used in a query. One can only reference its sub-fields in the form:
[source, sql] [source, sql]
-------------------------------------------------- --------------------------------------------------

View File

@ -96,8 +96,8 @@ public class SysColumns extends Command {
@Override @Override
public void execute(SqlSession session, ActionListener<SchemaRowSet> listener) { public void execute(SqlSession session, ActionListener<SchemaRowSet> listener) {
boolean isOdbcClient = session.configuration().mode() == Mode.ODBC; Mode mode = session.configuration().mode();
List<Attribute> output = output(isOdbcClient); List<Attribute> output = output(mode == Mode.ODBC);
String cluster = session.indexResolver().clusterName(); String cluster = session.indexResolver().clusterName();
// bail-out early if the catalog is present but differs // bail-out early if the catalog is present but differs
@ -114,7 +114,7 @@ public class SysColumns extends Command {
session.indexResolver().resolveAsSeparateMappings(idx, regex, ActionListener.wrap(esIndices -> { session.indexResolver().resolveAsSeparateMappings(idx, regex, ActionListener.wrap(esIndices -> {
List<List<?>> rows = new ArrayList<>(); List<List<?>> rows = new ArrayList<>();
for (EsIndex esIndex : esIndices) { for (EsIndex esIndex : esIndices) {
fillInRows(cluster, esIndex.name(), esIndex.mapping(), null, rows, columnMatcher, isOdbcClient); fillInRows(cluster, esIndex.name(), esIndex.mapping(), null, rows, columnMatcher, mode);
} }
listener.onResponse(Rows.of(output, rows)); listener.onResponse(Rows.of(output, rows));
@ -122,8 +122,9 @@ public class SysColumns extends Command {
} }
static void fillInRows(String clusterName, String indexName, Map<String, EsField> mapping, String prefix, List<List<?>> rows, static void fillInRows(String clusterName, String indexName, Map<String, EsField> mapping, String prefix, List<List<?>> rows,
Pattern columnMatcher, boolean isOdbcClient) { Pattern columnMatcher, Mode mode) {
int pos = 0; int pos = 0;
boolean isOdbcClient = mode == Mode.ODBC;
for (Map.Entry<String, EsField> entry : mapping.entrySet()) { for (Map.Entry<String, EsField> entry : mapping.entrySet()) {
pos++; // JDBC is 1-based so we start with 1 here pos++; // JDBC is 1-based so we start with 1 here
@ -132,9 +133,8 @@ public class SysColumns extends Command {
EsField field = entry.getValue(); EsField field = entry.getValue();
DataType type = field.getDataType(); DataType type = field.getDataType();
// skip the nested and object types only for ODBC // skip the nested, object and unsupported types for JDBC and ODBC
// https://github.com/elastic/elasticsearch/issues/35376 if (type.isPrimitive() || false == Mode.isDriver(mode)) {
if (type.isPrimitive() || !isOdbcClient) {
if (columnMatcher == null || columnMatcher.matcher(name).matches()) { if (columnMatcher == null || columnMatcher.matcher(name).matches()) {
rows.add(asList(clusterName, rows.add(asList(clusterName,
// schema is not supported // schema is not supported
@ -174,7 +174,7 @@ public class SysColumns extends Command {
} }
} }
if (field.getProperties() != null) { if (field.getProperties() != null) {
fillInRows(clusterName, indexName, field.getProperties(), name, rows, columnMatcher, isOdbcClient); fillInRows(clusterName, indexName, field.getProperties(), name, rows, columnMatcher, mode);
} }
} }
} }

View File

@ -226,7 +226,7 @@ public enum DataType {
} }
public boolean isPrimitive() { public boolean isPrimitive() {
return this != OBJECT && this != NESTED; return this != OBJECT && this != NESTED && this != UNSUPPORTED;
} }
public boolean isDateBased() { public boolean isDateBased() {

View File

@ -6,6 +6,7 @@
package org.elasticsearch.xpack.sql.plan.logical.command.sys; package org.elasticsearch.xpack.sql.plan.logical.command.sys;
import org.elasticsearch.test.ESTestCase; import org.elasticsearch.test.ESTestCase;
import org.elasticsearch.xpack.sql.proto.Mode;
import org.elasticsearch.xpack.sql.type.TypesTests; import org.elasticsearch.xpack.sql.type.TypesTests;
import java.sql.Types; import java.sql.Types;
@ -16,8 +17,9 @@ public class SysColumnsTests extends ESTestCase {
public void testSysColumns() { public void testSysColumns() {
List<List<?>> rows = new ArrayList<>(); List<List<?>> rows = new ArrayList<>();
SysColumns.fillInRows("test", "index", TypesTests.loadMapping("mapping-multi-field-variation.json", true), null, rows, null, false); SysColumns.fillInRows("test", "index", TypesTests.loadMapping("mapping-multi-field-variation.json", true), null, rows, null,
assertEquals(16, rows.size()); randomValueOtherThanMany(Mode::isDriver, () -> randomFrom(Mode.values())));
assertEquals(17, rows.size());
assertEquals(24, rows.get(0).size()); assertEquals(24, rows.get(0).size());
List<?> row = rows.get(0); List<?> row = rows.get(0);
@ -37,6 +39,12 @@ public class SysColumnsTests extends ESTestCase {
assertEquals(Types.VARCHAR, sqlType(row)); assertEquals(Types.VARCHAR, sqlType(row));
assertEquals(null, radix(row)); assertEquals(null, radix(row));
assertEquals(Integer.MAX_VALUE, bufferLength(row)); assertEquals(Integer.MAX_VALUE, bufferLength(row));
row = rows.get(3);
assertEquals("keyword", name(row));
assertEquals(Types.VARCHAR, sqlType(row));
assertEquals(null, radix(row));
assertEquals(Integer.MAX_VALUE, bufferLength(row));
row = rows.get(4); row = rows.get(4);
assertEquals("date", name(row)); assertEquals("date", name(row));
@ -45,23 +53,84 @@ public class SysColumnsTests extends ESTestCase {
assertEquals(24, precision(row)); assertEquals(24, precision(row));
assertEquals(8, bufferLength(row)); assertEquals(8, bufferLength(row));
row = rows.get(5);
assertEquals("unsupported", name(row));
assertEquals(Types.OTHER, sqlType(row));
assertEquals(null, radix(row));
assertEquals(0, bufferLength(row));
row = rows.get(6);
assertEquals("some", name(row));
assertEquals(Types.STRUCT, sqlType(row));
assertEquals(null, radix(row));
assertEquals(-1, bufferLength(row));
row = rows.get(7); row = rows.get(7);
assertEquals("some.dotted", name(row)); assertEquals("some.dotted", name(row));
assertEquals(Types.STRUCT, sqlType(row)); assertEquals(Types.STRUCT, sqlType(row));
assertEquals(null, radix(row)); assertEquals(null, radix(row));
assertEquals(-1, bufferLength(row)); assertEquals(-1, bufferLength(row));
row = rows.get(8);
assertEquals("some.dotted.field", name(row));
assertEquals(Types.VARCHAR, sqlType(row));
assertEquals(null, radix(row));
assertEquals(Integer.MAX_VALUE, bufferLength(row));
row = rows.get(9);
assertEquals("some.string", name(row));
assertEquals(Types.VARCHAR, sqlType(row));
assertEquals(null, radix(row));
assertEquals(Integer.MAX_VALUE, bufferLength(row));
row = rows.get(10);
assertEquals("some.string.normalized", name(row));
assertEquals(Types.VARCHAR, sqlType(row));
assertEquals(null, radix(row));
assertEquals(Integer.MAX_VALUE, bufferLength(row));
row = rows.get(11);
assertEquals("some.string.typical", name(row));
assertEquals(Types.VARCHAR, sqlType(row));
assertEquals(null, radix(row));
assertEquals(Integer.MAX_VALUE, bufferLength(row));
row = rows.get(12);
assertEquals("some.ambiguous", name(row));
assertEquals(Types.VARCHAR, sqlType(row));
assertEquals(null, radix(row));
assertEquals(Integer.MAX_VALUE, bufferLength(row));
row = rows.get(13);
assertEquals("some.ambiguous.one", name(row));
assertEquals(Types.VARCHAR, sqlType(row));
assertEquals(null, radix(row));
assertEquals(Integer.MAX_VALUE, bufferLength(row));
row = rows.get(14);
assertEquals("some.ambiguous.two", name(row));
assertEquals(Types.VARCHAR, sqlType(row));
assertEquals(null, radix(row));
assertEquals(Integer.MAX_VALUE, bufferLength(row));
row = rows.get(15); row = rows.get(15);
assertEquals("some.ambiguous.normalized", name(row)); assertEquals("some.ambiguous.normalized", name(row));
assertEquals(Types.VARCHAR, sqlType(row)); assertEquals(Types.VARCHAR, sqlType(row));
assertEquals(null, radix(row)); assertEquals(null, radix(row));
assertEquals(Integer.MAX_VALUE, bufferLength(row)); assertEquals(Integer.MAX_VALUE, bufferLength(row));
row = rows.get(16);
assertEquals("foo_type", name(row));
assertEquals(Types.OTHER, sqlType(row));
assertEquals(null, radix(row));
assertEquals(0, bufferLength(row));
} }
public void testSysColumnsInOdbcMode() { public void testSysColumnsInOdbcMode() {
List<List<?>> rows = new ArrayList<>(); List<List<?>> rows = new ArrayList<>();
SysColumns.fillInRows("test", "index", TypesTests.loadMapping("mapping-multi-field-variation.json", true), null, rows, null, true); SysColumns.fillInRows("test", "index", TypesTests.loadMapping("mapping-multi-field-variation.json", true), null, rows, null,
assertEquals(14, rows.size()); Mode.ODBC);
assertEquals(13, rows.size());
assertEquals(24, rows.get(0).size()); assertEquals(24, rows.get(0).size());
List<?> row = rows.get(0); List<?> row = rows.get(0);
@ -112,17 +181,6 @@ public class SysColumnsTests extends ESTestCase {
assertEquals(Short.class, sqlDataTypeSub(row).getClass()); assertEquals(Short.class, sqlDataTypeSub(row).getClass());
row = rows.get(5); row = rows.get(5);
assertEquals("unsupported", name(row));
assertEquals((short) Types.OTHER, sqlType(row));
assertEquals(null, radix(row));
assertEquals(0, precision(row));
assertEquals(0, bufferLength(row));
assertNull(decimalPrecision(row));
assertEquals(Short.class, nullable(row).getClass());
assertEquals(Short.class, sqlDataType(row).getClass());
assertEquals(Short.class, sqlDataTypeSub(row).getClass());
row = rows.get(6);
assertEquals("some.dotted.field", name(row)); assertEquals("some.dotted.field", name(row));
assertEquals((short) Types.VARCHAR, sqlType(row)); assertEquals((short) Types.VARCHAR, sqlType(row));
assertEquals(null, radix(row)); assertEquals(null, radix(row));
@ -132,7 +190,7 @@ public class SysColumnsTests extends ESTestCase {
assertEquals(Short.class, sqlDataType(row).getClass()); assertEquals(Short.class, sqlDataType(row).getClass());
assertEquals(Short.class, sqlDataTypeSub(row).getClass()); assertEquals(Short.class, sqlDataTypeSub(row).getClass());
row = rows.get(7); row = rows.get(6);
assertEquals("some.string", name(row)); assertEquals("some.string", name(row));
assertEquals((short) Types.VARCHAR, sqlType(row)); assertEquals((short) Types.VARCHAR, sqlType(row));
assertEquals(null, radix(row)); assertEquals(null, radix(row));
@ -142,7 +200,7 @@ public class SysColumnsTests extends ESTestCase {
assertEquals(Short.class, sqlDataType(row).getClass()); assertEquals(Short.class, sqlDataType(row).getClass());
assertEquals(Short.class, sqlDataTypeSub(row).getClass()); assertEquals(Short.class, sqlDataTypeSub(row).getClass());
row = rows.get(8); row = rows.get(7);
assertEquals("some.string.normalized", name(row)); assertEquals("some.string.normalized", name(row));
assertEquals((short) Types.VARCHAR, sqlType(row)); assertEquals((short) Types.VARCHAR, sqlType(row));
assertEquals(null, radix(row)); assertEquals(null, radix(row));
@ -152,7 +210,7 @@ public class SysColumnsTests extends ESTestCase {
assertEquals(Short.class, sqlDataType(row).getClass()); assertEquals(Short.class, sqlDataType(row).getClass());
assertEquals(Short.class, sqlDataTypeSub(row).getClass()); assertEquals(Short.class, sqlDataTypeSub(row).getClass());
row = rows.get(9); row = rows.get(8);
assertEquals("some.string.typical", name(row)); assertEquals("some.string.typical", name(row));
assertEquals((short) Types.VARCHAR, sqlType(row)); assertEquals((short) Types.VARCHAR, sqlType(row));
assertEquals(null, radix(row)); assertEquals(null, radix(row));
@ -161,8 +219,38 @@ public class SysColumnsTests extends ESTestCase {
assertEquals(Short.class, nullable(row).getClass()); assertEquals(Short.class, nullable(row).getClass());
assertEquals(Short.class, sqlDataType(row).getClass()); assertEquals(Short.class, sqlDataType(row).getClass());
assertEquals(Short.class, sqlDataTypeSub(row).getClass()); assertEquals(Short.class, sqlDataTypeSub(row).getClass());
row = rows.get(9);
assertEquals("some.ambiguous", name(row));
assertEquals((short) Types.VARCHAR, sqlType(row));
assertEquals(null, radix(row));
assertEquals(Integer.MAX_VALUE, bufferLength(row));
assertNull(decimalPrecision(row));
assertEquals(Short.class, nullable(row).getClass());
assertEquals(Short.class, sqlDataType(row).getClass());
assertEquals(Short.class, sqlDataTypeSub(row).getClass());
row = rows.get(10);
assertEquals("some.ambiguous.one", name(row));
assertEquals((short) Types.VARCHAR, sqlType(row));
assertEquals(null, radix(row));
assertEquals(Integer.MAX_VALUE, bufferLength(row));
assertNull(decimalPrecision(row));
assertEquals(Short.class, nullable(row).getClass());
assertEquals(Short.class, sqlDataType(row).getClass());
assertEquals(Short.class, sqlDataTypeSub(row).getClass());
row = rows.get(11);
assertEquals("some.ambiguous.two", name(row));
assertEquals((short) Types.VARCHAR, sqlType(row));
assertEquals(null, radix(row));
assertEquals(Integer.MAX_VALUE, bufferLength(row));
assertNull(decimalPrecision(row));
assertEquals(Short.class, nullable(row).getClass());
assertEquals(Short.class, sqlDataType(row).getClass());
assertEquals(Short.class, sqlDataTypeSub(row).getClass());
row = rows.get(13); row = rows.get(12);
assertEquals("some.ambiguous.normalized", name(row)); assertEquals("some.ambiguous.normalized", name(row));
assertEquals((short) Types.VARCHAR, sqlType(row)); assertEquals((short) Types.VARCHAR, sqlType(row));
assertEquals(null, radix(row)); assertEquals(null, radix(row));
@ -172,6 +260,141 @@ public class SysColumnsTests extends ESTestCase {
assertEquals(Short.class, sqlDataType(row).getClass()); assertEquals(Short.class, sqlDataType(row).getClass());
assertEquals(Short.class, sqlDataTypeSub(row).getClass()); assertEquals(Short.class, sqlDataTypeSub(row).getClass());
} }
public void testSysColumnsInJdbcMode() {
List<List<?>> rows = new ArrayList<>();
SysColumns.fillInRows("test", "index", TypesTests.loadMapping("mapping-multi-field-variation.json", true), null, rows, null,
Mode.JDBC);
assertEquals(13, rows.size());
assertEquals(24, rows.get(0).size());
List<?> row = rows.get(0);
assertEquals("bool", name(row));
assertEquals(Types.BOOLEAN, sqlType(row));
assertEquals(null, radix(row));
assertEquals(1, bufferLength(row));
row = rows.get(1);
assertEquals("int", name(row));
assertEquals(Types.INTEGER, sqlType(row));
assertEquals(Integer.class, radix(row).getClass());
assertEquals(4, bufferLength(row));
assertNull(decimalPrecision(row));
assertEquals(Integer.class, nullable(row).getClass());
assertEquals(Integer.class, sqlDataType(row).getClass());
assertEquals(Integer.class, sqlDataTypeSub(row).getClass());
row = rows.get(2);
assertEquals("text", name(row));
assertEquals(Types.VARCHAR, sqlType(row));
assertEquals(null, radix(row));
assertEquals(Integer.MAX_VALUE, bufferLength(row));
assertNull(decimalPrecision(row));
assertEquals(Integer.class, nullable(row).getClass());
assertEquals(Integer.class, sqlDataType(row).getClass());
assertEquals(Integer.class, sqlDataTypeSub(row).getClass());
row = rows.get(3);
assertEquals("keyword", name(row));
assertEquals(Types.VARCHAR, sqlType(row));
assertEquals(null, radix(row));
assertEquals(Integer.MAX_VALUE, bufferLength(row));
assertNull(decimalPrecision(row));
assertEquals(Integer.class, nullable(row).getClass());
assertEquals(Integer.class, sqlDataType(row).getClass());
assertEquals(Integer.class, sqlDataTypeSub(row).getClass());
row = rows.get(4);
assertEquals("date", name(row));
assertEquals(Types.TIMESTAMP, sqlType(row));
assertEquals(null, radix(row));
assertEquals(24, precision(row));
assertEquals(8, bufferLength(row));
assertNull(decimalPrecision(row));
assertEquals(Integer.class, nullable(row).getClass());
assertEquals(Integer.class, sqlDataType(row).getClass());
assertEquals(Integer.class, sqlDataTypeSub(row).getClass());
row = rows.get(5);
assertEquals("some.dotted.field", name(row));
assertEquals(Types.VARCHAR, sqlType(row));
assertEquals(null, radix(row));
assertEquals(Integer.MAX_VALUE, bufferLength(row));
assertNull(decimalPrecision(row));
assertEquals(Integer.class, nullable(row).getClass());
assertEquals(Integer.class, sqlDataType(row).getClass());
assertEquals(Integer.class, sqlDataTypeSub(row).getClass());
row = rows.get(6);
assertEquals("some.string", name(row));
assertEquals(Types.VARCHAR, sqlType(row));
assertEquals(null, radix(row));
assertEquals(Integer.MAX_VALUE, bufferLength(row));
assertNull(decimalPrecision(row));
assertEquals(Integer.class, nullable(row).getClass());
assertEquals(Integer.class, sqlDataType(row).getClass());
assertEquals(Integer.class, sqlDataTypeSub(row).getClass());
row = rows.get(7);
assertEquals("some.string.normalized", name(row));
assertEquals(Types.VARCHAR, sqlType(row));
assertEquals(null, radix(row));
assertEquals(Integer.MAX_VALUE, bufferLength(row));
assertNull(decimalPrecision(row));
assertEquals(Integer.class, nullable(row).getClass());
assertEquals(Integer.class, sqlDataType(row).getClass());
assertEquals(Integer.class, sqlDataTypeSub(row).getClass());
row = rows.get(8);
assertEquals("some.string.typical", name(row));
assertEquals(Types.VARCHAR, sqlType(row));
assertEquals(null, radix(row));
assertEquals(Integer.MAX_VALUE, bufferLength(row));
assertNull(decimalPrecision(row));
assertEquals(Integer.class, nullable(row).getClass());
assertEquals(Integer.class, sqlDataType(row).getClass());
assertEquals(Integer.class, sqlDataTypeSub(row).getClass());
row = rows.get(9);
assertEquals("some.ambiguous", name(row));
assertEquals(Types.VARCHAR, sqlType(row));
assertEquals(null, radix(row));
assertEquals(Integer.MAX_VALUE, bufferLength(row));
assertNull(decimalPrecision(row));
assertEquals(Integer.class, nullable(row).getClass());
assertEquals(Integer.class, sqlDataType(row).getClass());
assertEquals(Integer.class, sqlDataTypeSub(row).getClass());
row = rows.get(10);
assertEquals("some.ambiguous.one", name(row));
assertEquals(Types.VARCHAR, sqlType(row));
assertEquals(null, radix(row));
assertEquals(Integer.MAX_VALUE, bufferLength(row));
assertNull(decimalPrecision(row));
assertEquals(Integer.class, nullable(row).getClass());
assertEquals(Integer.class, sqlDataType(row).getClass());
assertEquals(Integer.class, sqlDataTypeSub(row).getClass());
row = rows.get(11);
assertEquals("some.ambiguous.two", name(row));
assertEquals(Types.VARCHAR, sqlType(row));
assertEquals(null, radix(row));
assertEquals(Integer.MAX_VALUE, bufferLength(row));
assertNull(decimalPrecision(row));
assertEquals(Integer.class, nullable(row).getClass());
assertEquals(Integer.class, sqlDataType(row).getClass());
assertEquals(Integer.class, sqlDataTypeSub(row).getClass());
row = rows.get(12);
assertEquals("some.ambiguous.normalized", name(row));
assertEquals(Types.VARCHAR, sqlType(row));
assertEquals(null, radix(row));
assertEquals(Integer.MAX_VALUE, bufferLength(row));
assertNull(decimalPrecision(row));
assertEquals(Integer.class, nullable(row).getClass());
assertEquals(Integer.class, sqlDataType(row).getClass());
assertEquals(Integer.class, sqlDataTypeSub(row).getClass());
}
private static Object name(List<?> list) { private static Object name(List<?> list) {
return list.get(3); return list.get(3);

View File

@ -43,6 +43,7 @@
} }
} }
} }
} },
"foo_type" : { "type" : "foo" }
} }
} }