This is an automated email from the ASF dual-hosted git repository. richardantal pushed a commit to branch 4.x in repository https://gitbox.apache.org/repos/asf/phoenix.git
The following commit(s) were added to refs/heads/4.x by this push: new c14b61d PHOENIX-6518 Implement SHOW CREATE TABLE SQL command c14b61d is described below commit c14b61d61ec7eb15d6f9cf5652e242c3e72b13ba Author: Richard Antal <antal97rich...@gmail.com> AuthorDate: Thu Jul 29 11:03:53 2021 +0200 PHOENIX-6518 Implement SHOW CREATE TABLE SQL command Change-Id: I2b8f693a94e1b9fee1e54ef09d2a28569de7a669 --- .../apache/phoenix/end2end/ShowCreateTableIT.java | 113 +++++++++++++++++++++ phoenix-core/src/main/antlr3/PhoenixSQL.g | 6 ++ .../org/apache/phoenix/jdbc/PhoenixStatement.java | 23 +++++ .../org/apache/phoenix/parse/ParseNodeFactory.java | 4 + .../org/apache/phoenix/parse/ShowCreateTable.java | 38 +++++++ .../phoenix/parse/ShowCreateTableStatement.java | 68 +++++++++++++ .../java/org/apache/phoenix/util/QueryUtil.java | 29 ++++++ .../org/apache/phoenix/parse/QueryParserTest.java | 16 +++ 8 files changed, 297 insertions(+) diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/ShowCreateTableIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/ShowCreateTableIT.java new file mode 100644 index 0000000..f4502d0 --- /dev/null +++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/ShowCreateTableIT.java @@ -0,0 +1,113 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.phoenix.end2end; + +import org.apache.phoenix.util.SchemaUtil; +import org.junit.Ignore; +import org.junit.Test; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.ResultSet; +import java.util.Properties; + +import static org.junit.Assert.assertTrue; + +public class ShowCreateTableIT extends ParallelStatsDisabledIT { + @Test + public void testShowCreateTableBasic() throws Exception { + Properties props = new Properties(); + Connection conn = DriverManager.getConnection(getUrl(), props); + String tableName = generateUniqueName(); + String ddl = "CREATE TABLE " + tableName + "(K VARCHAR NOT NULL PRIMARY KEY, INT INTEGER)"; + conn.createStatement().execute(ddl); + + ResultSet rs = conn.createStatement().executeQuery("SHOW CREATE TABLE " + tableName ); + assertTrue(rs.next()); + assertTrue("Expected: :" + ddl + "\nResult: " + rs.getString(1), + rs.getString(1).contains(ddl)); + } + + @Ignore + @Test + public void testShowCreateTableLowerCase() throws Exception { + Properties props = new Properties(); + Connection conn = DriverManager.getConnection(getUrl(), props); + String tableName = "lowercasetbl1"; + String ddl = "CREATE TABLE \"" + tableName + "\"(K VARCHAR NOT NULL PRIMARY KEY, INT INTEGER)"; + conn.createStatement().execute(ddl); + + ResultSet rs = conn.createStatement().executeQuery("SHOW CREATE TABLE \"" + tableName + "\""); + assertTrue(rs.next()); + assertTrue("Expected: :" + ddl + "\nResult: " + rs.getString(1), + rs.getString(1).contains(ddl)); + } + + @Test + public void testShowCreateTableUpperCase() throws Exception { + Properties props = new Properties(); + Connection conn = DriverManager.getConnection(getUrl(), props); + String tableName = generateUniqueName(); + String schemaName = generateUniqueName(); + String tableFullName = SchemaUtil.getQualifiedTableName(schemaName, tableName); + String ddl = "CREATE TABLE " + tableFullName + "(K VARCHAR NOT NULL PRIMARY KEY, INT INTEGER)"; + conn.createStatement().execute(ddl); + + ResultSet rs = conn.createStatement().executeQuery("SHOW CREATE TABLE " + tableFullName); + assertTrue(rs.next()); + assertTrue("Expected: :" + ddl + "\nResult: " + rs.getString(1), + rs.getString(1).contains(ddl)); + } + + @Test + public void testShowCreateTableView() throws Exception { + Properties props = new Properties(); + Connection conn = DriverManager.getConnection(getUrl(), props); + String tableName = generateUniqueName(); + String viewName = generateUniqueName(); + + String schemaName = generateUniqueName(); + String tableFullName = SchemaUtil.getQualifiedTableName(schemaName, tableName); + String viewFullName = SchemaUtil.getQualifiedTableName(schemaName, viewName); + String ddl = "CREATE TABLE " + tableFullName + "(K VARCHAR NOT NULL PRIMARY KEY, INT INTEGER)"; + conn.createStatement().execute(ddl); + String ddlView = "CREATE VIEW " + viewFullName + " AS SELECT * FROM " + tableFullName; + conn.createStatement().execute(ddlView); + + ResultSet rs = conn.createStatement().executeQuery("SHOW CREATE TABLE " + viewFullName); + assertTrue(rs.next()); + assertTrue("Expected: :" + ddlView + "\nResult: " + rs.getString(1), + rs.getString(1).contains(ddlView)); + } + + @Test + public void testShowCreateTableIndex() throws Exception { + Properties props = new Properties(); + Connection conn = DriverManager.getConnection(getUrl(), props); + String tableName = generateUniqueName(); + String indexname = generateUniqueName(); + String ddl = "CREATE TABLE " + tableName + "(K VARCHAR NOT NULL PRIMARY KEY, INT INTEGER)"; + conn.createStatement().execute(ddl); + String createIndex = "CREATE INDEX " + indexname + " ON " + tableName + "(K DESC)"; + conn.createStatement().execute(createIndex); + ResultSet rs = conn.createStatement().executeQuery("SHOW CREATE TABLE " + indexname); + assertTrue(rs.next()); + assertTrue("Expected: " + createIndex + "\nResult: " + rs.getString(1), + rs.getString(1).contains(createIndex)); + } +} diff --git a/phoenix-core/src/main/antlr3/PhoenixSQL.g b/phoenix-core/src/main/antlr3/PhoenixSQL.g index d79b742..1d66dba 100644 --- a/phoenix-core/src/main/antlr3/PhoenixSQL.g +++ b/phoenix-core/src/main/antlr3/PhoenixSQL.g @@ -428,6 +428,7 @@ oneStatement returns [BindableStatement ret] | s=alter_index_node | s=alter_table_node | s=show_node + | s=show_create_table_node | s=trace_node | s=create_function_node | s=drop_function_node @@ -496,6 +497,11 @@ show_node returns [ShowStatement ret] | SHOW SCHEMAS (LIKE pattern=string_literal)? { $ret = factory.showSchemasStatement(pattern); } ; +// Parse a describe statement. SHOW CREATE TABLE tablename/viewname/indexname ... +show_create_table_node returns [ShowCreateTable ret] + : SHOW CREATE TABLE tablename=from_table_name { $ret = factory.showCreateTable(tablename); } + ; + // Parse a create view statement. create_view_node returns [CreateTableStatement ret] : CREATE VIEW (IF NOT ex=EXISTS)? t=from_table_name diff --git a/phoenix-core/src/main/java/org/apache/phoenix/jdbc/PhoenixStatement.java b/phoenix-core/src/main/java/org/apache/phoenix/jdbc/PhoenixStatement.java index 2a76887..c116508 100644 --- a/phoenix-core/src/main/java/org/apache/phoenix/jdbc/PhoenixStatement.java +++ b/phoenix-core/src/main/java/org/apache/phoenix/jdbc/PhoenixStatement.java @@ -140,6 +140,8 @@ import org.apache.phoenix.parse.DMLStatement; import org.apache.phoenix.parse.DeclareCursorStatement; import org.apache.phoenix.parse.DeleteJarStatement; import org.apache.phoenix.parse.DeleteStatement; +import org.apache.phoenix.parse.ShowCreateTableStatement; +import org.apache.phoenix.parse.ShowCreateTable; import org.apache.phoenix.parse.DropColumnStatement; import org.apache.phoenix.parse.DropFunctionStatement; import org.apache.phoenix.parse.DropIndexStatement; @@ -1311,6 +1313,22 @@ public class PhoenixStatement implements Statement, SQLCloseable { } } + private static class ExecutableShowCreateTable extends ShowCreateTableStatement + implements CompilableStatement { + + public ExecutableShowCreateTable(TableName tableName) { + super(tableName); + } + + @Override + public QueryPlan compilePlan(final PhoenixStatement stmt, Sequence.ValueOp seqAction) + throws SQLException { + PreparedStatement delegateStmt = QueryUtil.getShowCreateTableStmt(stmt.getConnection(), null, + getTableName()); + return ((PhoenixPreparedStatement) delegateStmt).compileQuery(); + } + } + private static class ExecutableCreateIndexStatement extends CreateIndexStatement implements CompilableStatement { public ExecutableCreateIndexStatement(NamedNode indexName, NamedTableNode dataTable, IndexKeyConstraint ikConstraint, List<ColumnName> includeColumns, List<ParseNode> splits, @@ -1898,6 +1916,11 @@ public class PhoenixStatement implements Statement, SQLCloseable { return new ExecutableShowSchemasStatement(pattern); } + @Override + public ShowCreateTable showCreateTable(TableName tableName) { + return new ExecutableShowCreateTable(tableName); + } + } static class PhoenixStatementParser extends SQLParser { diff --git a/phoenix-core/src/main/java/org/apache/phoenix/parse/ParseNodeFactory.java b/phoenix-core/src/main/java/org/apache/phoenix/parse/ParseNodeFactory.java index 786b356..8138ea0 100644 --- a/phoenix-core/src/main/java/org/apache/phoenix/parse/ParseNodeFactory.java +++ b/phoenix-core/src/main/java/org/apache/phoenix/parse/ParseNodeFactory.java @@ -952,4 +952,8 @@ public class ParseNodeFactory { public ShowSchemasStatement showSchemasStatement(String pattern) { return new ShowSchemasStatement(pattern); } + + public ShowCreateTable showCreateTable(TableName tableName) { + return new ShowCreateTableStatement(tableName); + } } diff --git a/phoenix-core/src/main/java/org/apache/phoenix/parse/ShowCreateTable.java b/phoenix-core/src/main/java/org/apache/phoenix/parse/ShowCreateTable.java new file mode 100644 index 0000000..4fe77a7 --- /dev/null +++ b/phoenix-core/src/main/java/org/apache/phoenix/parse/ShowCreateTable.java @@ -0,0 +1,38 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.phoenix.parse; + +import org.apache.phoenix.jdbc.PhoenixStatement; + +/** + * Parent class for SHOW CREATE TABLE statements. + */ +public class ShowCreateTable implements BindableStatement { + @Override + public int getBindCount() { + return 0; + } + + @Override + public PhoenixStatement.Operation getOperation() { + return PhoenixStatement.Operation.QUERY; + } + + public ShowCreateTable() {} +} diff --git a/phoenix-core/src/main/java/org/apache/phoenix/parse/ShowCreateTableStatement.java b/phoenix-core/src/main/java/org/apache/phoenix/parse/ShowCreateTableStatement.java new file mode 100644 index 0000000..2a9b127 --- /dev/null +++ b/phoenix-core/src/main/java/org/apache/phoenix/parse/ShowCreateTableStatement.java @@ -0,0 +1,68 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.phoenix.parse; + +import org.apache.phoenix.compile.ColumnResolver; +import com.google.common.base.Preconditions; +import org.apache.phoenix.util.SchemaUtil; + +import java.util.Objects; + +/** + * ParseNode implementation for SHOW CREATE TABLE statements. + */ +public class ShowCreateTableStatement extends ShowCreateTable { + + private TableName tableName; + + public ShowCreateTableStatement(TableName tn) { + tableName = tn; + } + + public TableName getTableName() { + return tableName; + } + + public void toSQL(ColumnResolver resolver, StringBuilder buf) { + Preconditions.checkNotNull(buf); + buf.append("SHOW CREATE TABLE "); + + buf.append(SchemaUtil + .getEscapedTableName(tableName.getSchemaName(), tableName.getTableName())); + } + + @Override + public String toString() { + StringBuilder buf = new StringBuilder(); + toSQL(null, buf); + return buf.toString(); + } + + @Override + public boolean equals(Object other) { + if (!(other instanceof ShowCreateTableStatement)) return false; + ShowCreateTableStatement stmt = (ShowCreateTableStatement) other; + return Objects.equals(tableName, stmt.getTableName()); + } + + @Override + public int hashCode() { + return Objects.hash(tableName); + } +} diff --git a/phoenix-core/src/main/java/org/apache/phoenix/util/QueryUtil.java b/phoenix-core/src/main/java/org/apache/phoenix/util/QueryUtil.java index 749b601..4ad09f6 100644 --- a/phoenix-core/src/main/java/org/apache/phoenix/util/QueryUtil.java +++ b/phoenix-core/src/main/java/org/apache/phoenix/util/QueryUtil.java @@ -88,6 +88,7 @@ import org.apache.phoenix.jdbc.PhoenixConnection; import org.apache.phoenix.jdbc.PhoenixEmbeddedDriver; import org.apache.phoenix.parse.HintNode; import org.apache.phoenix.parse.HintNode.Hint; +import org.apache.phoenix.parse.TableName; import org.apache.phoenix.parse.WildcardParseNode; import org.apache.phoenix.query.QueryConstants; import org.apache.phoenix.query.QueryServices; @@ -99,6 +100,8 @@ import org.apache.phoenix.schema.PTableType; import org.apache.phoenix.schema.SortOrder; import org.apache.phoenix.schema.tuple.Tuple; import org.apache.phoenix.schema.types.PInteger; +import org.apache.phoenix.schema.tool.SchemaExtractionProcessor; +import org.apache.phoenix.schema.tool.SchemaProcessor; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -769,6 +772,32 @@ public final class QueryUtil { return stmt; } + /** + * Util that generates a PreparedStatement against syscat to get the table listing in a given schema. + */ + public static PreparedStatement getShowCreateTableStmt(PhoenixConnection connection, String catalog, TableName tn) throws SQLException { + + String output; + SchemaProcessor processor = new SchemaExtractionProcessor(null, + connection.unwrap(PhoenixConnection.class).getQueryServices().getConfiguration(), + tn.getSchemaName() == null ? null : "\"" + tn.getSchemaName()+ "\"", + "\"" + tn.getTableName() + "\""); + try { + output = processor.process(); + } catch (Exception e) { + LOGGER.error(e.getStackTrace().toString()); + throw new SQLException(e.getMessage()); + } + + StringBuilder buf = new StringBuilder("select \n" + + " ? as \"CREATE STATEMENT\""); + PreparedStatement stmt = connection.prepareStatement(buf.toString()); + + stmt.setString(1, output); + + return stmt; + } + public static void addTenantIdFilter(PhoenixConnection connection, StringBuilder buf, String tenantIdPattern, List<String> parameterValues) { PName tenantId = connection.getTenantId(); diff --git a/phoenix-core/src/test/java/org/apache/phoenix/parse/QueryParserTest.java b/phoenix-core/src/test/java/org/apache/phoenix/parse/QueryParserTest.java index fbf1cea..0dc86c5 100644 --- a/phoenix-core/src/test/java/org/apache/phoenix/parse/QueryParserTest.java +++ b/phoenix-core/src/test/java/org/apache/phoenix/parse/QueryParserTest.java @@ -912,4 +912,20 @@ public class QueryParserTest { parseQueryThatShouldFail("show tables in 'foo'"); parseQueryThatShouldFail("show tables like foo"); } + + @Test + public void testShowCreateTable() throws Exception { + // Happy paths + parseQuery("SHOW CREATE TABLE FOO"); + parseQuery("show create table FOO"); + parseQuery("SHOW CREATE TABLE s.FOO"); + parseQuery("SHOW CREATE TABLE \"foo\""); + parseQuery("SHOW CREATE TABLE s.\"foo\""); + parseQuery("SHOW CREATE TABLE \"s\".FOO"); + + // Expected failures. + parseQueryThatShouldFail("SHOW CREATE VIEW foo"); + parseQueryThatShouldFail("SHOW CREATE TABLE 'foo'"); + + } }