diff --git a/src/main/java/com/alibaba/druid/sql/ast/statement/SQLCreateTableStatement.java b/src/main/java/com/alibaba/druid/sql/ast/statement/SQLCreateTableStatement.java index 3cd055fa74..bc12ca29d1 100644 --- a/src/main/java/com/alibaba/druid/sql/ast/statement/SQLCreateTableStatement.java +++ b/src/main/java/com/alibaba/druid/sql/ast/statement/SQLCreateTableStatement.java @@ -27,6 +27,7 @@ import com.alibaba.druid.sql.dialect.mysql.ast.MySqlUnique; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlTableIndex; import com.alibaba.druid.sql.visitor.SQLASTVisitor; import com.alibaba.druid.util.FnvHash; +import com.alibaba.druid.util.JdbcConstants; import com.alibaba.druid.util.ListDG; import com.alibaba.druid.util.lang.Consumer; @@ -52,6 +53,7 @@ public class SQLCreateTableStatement extends SQLStatementImpl implements SQLDDLS protected SQLName tablespace; protected SQLPartitionBy partitioning; + protected SQLName storedAs; public SQLCreateTableStatement(){ @@ -978,6 +980,17 @@ public class SQLCreateTableStatement extends SQLStatementImpl implements SQLDDLS } } + public SQLName getStoredAs() { + return storedAs; + } + + public void setStoredAs(SQLName x) { + if (x != null) { + x.setParent(this); + } + this.storedAs = x; + } + public SQLCreateTableStatement clone() { SQLCreateTableStatement x = new SQLCreateTableStatement(dbType); cloneTo(x); diff --git a/src/main/java/com/alibaba/druid/sql/dialect/hive/parser/HiveCreateTableParser.java b/src/main/java/com/alibaba/druid/sql/dialect/hive/parser/HiveCreateTableParser.java new file mode 100644 index 0000000000..b6dff806dd --- /dev/null +++ b/src/main/java/com/alibaba/druid/sql/dialect/hive/parser/HiveCreateTableParser.java @@ -0,0 +1,127 @@ +package com.alibaba.druid.sql.dialect.hive.parser; + +import com.alibaba.druid.sql.ast.SQLName; +import com.alibaba.druid.sql.ast.statement.*; +import com.alibaba.druid.sql.dialect.hive.stmt.HiveCreateTableStatement; +import com.alibaba.druid.sql.parser.*; +import com.alibaba.druid.util.FnvHash; +import com.alibaba.druid.util.JdbcConstants; + +public class HiveCreateTableParser extends SQLCreateTableParser { + public HiveCreateTableParser(SQLExprParser exprParser) { + super(exprParser); + } + + public SQLCreateTableStatement parseCreateTable(boolean acceptCreate) { + HiveCreateTableStatement stmt = newCreateStatement(); + + if (acceptCreate) { + if (lexer.hasComment() && lexer.isKeepComments()) { + stmt.addBeforeComment(lexer.readAndResetComments()); + } + + accept(Token.CREATE); + } + + if (lexer.identifierEquals("GLOBAL")) { + lexer.nextToken(); + + if (lexer.identifierEquals("TEMPORARY")) { + lexer.nextToken(); + stmt.setType(SQLCreateTableStatement.Type.GLOBAL_TEMPORARY); + } else { + throw new ParserException("syntax error " + lexer.info()); + } + } else if (lexer.token() == Token.IDENTIFIER && lexer.stringVal().equalsIgnoreCase("LOCAL")) { + lexer.nextToken(); + if (lexer.token() == Token.IDENTIFIER && lexer.stringVal().equalsIgnoreCase("TEMPORAY")) { + lexer.nextToken(); + stmt.setType(SQLCreateTableStatement.Type.LOCAL_TEMPORARY); + } else { + throw new ParserException("syntax error. " + lexer.info()); + } + } + + accept(Token.TABLE); + + if (lexer.token() == Token.IF || lexer.identifierEquals("IF")) { + lexer.nextToken(); + accept(Token.NOT); + accept(Token.EXISTS); + + stmt.setIfNotExiists(true); + } + + stmt.setName(this.exprParser.name()); + + if (lexer.token() == Token.LPAREN) { + lexer.nextToken(); + + for (; ; ) { + Token token = lexer.token(); + if (token == Token.IDENTIFIER + && lexer.stringVal().equalsIgnoreCase("SUPPLEMENTAL") + && JdbcConstants.ORACLE.equals(dbType)) { + this.parseCreateTableSupplementalLogingProps(stmt); + } else if (token == Token.IDENTIFIER // + || token == Token.LITERAL_ALIAS) { + SQLColumnDefinition column = this.exprParser.parseColumn(); + stmt.getTableElementList().add(column); + } else if (token == Token.PRIMARY // + || token == Token.UNIQUE // + || token == Token.CHECK // + || token == Token.CONSTRAINT + || token == Token.FOREIGN) { + SQLConstraint constraint = this.exprParser.parseConstaint(); + constraint.setParent(stmt); + stmt.getTableElementList().add((SQLTableElement) constraint); + } else if (token == Token.TABLESPACE) { + throw new ParserException("TODO " + lexer.info()); + } else { + SQLColumnDefinition column = this.exprParser.parseColumn(); + stmt.getTableElementList().add(column); + } + + if (lexer.token() == Token.COMMA) { + lexer.nextToken(); + + if (lexer.token() == Token.RPAREN) { // compatible for sql server + break; + } + continue; + } + + break; + } + + accept(Token.RPAREN); + + if (lexer.identifierEquals("INHERITS")) { + lexer.nextToken(); + accept(Token.LPAREN); + SQLName inherits = this.exprParser.name(); + stmt.setInherits(new SQLExprTableSource(inherits)); + accept(Token.RPAREN); + } + } + + if (lexer.identifierEquals(FnvHash.Constants.STORED)) { + lexer.nextToken(); + accept(Token.AS); + SQLName name = this.exprParser.name(); + stmt.setStoredAs(name); + } + + if (lexer.token() == Token.AS) { + lexer.nextToken(); + SQLSelect select = this.createSQLSelectParser().select(); + stmt.setSelect(select); + } + + return stmt; + } + + protected HiveCreateTableStatement newCreateStatement() { + return new HiveCreateTableStatement(); + } +} diff --git a/src/main/java/com/alibaba/druid/sql/dialect/hive/parser/HiveStatementParser.java b/src/main/java/com/alibaba/druid/sql/dialect/hive/parser/HiveStatementParser.java index f6df697308..88b1bc5417 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/hive/parser/HiveStatementParser.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/hive/parser/HiveStatementParser.java @@ -20,10 +20,7 @@ import com.alibaba.druid.sql.ast.SQLStatement; import com.alibaba.druid.sql.ast.expr.SQLQueryExpr; import com.alibaba.druid.sql.ast.statement.SQLReplaceStatement; import com.alibaba.druid.sql.ast.statement.SQLSelect; -import com.alibaba.druid.sql.parser.Lexer; -import com.alibaba.druid.sql.parser.SQLParserFeature; -import com.alibaba.druid.sql.parser.SQLStatementParser; -import com.alibaba.druid.sql.parser.Token; +import com.alibaba.druid.sql.parser.*; import com.alibaba.druid.util.JdbcConstants; public class HiveStatementParser extends SQLStatementParser { @@ -75,4 +72,8 @@ public class HiveStatementParser extends SQLStatementParser { return stmt; } + + public SQLCreateTableParser getSQLCreateTableParser() { + return new HiveCreateTableParser(this.exprParser); + } } diff --git a/src/main/java/com/alibaba/druid/sql/dialect/hive/stmt/HiveCreateTableStatement.java b/src/main/java/com/alibaba/druid/sql/dialect/hive/stmt/HiveCreateTableStatement.java new file mode 100644 index 0000000000..1006879c2a --- /dev/null +++ b/src/main/java/com/alibaba/druid/sql/dialect/hive/stmt/HiveCreateTableStatement.java @@ -0,0 +1,12 @@ +package com.alibaba.druid.sql.dialect.hive.stmt; + +import com.alibaba.druid.sql.ast.SQLName; +import com.alibaba.druid.sql.ast.expr.SQLIdentifierExpr; +import com.alibaba.druid.sql.ast.statement.SQLCreateTableStatement; +import com.alibaba.druid.util.JdbcConstants; + +public class HiveCreateTableStatement extends SQLCreateTableStatement { + public HiveCreateTableStatement() { + this.dbType = JdbcConstants.HIVE; + } +} diff --git a/src/main/java/com/alibaba/druid/sql/dialect/hive/visitor/HiveOutputVisitor.java b/src/main/java/com/alibaba/druid/sql/dialect/hive/visitor/HiveOutputVisitor.java index 63fea168aa..27e0a1d5c4 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/hive/visitor/HiveOutputVisitor.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/hive/visitor/HiveOutputVisitor.java @@ -15,6 +15,9 @@ */ package com.alibaba.druid.sql.dialect.hive.visitor; +import com.alibaba.druid.sql.ast.statement.SQLCreateTableStatement; +import com.alibaba.druid.sql.ast.statement.SQLExprTableSource; +import com.alibaba.druid.sql.ast.statement.SQLSelect; import com.alibaba.druid.sql.visitor.SQLASTOutputVisitor; import java.util.List; @@ -31,5 +34,4 @@ public class HiveOutputVisitor extends SQLASTOutputVisitor implements HiveASTVis public HiveOutputVisitor(Appendable appender, boolean parameterized) { super(appender, parameterized); } - } diff --git a/src/main/java/com/alibaba/druid/sql/parser/SQLParser.java b/src/main/java/com/alibaba/druid/sql/parser/SQLParser.java index 332af0ed30..d73032eb7f 100644 --- a/src/main/java/com/alibaba/druid/sql/parser/SQLParser.java +++ b/src/main/java/com/alibaba/druid/sql/parser/SQLParser.java @@ -15,6 +15,9 @@ */ package com.alibaba.druid.sql.parser; +import com.alibaba.druid.sql.ast.statement.SQLCreateTableStatement; +import com.alibaba.druid.sql.dialect.hive.stmt.HiveCreateTableStatement; + public class SQLParser { protected final Lexer lexer; protected String dbType; @@ -323,4 +326,8 @@ public class SQLParser { public final boolean isEnabled(SQLParserFeature feature) { return lexer.isEnabled(feature); } + + protected SQLCreateTableStatement newCreateStatement() { + return new SQLCreateTableStatement(getDbType()); + } } diff --git a/src/main/java/com/alibaba/druid/sql/visitor/SQLASTOutputVisitor.java b/src/main/java/com/alibaba/druid/sql/visitor/SQLASTOutputVisitor.java index 856d174226..7a624efcbe 100644 --- a/src/main/java/com/alibaba/druid/sql/visitor/SQLASTOutputVisitor.java +++ b/src/main/java/com/alibaba/druid/sql/visitor/SQLASTOutputVisitor.java @@ -2537,6 +2537,12 @@ public class SQLASTOutputVisitor extends SQLASTVisitorAdapter implements Paramet print(')'); } + SQLName storedAs = x.getStoredAs(); + if (storedAs != null) { + print0(ucase ? " STORE AS " : " store as "); + printExpr(storedAs); + } + SQLSelect select = x.getSelect(); if (printSelect && select != null) { println(); diff --git a/src/test/java/com/alibaba/druid/bvt/sql/h2/H2_CreateTableTest_0.java b/src/test/java/com/alibaba/druid/bvt/sql/h2/H2_CreateTableTest_0.java new file mode 100644 index 0000000000..6627e5f26b --- /dev/null +++ b/src/test/java/com/alibaba/druid/bvt/sql/h2/H2_CreateTableTest_0.java @@ -0,0 +1,69 @@ +/* + * Copyright 1999-2017 Alibaba Group Holding Ltd. + * + * Licensed 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 com.alibaba.druid.bvt.sql.h2; + +import com.alibaba.druid.sql.OracleTest; +import com.alibaba.druid.sql.SQLUtils; +import com.alibaba.druid.sql.ast.SQLStatement; +import com.alibaba.druid.sql.dialect.oracle.visitor.OracleSchemaStatVisitor; +import com.alibaba.druid.util.JdbcConstants; +import org.junit.Assert; + +import java.util.List; + +public class H2_CreateTableTest_0 extends OracleTest { + + public void test_0() throws Exception { + String sql = // + "create table if not EXISTS sc001.test stored as parquet as\n" + + "select * from sc002.test;"; // + + List statementList = SQLUtils.toStatementList(sql, JdbcConstants.HIVE); + SQLStatement stmt = statementList.get(0); + print(statementList); + + + + Assert.assertEquals(1, statementList.size()); + + OracleSchemaStatVisitor visitor = new OracleSchemaStatVisitor(); + stmt.accept(visitor); + + { + String text = SQLUtils.toSQLString(stmt, JdbcConstants.HIVE); + + assertEquals("CREATE TABLE sc001.test STORE AS parquet\n" + + "AS\n" + + "SELECT *\n" + + "FROM sc002.test;", text); + } + + System.out.println("Tables : " + visitor.getTables()); + System.out.println("fields : " + visitor.getColumns()); + System.out.println("coditions : " + visitor.getConditions()); + System.out.println("relationships : " + visitor.getRelationships()); + System.out.println("orderBy : " + visitor.getOrderByColumns()); + + assertEquals(2, visitor.getTables().size()); + assertEquals(1, visitor.getColumns().size()); + assertEquals(0, visitor.getConditions().size()); + assertEquals(0, visitor.getRelationships().size()); + assertEquals(0, visitor.getOrderByColumns().size()); + + Assert.assertTrue(visitor.containsTable("sc001.test")); + + } +}