DRILL-736: Fix DESCRIBE issues when the selected table exists in more than one schema.
Project: http://git-wip-us.apache.org/repos/asf/incubator-drill/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-drill/commit/299d1bf1 Tree: http://git-wip-us.apache.org/repos/asf/incubator-drill/tree/299d1bf1 Diff: http://git-wip-us.apache.org/repos/asf/incubator-drill/diff/299d1bf1 Branch: refs/heads/master Commit: 299d1bf1c053da7bcf9902e7bf1ad870b9337c61 Parents: 0c3bad9 Author: vkorukanti <[email protected]> Authored: Mon Jun 9 15:48:55 2014 -0700 Committer: vkorukanti <[email protected]> Committed: Mon Jun 9 15:48:55 2014 -0700 ---------------------------------------------------------------------- .../sql/handlers/AbstractSqlHandler.java | 4 + .../sql/handlers/DescribeTableHandler.java | 81 ++++++++++++-------- .../apache/drill/jdbc/test/TestMetadataDDL.java | 43 ++++++++++- 3 files changed, 93 insertions(+), 35 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-drill/blob/299d1bf1/exec/java-exec/src/main/java/org/apache/drill/exec/planner/sql/handlers/AbstractSqlHandler.java ---------------------------------------------------------------------- diff --git a/exec/java-exec/src/main/java/org/apache/drill/exec/planner/sql/handlers/AbstractSqlHandler.java b/exec/java-exec/src/main/java/org/apache/drill/exec/planner/sql/handlers/AbstractSqlHandler.java index 4a3584c..3504505 100644 --- a/exec/java-exec/src/main/java/org/apache/drill/exec/planner/sql/handlers/AbstractSqlHandler.java +++ b/exec/java-exec/src/main/java/org/apache/drill/exec/planner/sql/handlers/AbstractSqlHandler.java @@ -79,6 +79,10 @@ public abstract class AbstractSqlHandler { throw new Exception(String.format("Invalid schema path '%s'.", Joiner.on(".").join(schemaPath))); } + public static boolean isRootSchema(SchemaPlus schema) { + return schema.getParentSchema() == null; + } + private static SchemaPlus searchSchemaTree(SchemaPlus schema, List<String> schemaPath) { for (String schemaName : schemaPath) { schema = schema.getSubSchema(schemaName); http://git-wip-us.apache.org/repos/asf/incubator-drill/blob/299d1bf1/exec/java-exec/src/main/java/org/apache/drill/exec/planner/sql/handlers/DescribeTableHandler.java ---------------------------------------------------------------------- diff --git a/exec/java-exec/src/main/java/org/apache/drill/exec/planner/sql/handlers/DescribeTableHandler.java b/exec/java-exec/src/main/java/org/apache/drill/exec/planner/sql/handlers/DescribeTableHandler.java index d6849f4..1020757 100644 --- a/exec/java-exec/src/main/java/org/apache/drill/exec/planner/sql/handlers/DescribeTableHandler.java +++ b/exec/java-exec/src/main/java/org/apache/drill/exec/planner/sql/handlers/DescribeTableHandler.java @@ -19,11 +19,13 @@ package org.apache.drill.exec.planner.sql.handlers; import com.google.common.collect.ImmutableList; +import net.hydromatic.optiq.SchemaPlus; import net.hydromatic.optiq.tools.Planner; import net.hydromatic.optiq.tools.RelConversionException; import org.apache.drill.exec.ops.QueryContext; import org.apache.drill.exec.planner.sql.parser.DrillParserUtil; import org.apache.drill.exec.planner.sql.parser.SqlDescribeTable; +import org.apache.drill.exec.store.AbstractSchema; import org.eigenbase.sql.*; import org.eigenbase.sql.fun.SqlStdOperatorTable; import org.eigenbase.sql.parser.SqlParserPos; @@ -39,50 +41,61 @@ public class DescribeTableHandler extends DefaultSqlHandler { /** Rewrite the parse tree as SELECT ... FROM INFORMATION_SCHEMA.COLUMNS ... */ @Override - public SqlNode rewrite(SqlNode sqlNode) throws RelConversionException{ + public SqlNode rewrite(SqlNode sqlNode) throws RelConversionException { SqlDescribeTable node = unwrap(sqlNode, SqlDescribeTable.class); - List<SqlNode> selectList = ImmutableList.of((SqlNode)new SqlIdentifier("COLUMN_NAME", SqlParserPos.ZERO), - new SqlIdentifier("DATA_TYPE", SqlParserPos.ZERO), - new SqlIdentifier("IS_NULLABLE", SqlParserPos.ZERO)); + try { + List<SqlNode> selectList = ImmutableList.of((SqlNode) new SqlIdentifier("COLUMN_NAME", SqlParserPos.ZERO), + new SqlIdentifier("DATA_TYPE", SqlParserPos.ZERO), + new SqlIdentifier("IS_NULLABLE", SqlParserPos.ZERO)); - SqlNode fromClause = new SqlIdentifier( - ImmutableList.of("INFORMATION_SCHEMA", "COLUMNS"), null, SqlParserPos.ZERO, null); + SqlNode fromClause = new SqlIdentifier( + ImmutableList.of("INFORMATION_SCHEMA", "COLUMNS"), null, SqlParserPos.ZERO, null); - final SqlIdentifier table = node.getTable(); - final int numLevels = table.names.size(); + final SqlIdentifier table = node.getTable(); + final SchemaPlus schema = findSchema(context.getRootSchema(), context.getNewDefaultSchema(), + Util.skipLast(table.names)); + final String tableName = Util.last(table.names); - SqlNode schemaCondition = null; - if (numLevels > 1) { - schemaCondition = DrillParserUtil.createCondition( - new SqlIdentifier("TABLE_SCHEMA", SqlParserPos.ZERO), - SqlStdOperatorTable.EQUALS, - SqlLiteral.createCharString(Util.sepList(table.names.subList(0, numLevels - 1), "."), - CHARSET, SqlParserPos.ZERO) - ); - } + if (schema.getTable(tableName) == null) { + throw new RelConversionException(String.format("Table %s is not valid", Util.sepList(table.names, "."))); + } - SqlNode where = DrillParserUtil.createCondition( - new SqlIdentifier("TABLE_NAME", SqlParserPos.ZERO), - SqlStdOperatorTable.EQUALS, - SqlLiteral.createCharString(table.names.get(numLevels-1), CHARSET, SqlParserPos.ZERO)); + SqlNode schemaCondition = null; + if (!isRootSchema(schema)) { + AbstractSchema drillSchema = getDrillSchema(schema); - where = DrillParserUtil.createCondition(schemaCondition, SqlStdOperatorTable.AND, where); + schemaCondition = DrillParserUtil.createCondition( + new SqlIdentifier("TABLE_SCHEMA", SqlParserPos.ZERO), + SqlStdOperatorTable.EQUALS, + SqlLiteral.createCharString(drillSchema.getFullSchemaName(), CHARSET, SqlParserPos.ZERO) + ); + } - SqlNode columnFilter = null; - if (node.getColumn() != null) { - columnFilter = DrillParserUtil.createCondition(new SqlIdentifier("COLUMN_NAME", SqlParserPos.ZERO), + SqlNode where = DrillParserUtil.createCondition( + new SqlIdentifier("TABLE_NAME", SqlParserPos.ZERO), SqlStdOperatorTable.EQUALS, - SqlLiteral.createCharString(node.getColumn().toString(), CHARSET, SqlParserPos.ZERO)); - } else if (node.getColumnQualifier() != null) { - columnFilter = DrillParserUtil.createCondition(new SqlIdentifier("COLUMN_NAME", SqlParserPos.ZERO), - SqlStdOperatorTable.LIKE, node.getColumnQualifier()); + SqlLiteral.createCharString(tableName, CHARSET, SqlParserPos.ZERO)); + + where = DrillParserUtil.createCondition(schemaCondition, SqlStdOperatorTable.AND, where); + + SqlNode columnFilter = null; + if (node.getColumn() != null) { + columnFilter = DrillParserUtil.createCondition(new SqlIdentifier("COLUMN_NAME", SqlParserPos.ZERO), + SqlStdOperatorTable.EQUALS, + SqlLiteral.createCharString(node.getColumn().toString(), CHARSET, SqlParserPos.ZERO)); + } else if (node.getColumnQualifier() != null) { + columnFilter = DrillParserUtil.createCondition(new SqlIdentifier("COLUMN_NAME", SqlParserPos.ZERO), + SqlStdOperatorTable.LIKE, node.getColumnQualifier()); + } + + where = DrillParserUtil.createCondition(where, SqlStdOperatorTable.AND, columnFilter); + + return new SqlSelect(SqlParserPos.ZERO, null, new SqlNodeList(selectList, SqlParserPos.ZERO), + fromClause, where, null, null, null, null, null, null); + } catch (Exception ex) { + throw new RelConversionException("Error while rewriting DESCRIBE query: " + ex.getMessage(), ex); } - - where = DrillParserUtil.createCondition(where, SqlStdOperatorTable.AND, columnFilter); - - return new SqlSelect(SqlParserPos.ZERO, null, new SqlNodeList(selectList, SqlParserPos.ZERO), - fromClause, where, null, null, null, null, null, null); } } http://git-wip-us.apache.org/repos/asf/incubator-drill/blob/299d1bf1/exec/jdbc/src/test/java/org/apache/drill/jdbc/test/TestMetadataDDL.java ---------------------------------------------------------------------- diff --git a/exec/jdbc/src/test/java/org/apache/drill/jdbc/test/TestMetadataDDL.java b/exec/jdbc/src/test/java/org/apache/drill/jdbc/test/TestMetadataDDL.java index 59e2d03..06a671d 100644 --- a/exec/jdbc/src/test/java/org/apache/drill/jdbc/test/TestMetadataDDL.java +++ b/exec/jdbc/src/test/java/org/apache/drill/jdbc/test/TestMetadataDDL.java @@ -129,7 +129,7 @@ public class TestMetadataDDL extends TestJdbcQuery { @Test public void testDescribeTable() throws Exception{ - JdbcAssert.withNoDefaultSchema() + JdbcAssert.withFull("INFORMATION_SCHEMA") .sql("DESCRIBE CATALOGS") .returns( "COLUMN_NAME=CATALOG_NAME; DATA_TYPE=VARCHAR; IS_NULLABLE=NO\n"+ @@ -149,6 +149,47 @@ public class TestMetadataDDL extends TestJdbcQuery { } @Test + public void testDescribeSameTableInMultipleSchemas() throws Exception{ + JdbcAssert.withNoDefaultSchema().withConnection(new Function<Connection, Void>() { + public Void apply(Connection connection) { + try { + Statement statement = connection.createStatement(); + statement.executeQuery("USE dfs.tmp").close(); + + // INFORMATION_SCHEMA already has a table named "TABLES". Now create a table with same name in "dfs.tmp" schema + statement.executeQuery("CREATE OR REPLACE VIEW `TABLES` AS SELECT key FROM hive.kv").close(); + + // Test describe of `TABLES` with no schema qualifier + ResultSet resultSet = statement.executeQuery("DESCRIBE `TABLES`"); + String result = JdbcAssert.toString(resultSet).trim(); + resultSet.close(); + String expected = "COLUMN_NAME=key; DATA_TYPE=INTEGER; IS_NULLABLE=NO"; + assertTrue(String.format("Generated string:\n%s\ndoes not match:\n%s", result, expected), expected.equals(result)); + + // Test describe of `TABLES` with a schema qualifier which is not in default schema + resultSet = statement.executeQuery("DESCRIBE INFORMATION_SCHEMA.`TABLES`"); + result = JdbcAssert.toString(resultSet).trim(); + resultSet.close(); + expected = + "COLUMN_NAME=TABLE_CATALOG; DATA_TYPE=VARCHAR; IS_NULLABLE=NO\n" + + "COLUMN_NAME=TABLE_SCHEMA; DATA_TYPE=VARCHAR; IS_NULLABLE=NO\n" + + "COLUMN_NAME=TABLE_NAME; DATA_TYPE=VARCHAR; IS_NULLABLE=NO\n" + + "COLUMN_NAME=TABLE_TYPE; DATA_TYPE=VARCHAR; IS_NULLABLE=NO"; + assertTrue(String.format("Generated string:\n%s\ndoes not match:\n%s", result, expected), expected.equals(result)); + + // drop created view + statement.executeQuery("DROP VIEW `TABLES`").close(); + + statement.close(); + return null; + } catch (Exception e) { + throw new RuntimeException(e); + } + } + }); + } + + @Test public void testDescribeTableWithColumnName() throws Exception{ JdbcAssert.withFull("INFORMATION_SCHEMA") .sql("DESCRIBE `TABLES` TABLE_CATALOG")
