SQL: add catalog filter to SYS COLUMNS command (elastic/x-pack-elasticsearch#3978)

Add basic support for catalog parameters in SYS COLUMN
Pass an empty string instead of a null inside the prepared statement
Don't use pattern for catalog in getColumns

Original commit: elastic/x-pack-elasticsearch@17e9e851a0
This commit is contained in:
Costin Leau 2018-02-25 05:08:48 +02:00 committed by GitHub
parent b2631f9ac8
commit b8c9c5325c
8 changed files with 704 additions and 663 deletions

View File

@ -759,9 +759,11 @@ class JdbcDatabaseMetaData implements DatabaseMetaData, JdbcWrapper {
@Override
public ResultSet getColumns(String catalog, String schemaPattern, String tableNamePattern, String columnNamePattern)
throws SQLException {
PreparedStatement ps = con.prepareStatement("SYS COLUMNS TABLE LIKE ? LIKE ?");
ps.setString(1, tableNamePattern != null ? tableNamePattern.trim() : "%");
ps.setString(2, columnNamePattern != null ? columnNamePattern.trim() : "%");
PreparedStatement ps = con.prepareStatement("SYS COLUMNS CATALOG ? TABLE LIKE ? LIKE ?");
// TODO: until passing null works, pass an empty string
ps.setString(1, catalog != null ? catalog.trim() : "");
ps.setString(2, tableNamePattern != null ? tableNamePattern.trim() : "%");
ps.setString(3, columnNamePattern != null ? columnNamePattern.trim() : "%");
return ps.executeQuery();
}

View File

@ -59,7 +59,8 @@ statement
| SYS TABLES (CATALOG LIKE? clusterPattern=pattern)?
(LIKE? tablePattern=pattern)?
(TYPE STRING (',' STRING)* )? #sysTables
| SYS COLUMNS (TABLE LIKE? indexPattern=pattern)?
| SYS COLUMNS (CATALOG cluster=STRING)?
(TABLE LIKE? indexPattern=pattern)?
(LIKE? columnPattern=pattern)? #sysColumns
| SYS TYPES #sysTypes
| SYS TABLE TYPES #sysTableTypes
@ -286,7 +287,9 @@ ASC: 'ASC';
BETWEEN: 'BETWEEN';
BY: 'BY';
CAST: 'CAST';
CATALOG: 'CATALOG';CATALOGS: 'CATALOGS';COLUMNS: 'COLUMNS';
CATALOG: 'CATALOG';
CATALOGS: 'CATALOGS';
COLUMNS: 'COLUMNS';
DEBUG: 'DEBUG';
DESC: 'DESC';
DESCRIBE: 'DESCRIBE';

View File

@ -160,7 +160,7 @@ abstract class CommandBuilder extends LogicalPlanBuilder {
@Override
public Object visitSysColumns(SysColumnsContext ctx) {
return new SysColumns(source(ctx), visitPattern(ctx.indexPattern), visitPattern(ctx.columnPattern));
return new SysColumns(source(ctx), string(ctx.cluster), visitPattern(ctx.indexPattern), visitPattern(ctx.columnPattern));
}
@Override

View File

@ -6,6 +6,7 @@
package org.elasticsearch.xpack.sql.plan.logical.command.sys;
import org.elasticsearch.action.ActionListener;
import org.elasticsearch.common.Strings;
import org.elasticsearch.xpack.sql.analysis.index.EsIndex;
import org.elasticsearch.xpack.sql.expression.Attribute;
import org.elasticsearch.xpack.sql.expression.regex.LikePattern;
@ -37,26 +38,20 @@ import static org.elasticsearch.xpack.sql.type.DataType.SHORT;
*/
public class SysColumns extends Command {
private final String catalog;
private final LikePattern indexPattern;
private final LikePattern columnPattern;
public SysColumns(Location location, LikePattern indexPattern, LikePattern columnPattern) {
public SysColumns(Location location, String catalog, LikePattern indexPattern, LikePattern columnPattern) {
super(location);
this.catalog = catalog;
this.indexPattern = indexPattern;
this.columnPattern = columnPattern;
}
public LikePattern indexPattern() {
return indexPattern;
}
public LikePattern columnPattern() {
return columnPattern;
}
@Override
protected NodeInfo<SysColumns> info() {
return NodeInfo.create(this, SysColumns::new, indexPattern, columnPattern);
return NodeInfo.create(this, SysColumns::new, catalog, indexPattern, columnPattern);
}
@Override
@ -91,12 +86,18 @@ public class SysColumns extends Command {
@Override
public void execute(SqlSession session, ActionListener<SchemaRowSet> listener) {
String cluster = session.indexResolver().clusterName();
// bail-out early if the catalog is present but differs
if (Strings.hasText(catalog) && !cluster.equals(catalog)) {
listener.onResponse(Rows.empty(output()));
return;
}
String index = indexPattern != null ? indexPattern.asIndexNameWildcard() : "*";
String regex = indexPattern != null ? indexPattern.asJavaRegex() : null;
Pattern columnMatcher = columnPattern != null ? Pattern.compile(columnPattern.asJavaRegex()) : null;
String cluster = session.indexResolver().clusterName();
session.indexResolver().resolveAsSeparateMappings(index, regex, ActionListener.wrap(esIndices -> {
List<List<?>> rows = new ArrayList<>();
@ -168,7 +169,7 @@ public class SysColumns extends Command {
@Override
public int hashCode() {
return Objects.hash(indexPattern, columnPattern);
return Objects.hash(catalog, indexPattern, columnPattern);
}
@Override
@ -182,7 +183,8 @@ public class SysColumns extends Command {
}
SysColumns other = (SysColumns) obj;
return Objects.equals(indexPattern, other.indexPattern)
return Objects.equals(catalog, other.catalog)
&& Objects.equals(indexPattern, other.indexPattern)
&& Objects.equals(columnPattern, other.columnPattern);
}
}

View File

@ -75,6 +75,24 @@ public class SysParserTests extends ESTestCase {
runSysColumns("SYS COLUMNS");
}
public void testSysColumnEmptyCatalog() throws Exception {
Tuple<Command, SqlSession> sql = sql("SYS COLUMNS CATALOG '' TABLE LIKE '%' LIKE '%'");
sql.v1().execute(sql.v2(), ActionListener.wrap(r -> {
assertEquals(24, r.columnCount());
assertEquals(22, r.size());
}, ex -> fail(ex.getMessage())));
}
public void testSysColsTableOnlyCatalog() throws Exception {
Tuple<Command, SqlSession> sql = sql("SYS COLUMNS CATALOG 'catalog'");
sql.v1().execute(sql.v2(), ActionListener.wrap(r -> {
assertEquals(24, r.columnCount());
assertEquals(0, r.size());
}, ex -> fail(ex.getMessage())));
}
public void testSysColsTableOnlyPattern() throws Exception {
runSysColumns("SYS COLUMNS TABLE LIKE 'test'");
}

View File

@ -286,9 +286,9 @@ public class JdbcSecurityIT extends SqlSecurityTestCase {
createUser("full_access", "cli_or_jdbc_minimal");
expectActionMatchesAdmin(
con -> con.getMetaData().getColumns("%", "%", "%t", "%"),
con -> con.getMetaData().getColumns(null, "%", "%t", "%"),
"full_access",
con -> con.getMetaData().getColumns("%", "%", "%", "%"));
con -> con.getMetaData().getColumns(null, "%", "%", "%"));
}
public void testMetaDataGetColumnsWithNoAccess() throws Exception {
@ -301,18 +301,18 @@ public class JdbcSecurityIT extends SqlSecurityTestCase {
createUser("wrong_access", "read_something_else");
expectActionMatchesAdmin(
con -> con.getMetaData().getColumns("%", "%", "not_created", "%"),
con -> con.getMetaData().getColumns(null, "%", "not_created", "%"),
"wrong_access",
con -> con.getMetaData().getColumns("%", "%", "test", "%"));
con -> con.getMetaData().getColumns(null, "%", "test", "%"));
}
public void testMetaDataGetColumnsSingleFieldGranted() throws Exception {
createUser("only_a", "read_test_a");
expectActionMatchesAdmin(
con -> con.getMetaData().getColumns("%", "%", "test", "a"),
con -> con.getMetaData().getColumns(null, "%", "test", "a"),
"only_a",
con -> con.getMetaData().getColumns("%", "%", "test", "%"));
con -> con.getMetaData().getColumns(null, "%", "test", "%"));
}
public void testMetaDataGetColumnsSingleFieldExcepted() throws Exception {
@ -322,7 +322,7 @@ public class JdbcSecurityIT extends SqlSecurityTestCase {
* both 'a' and 'b' we'll have to roll our own assertion here, but we
* are intentionally much less restrictive then the tests elsewhere. */
try (Connection con = es(userProperties("not_c"))) {
ResultSet result = con.getMetaData().getColumns("%", "%", "test", "%");
ResultSet result = con.getMetaData().getColumns(null, "%", "test", "%");
assertTrue(result.next());
String columnName = result.getString(4);
assertEquals("a", columnName);
@ -337,8 +337,8 @@ public class JdbcSecurityIT extends SqlSecurityTestCase {
createUser("no_3s", "read_test_without_c_3");
expectActionMatchesAdmin(
con -> con.getMetaData().getColumns("%", "%", "test", "%"),
con -> con.getMetaData().getColumns(null, "%", "test", "%"),
"no_3s",
con -> con.getMetaData().getColumns("%", "%", "test", "%"));
con -> con.getMetaData().getColumns(null, "%", "test", "%"));
}
}

View File

@ -84,7 +84,7 @@ public class DatabaseMetaDataTestCase extends JdbcIntegrationTestCase {
h2.createStatement().executeUpdate("RUNSCRIPT FROM 'classpath:/setup_mock_metadata_get_columns.sql'");
ResultSet expected = h2.createStatement().executeQuery("SELECT '" + clusterName() + "' AS TABLE_CAT, * FROM mock");
assertResultSets(expected, es.getMetaData().getColumns("%", "%", "%", null));
assertResultSets(expected, es.getMetaData().getColumns(null, "%", "%", null));
}
}
}