SQL: Polish behavior of SYS TABLES command (#40535)
SYS TABLES meta command has been improved to better adhere to the ODBC spec in particular with regards to the handling of enumerations (and the differences between '%', null and ''(empty string)) Fix #40348 (cherry picked from commit e3070615000228c283d17ce8d182b44f1450a5d5)
This commit is contained in:
parent
de5f924daa
commit
5485efa2af
|
@ -257,8 +257,7 @@ class JdbcDatabaseMetaData implements DatabaseMetaData, JdbcWrapper {
|
|||
|
||||
@Override
|
||||
public boolean supportsConvert() throws SQLException {
|
||||
//TODO: add Convert
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -774,14 +773,14 @@ class JdbcDatabaseMetaData implements DatabaseMetaData, JdbcWrapper {
|
|||
@Override
|
||||
public ResultSet getCatalogs() throws SQLException {
|
||||
// TABLE_CAT is the first column
|
||||
Object[][] data = queryColumn(con, "SYS TABLES CATALOG LIKE '%'", 1);
|
||||
Object[][] data = queryColumn(con, "SYS TABLES CATALOG LIKE '%' LIKE ''", 1);
|
||||
return memorySet(con.cfg, columnInfo("", "TABLE_CAT"), data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResultSet getTableTypes() throws SQLException {
|
||||
// TABLE_TYPE (4)
|
||||
Object[][] data = queryColumn(con, "SYS TABLES TYPE '%'", 4);
|
||||
Object[][] data = queryColumn(con, "SYS TABLES CATALOG LIKE '' LIKE '' TYPE '%'", 4);
|
||||
return memorySet(con.cfg, columnInfo("", "TABLE_TYPE"), data);
|
||||
}
|
||||
|
||||
|
|
|
@ -149,9 +149,8 @@ abstract class CommandBuilder extends LogicalPlanBuilder {
|
|||
if (value != null) {
|
||||
// check special ODBC wildcard case
|
||||
if (value.equals(StringUtils.SQL_WILDCARD) && ctx.string().size() == 1) {
|
||||
// convert % to enumeration
|
||||
// https://docs.microsoft.com/en-us/sql/odbc/reference/develop-app/value-list-arguments?view=ssdt-18vs2017
|
||||
types.addAll(IndexType.VALID);
|
||||
// treat % as null
|
||||
// https://docs.microsoft.com/en-us/sql/odbc/reference/develop-app/value-list-arguments
|
||||
}
|
||||
// special case for legacy apps (like msquery) that always asks for 'TABLE'
|
||||
// which we manually map to all concrete tables supported
|
||||
|
|
|
@ -14,9 +14,8 @@ import org.elasticsearch.xpack.sql.plan.logical.command.Command;
|
|||
import org.elasticsearch.xpack.sql.session.Rows;
|
||||
import org.elasticsearch.xpack.sql.session.SchemaRowSet;
|
||||
import org.elasticsearch.xpack.sql.session.SqlSession;
|
||||
import org.elasticsearch.xpack.sql.tree.Source;
|
||||
import org.elasticsearch.xpack.sql.tree.NodeInfo;
|
||||
import org.elasticsearch.xpack.sql.util.CollectionUtils;
|
||||
import org.elasticsearch.xpack.sql.tree.Source;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
|
@ -77,8 +76,11 @@ public class SysTables extends Command {
|
|||
// namely one param specified with '%', everything else empty string
|
||||
// https://docs.microsoft.com/en-us/sql/odbc/reference/syntax/sqltables-function?view=ssdt-18vs2017#comments
|
||||
|
||||
if (clusterPattern != null && clusterPattern.pattern().equals(SQL_WILDCARD)) {
|
||||
if ((pattern == null || pattern.pattern().isEmpty()) && CollectionUtils.isEmpty(types)) {
|
||||
// catalog enumeration
|
||||
if (clusterPattern == null || clusterPattern.pattern().equals(SQL_WILDCARD)) {
|
||||
// enumerate only if pattern is "" and no types are specified (types is null)
|
||||
if (pattern != null && pattern.pattern().isEmpty() && index == null
|
||||
&& types == null) {
|
||||
Object[] enumeration = new Object[10];
|
||||
// send only the cluster, everything else null
|
||||
enumeration[0] = cluster;
|
||||
|
@ -87,12 +89,15 @@ public class SysTables extends Command {
|
|||
}
|
||||
}
|
||||
|
||||
// if no types were specified (the parser takes care of the % case)
|
||||
if (IndexType.VALID.equals(types)) {
|
||||
if ((clusterPattern == null || clusterPattern.pattern().isEmpty())
|
||||
&& (pattern == null || pattern.pattern().isEmpty())) {
|
||||
// enumerate types
|
||||
// if no types are specified (the parser takes care of the % case)
|
||||
if (types == null) {
|
||||
// empty string for catalog
|
||||
if (clusterPattern != null && clusterPattern.pattern().isEmpty()
|
||||
// empty string for table like and no index specified
|
||||
&& pattern != null && pattern.pattern().isEmpty() && index == null) {
|
||||
List<List<?>> values = new ArrayList<>();
|
||||
// send only the types, everything else null
|
||||
// send only the types, everything else is made of empty strings
|
||||
for (IndexType type : IndexType.VALID) {
|
||||
Object[] enumeration = new Object[10];
|
||||
enumeration[3] = type.toSql();
|
||||
|
@ -105,7 +110,7 @@ public class SysTables extends Command {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
// no enumeration pattern found, list actual tables
|
||||
String cRegex = clusterPattern != null ? clusterPattern.asJavaRegex() : null;
|
||||
|
||||
// if the catalog doesn't match, don't return any results
|
||||
|
|
|
@ -51,20 +51,60 @@ public class SysTablesTests extends ESTestCase {
|
|||
private final IndexInfo index = new IndexInfo("test", IndexType.INDEX);
|
||||
private final IndexInfo alias = new IndexInfo("alias", IndexType.ALIAS);
|
||||
|
||||
public void testSysTablesEnumerateCatalog() throws Exception {
|
||||
executeCommand("SYS TABLES CATALOG LIKE '%'", r -> {
|
||||
//
|
||||
// catalog enumeration
|
||||
//
|
||||
public void testSysTablesCatalogEnumeration() throws Exception {
|
||||
executeCommand("SYS TABLES CATALOG LIKE '%' LIKE ''", r -> {
|
||||
assertEquals(1, r.size());
|
||||
assertEquals(CLUSTER_NAME, r.column(0));
|
||||
});
|
||||
// everything else should be null
|
||||
for (int i = 1; i < 10; i++) {
|
||||
assertNull(r.column(i));
|
||||
}
|
||||
}, index);
|
||||
}
|
||||
|
||||
public void testSysTablesEnumerateTypes() throws Exception {
|
||||
executeCommand("SYS TABLES TYPE '%'", r -> {
|
||||
//
|
||||
// table types enumeration
|
||||
//
|
||||
public void testSysTablesTypesEnumerationWoString() throws Exception {
|
||||
executeCommand("SYS TABLES CATALOG LIKE '' LIKE '' ", r -> {
|
||||
assertEquals(2, r.size());
|
||||
assertEquals("BASE TABLE", r.column(3));
|
||||
assertTrue(r.advanceRow());
|
||||
assertEquals("VIEW", r.column(3));
|
||||
});
|
||||
}, new IndexInfo[0]);
|
||||
}
|
||||
|
||||
public void testSysTablesEnumerateTypes() throws Exception {
|
||||
executeCommand("SYS TABLES CATALOG LIKE '' LIKE '' TYPE '%'", r -> {
|
||||
assertEquals(2, r.size());
|
||||
assertEquals("BASE TABLE", r.column(3));
|
||||
assertTrue(r.advanceRow());
|
||||
assertEquals("VIEW", r.column(3));
|
||||
}, alias, index);
|
||||
}
|
||||
|
||||
public void testSysTablesTypesEnumeration() throws Exception {
|
||||
executeCommand("SYS TABLES CATALOG LIKE '' LIKE '' TYPE '%'", r -> {
|
||||
assertEquals(2, r.size());
|
||||
|
||||
Iterator<IndexType> it = IndexType.VALID.stream().sorted(Comparator.comparing(IndexType::toSql)).iterator();
|
||||
|
||||
for (int t = 0; t < r.size(); t++) {
|
||||
assertEquals(it.next().toSql(), r.column(3));
|
||||
|
||||
// everything else should be null
|
||||
for (int i = 0; i < 10; i++) {
|
||||
if (i != 3) {
|
||||
assertNull(r.column(i));
|
||||
}
|
||||
}
|
||||
|
||||
r.advanceRow();
|
||||
}
|
||||
}, new IndexInfo[0]);
|
||||
}
|
||||
|
||||
public void testSysTablesDifferentCatalog() throws Exception {
|
||||
|
@ -77,17 +117,42 @@ public class SysTablesTests extends ESTestCase {
|
|||
public void testSysTablesNoTypes() throws Exception {
|
||||
executeCommand("SYS TABLES", r -> {
|
||||
assertEquals(2, r.size());
|
||||
assertEquals("test", r.column(2));
|
||||
assertEquals("BASE TABLE", r.column(3));
|
||||
assertTrue(r.advanceRow());
|
||||
assertEquals("alias", r.column(2));
|
||||
assertEquals("VIEW", r.column(3));
|
||||
}, index, alias);
|
||||
}
|
||||
|
||||
public void testSysTablesWithLegacyTypes() throws Exception {
|
||||
executeCommand("SYS TABLES TYPE 'TABLE', 'ALIAS'", r -> {
|
||||
assertEquals(2, r.size());
|
||||
assertEquals("test", r.column(2));
|
||||
assertEquals("TABLE", r.column(3));
|
||||
assertTrue(r.advanceRow());
|
||||
assertEquals("alias", r.column(2));
|
||||
assertEquals("VIEW", r.column(3));
|
||||
}, index, alias);
|
||||
}
|
||||
|
||||
public void testSysTablesWithProperTypes() throws Exception {
|
||||
executeCommand("SYS TABLES TYPE 'BASE TABLE', 'ALIAS'", r -> {
|
||||
assertEquals(2, r.size());
|
||||
assertEquals("test", r.column(2));
|
||||
assertEquals("BASE TABLE", r.column(3));
|
||||
assertTrue(r.advanceRow());
|
||||
assertEquals("alias", r.column(2));
|
||||
assertEquals("VIEW", r.column(3));
|
||||
}, index, alias);
|
||||
}
|
||||
|
||||
public void testSysTablesPattern() throws Exception {
|
||||
executeCommand("SYS TABLES LIKE '%'", r -> {
|
||||
assertEquals("test", r.column(2));
|
||||
assertTrue(r.advanceRow());
|
||||
assertEquals(2, r.size());
|
||||
assertEquals("test", r.column(2));
|
||||
assertEquals("BASE TABLE", r.column(3));
|
||||
assertTrue(r.advanceRow());
|
||||
assertEquals("alias", r.column(2));
|
||||
}, index, alias);
|
||||
}
|
||||
|
@ -130,7 +195,18 @@ public class SysTablesTests extends ESTestCase {
|
|||
assertEquals("test", r.column(2));
|
||||
assertEquals("TABLE", r.column(3));
|
||||
}, index);
|
||||
}
|
||||
|
||||
public void testSysTablesNoPatternWithTypesSpecifiedInLegacyMode() throws Exception {
|
||||
executeCommand("SYS TABLES TYPE 'TABLE','VIEW'", r -> {
|
||||
assertEquals(2, r.size());
|
||||
assertEquals("test", r.column(2));
|
||||
assertEquals("TABLE", r.column(3));
|
||||
assertTrue(r.advanceRow());
|
||||
assertEquals("alias", r.column(2));
|
||||
assertEquals("VIEW", r.column(3));
|
||||
|
||||
}, index, alias);
|
||||
}
|
||||
|
||||
public void testSysTablesOnlyIndicesLegacyModeParameterized() throws Exception {
|
||||
|
@ -192,43 +268,6 @@ public class SysTablesTests extends ESTestCase {
|
|||
}, new IndexInfo[0]);
|
||||
}
|
||||
|
||||
public void testSysTablesCatalogEnumeration() throws Exception {
|
||||
executeCommand("SYS TABLES CATALOG LIKE '%' LIKE ''", r -> {
|
||||
assertEquals(1, r.size());
|
||||
assertEquals(CLUSTER_NAME, r.column(0));
|
||||
// everything else should be null
|
||||
for (int i = 1; i < 10; i++) {
|
||||
assertNull(r.column(i));
|
||||
}
|
||||
}, new IndexInfo[0]);
|
||||
}
|
||||
|
||||
public void testSysTablesTypesEnumeration() throws Exception {
|
||||
executeCommand("SYS TABLES CATALOG LIKE '' LIKE '' TYPE '%'", r -> {
|
||||
assertEquals(2, r.size());
|
||||
|
||||
Iterator<IndexType> it = IndexType.VALID.stream().sorted(Comparator.comparing(IndexType::toSql)).iterator();
|
||||
|
||||
for (int t = 0; t < r.size(); t++) {
|
||||
assertEquals(it.next().toSql(), r.column(3));
|
||||
|
||||
// everything else should be null
|
||||
for (int i = 0; i < 10; i++) {
|
||||
if (i != 3) {
|
||||
assertNull(r.column(i));
|
||||
}
|
||||
}
|
||||
|
||||
r.advanceRow();
|
||||
}
|
||||
}, new IndexInfo[0]);
|
||||
}
|
||||
|
||||
public void testSysTablesTypesEnumerationWoString() throws Exception {
|
||||
executeCommand("SYS TABLES CATALOG LIKE '' LIKE '' ", r -> {
|
||||
assertEquals(0, r.size());
|
||||
}, new IndexInfo[0]);
|
||||
}
|
||||
|
||||
private SqlTypedParamValue param(Object value) {
|
||||
return new SqlTypedParamValue(DataTypes.fromJava(value).typeName, value);
|
||||
|
|
Loading…
Reference in New Issue