diff --git a/ql/src/java/org/apache/hadoop/hive/ql/ErrorMsg.java b/ql/src/java/org/apache/hadoop/hive/ql/ErrorMsg.java index f091f67..1de3309 100644 --- a/ql/src/java/org/apache/hadoop/hive/ql/ErrorMsg.java +++ b/ql/src/java/org/apache/hadoop/hive/ql/ErrorMsg.java @@ -447,6 +447,8 @@ INVALID_LOAD_TABLE_FILE_WORK(10322, "Invalid Load Table Work or Load File Work"), CLASSPATH_ERROR(10323, "Classpath error"), IMPORT_SEMANTIC_ERROR(10324, "Import Semantic Analyzer Error"), + INVALID_FK_SYNTAX(10325, "Invalid Foreign Key syntax"), + INVALID_PK_SYNTAX(10326, "Invalid Primary Key syntax"), //========================== 20000 range starts here ========================// SCRIPT_INIT_ERROR(20000, "Unable to initialize custom script."), SCRIPT_IO_ERROR(20001, "An error occurred while reading or writing to your custom script. " @@ -463,7 +465,6 @@ OP_NOT_ALLOWED_IN_AUTOCOMMIT(20006, "Operation {0} is not allowed when autoCommit=true.", true),//todo: better SQLState? OP_NOT_ALLOWED_IN_TXN(20007, "Operation {0} is not allowed in a transaction. TransactionID={1}.", true), OP_NOT_ALLOWED_WITHOUT_TXN(20008, "Operation {0} is not allowed since autoCommit=false and there is no active transaction", true), - //========================== 30000 range starts here ========================// STATSPUBLISHER_NOT_OBTAINED(30000, "StatsPublisher cannot be obtained. " + "There was a error to retrieve the StatsPublisher, and retrying " + diff --git a/ql/src/java/org/apache/hadoop/hive/ql/exec/DDLTask.java b/ql/src/java/org/apache/hadoop/hive/ql/exec/DDLTask.java index b26f09d..373a9a2 100644 --- a/ql/src/java/org/apache/hadoop/hive/ql/exec/DDLTask.java +++ b/ql/src/java/org/apache/hadoop/hive/ql/exec/DDLTask.java @@ -78,6 +78,8 @@ import org.apache.hadoop.hive.metastore.api.Order; import org.apache.hadoop.hive.metastore.api.PrincipalType; import org.apache.hadoop.hive.metastore.api.RolePrincipalGrant; +import org.apache.hadoop.hive.metastore.api.SQLForeignKey; +import org.apache.hadoop.hive.metastore.api.SQLPrimaryKey; import org.apache.hadoop.hive.metastore.api.SerDeInfo; import org.apache.hadoop.hive.metastore.api.ShowCompactResponse; import org.apache.hadoop.hive.metastore.api.ShowCompactResponseElement; @@ -3925,6 +3927,8 @@ private int switchDatabase(Hive db, SwitchDatabaseDesc switchDb) private int createTable(Hive db, CreateTableDesc crtTbl) throws HiveException { // create the table Table tbl = crtTbl.toTable(conf); + List primaryKeys = crtTbl.getPrimaryKeys(); + List foreignKeys = crtTbl.getForeignKeys(); LOG.info("creating table " + tbl.getDbName() + "." + tbl.getTableName() + " on " + tbl.getDataLocation()); @@ -3937,7 +3941,12 @@ private int createTable(Hive db, CreateTableDesc crtTbl) throws HiveException { throw new HiveException("Unable to alter table. " + e.getMessage(), e); } } else { - db.createTable(tbl, crtTbl.getIfNotExists()); + if ((foreignKeys != null && foreignKeys.size() > 0 ) || + (primaryKeys != null && primaryKeys.size() > 0)) { + db.createTable(tbl, crtTbl.getIfNotExists(), primaryKeys, foreignKeys); + } else { + db.createTable(tbl, crtTbl.getIfNotExists()); + } if ( crtTbl.isCTAS()) { Table createdTable = db.getTable(tbl.getDbName(), tbl.getTableName()); DataContainer dc = new DataContainer(createdTable.getTTable()); diff --git a/ql/src/java/org/apache/hadoop/hive/ql/metadata/Hive.java b/ql/src/java/org/apache/hadoop/hive/ql/metadata/Hive.java index 4c9acce..f0e39ae 100644 --- a/ql/src/java/org/apache/hadoop/hive/ql/metadata/Hive.java +++ b/ql/src/java/org/apache/hadoop/hive/ql/metadata/Hive.java @@ -104,6 +104,8 @@ import org.apache.hadoop.hive.metastore.api.PrivilegeBag; import org.apache.hadoop.hive.metastore.api.Role; import org.apache.hadoop.hive.metastore.api.RolePrincipalGrant; +import org.apache.hadoop.hive.metastore.api.SQLForeignKey; +import org.apache.hadoop.hive.metastore.api.SQLPrimaryKey; import org.apache.hadoop.hive.metastore.api.SerDeInfo; import org.apache.hadoop.hive.metastore.api.SetPartitionsStatsRequest; import org.apache.hadoop.hive.metastore.api.ShowCompactResponse; @@ -788,7 +790,8 @@ public void createTable(Table tbl) throws HiveException { * if true, ignore AlreadyExistsException * @throws HiveException */ - public void createTable(Table tbl, boolean ifNotExists) throws HiveException { + public void createTable(Table tbl, boolean ifNotExists, + List primaryKeys, List foreignKeys) throws HiveException { try { if (tbl.getDbName() == null || "".equals(tbl.getDbName().trim())) { tbl.setDbName(SessionState.get().getCurrentDatabase()); @@ -813,7 +816,12 @@ public void createTable(Table tbl, boolean ifNotExists) throws HiveException { tTbl.setPrivileges(principalPrivs); } } - getMSC().createTable(tTbl); + if (primaryKeys == null && foreignKeys == null) { + getMSC().createTable(tTbl); + } else { + getMSC().createTableWithConstraints(tTbl, primaryKeys, foreignKeys); + } + } catch (AlreadyExistsException e) { if (!ifNotExists) { throw new HiveException(e); @@ -823,6 +831,10 @@ public void createTable(Table tbl, boolean ifNotExists) throws HiveException { } } + public void createTable(Table tbl, boolean ifNotExists) throws HiveException { + createTable(tbl, ifNotExists, null, null); + } + public static List getFieldsFromDeserializerForMsStorage( Table tbl, Deserializer deserializer) throws SerDeException, MetaException { List schema = MetaStoreUtils.getFieldsFromDeserializer( @@ -3583,4 +3595,5 @@ public long getPermanenFunctionsChangeVersion() throws HiveException { throw new HiveException(e); } } + }; diff --git a/ql/src/java/org/apache/hadoop/hive/ql/parse/BaseSemanticAnalyzer.java b/ql/src/java/org/apache/hadoop/hive/ql/parse/BaseSemanticAnalyzer.java index 19342a8..5abe4e5 100644 --- a/ql/src/java/org/apache/hadoop/hive/ql/parse/BaseSemanticAnalyzer.java +++ b/ql/src/java/org/apache/hadoop/hive/ql/parse/BaseSemanticAnalyzer.java @@ -44,6 +44,8 @@ import org.apache.hadoop.hive.metastore.api.Database; import org.apache.hadoop.hive.metastore.api.FieldSchema; import org.apache.hadoop.hive.metastore.api.Order; +import org.apache.hadoop.hive.metastore.api.SQLForeignKey; +import org.apache.hadoop.hive.metastore.api.SQLPrimaryKey; import org.apache.hadoop.hive.ql.CompilationOpContext; import org.apache.hadoop.hive.ql.Context; import org.apache.hadoop.hive.ql.ErrorMsg; @@ -614,29 +616,212 @@ private static String spliceString(String str, int i, int length, String replace * Get the list of FieldSchema out of the ASTNode. */ public static List getColumns(ASTNode ast, boolean lowerCase) throws SemanticException { + return getColumns(ast, lowerCase, new ArrayList(), new ArrayList()); + } + + static class PKInfo { + public String colName; + public String constraintName; + public boolean rely; + + public PKInfo(String colName, String constraintName, boolean rely) { + this.colName = colName; + this.constraintName = constraintName; + this.rely = rely; + } + } + + /** + * Get the primary keys from the AST and populate the pkInfos with the required + * information. + * @param child The node with primary key token + * @param pkInfos Primary Key information structure + * @throws SemanticException + */ + private static void processPrimaryKeyInfos( + ASTNode child, List pkInfos) throws SemanticException { + if (child.getChildCount() < 6) { + throw new SemanticException(ErrorMsg.INVALID_PK_SYNTAX.getMsg()); + } + // The ANTLR grammar looks like : + // 1. KW_CONSTRAINT idfr=identifier KW_PRIMARY idfr2=identifier pkCols=columnParenthesesList + // relySpec=relySpecification enableSpec=enableSpecification validateSpec=validateSpecification + // -> {$idfr2.tree.toString().equalsIgnoreCase("key")}? + // ^(TOK_PRIMARY_KEY $pkCols $idfr $relySpec $enableSpec $validateSpec) + // -> ^(TOK_PRIMARY_KEY) + // when the user specifies the constraint name (i.e. child.getChildCount() == 7) + // 2. KW_PRIMARY idfr=identifier columnParenthesesList relySpec=relySpecification + // enableSpec=enableSpecification validateSpec=validateSpecification + // -> {$idfr.tree.toString().equalsIgnoreCase("key")}? + // ^(TOK_PRIMARY_KEY columnParenthesesList $relySpec $enableSpec $validateSpec) + // -> ^(TOK_PRIMARY_KEY) + // when the user does not specify the constraint name (i.e. child.getChildCount() == 6) + boolean userSpecifiedConstraintName = child.getChildCount() == 7; + int relyIndex = child.getChildCount() == 7 ? 4 : 3; + for (int j = 0; j < child.getChild(1).getChildCount(); j++) { + Tree grandChild = child.getChild(1).getChild(j); + boolean rely = child.getChild(relyIndex).getType() == HiveParser.TOK_VALIDATE; + boolean enable = child.getChild(relyIndex+1).getType() == HiveParser.TOK_ENABLE; + boolean validate = child.getChild(relyIndex+2).getType() == HiveParser.TOK_VALIDATE; + if (enable) { + throw new SemanticException( + ErrorMsg.INVALID_PK_SYNTAX.getMsg(" ENABLE feature not supported yet")); + } + if (validate) { + throw new SemanticException( + ErrorMsg.INVALID_PK_SYNTAX.getMsg(" VALIDATE feature not supported yet")); + } + pkInfos.add( + new PKInfo( + unescapeIdentifier(grandChild.getText().toLowerCase()), + (userSpecifiedConstraintName ? + unescapeIdentifier(child.getChild(3).getText().toLowerCase()) : null), + rely)); + } + } + + /** + * Process the primary keys from the pkInfos structure and populate the SQLPrimaryKey list + * @param parent Parent of the primary key token node + * @param pkInfos primary key information + * @param primaryKeys SQLPrimaryKey list + * @param nametoFS Mapping from column name to field schema for the current table + * @throws SemanticException + */ + private static void processPrimaryKeys(ASTNode parent, List pkInfos, + List primaryKeys, Map nametoFS) throws SemanticException { + int cnt = 1; + String[] qualifiedTabName = getQualifiedTableName((ASTNode) parent.getChild(0)); + + for (int i = 0; i < pkInfos.size(); i++) { + String pk = pkInfos.get(i).colName; + if (nametoFS.containsKey(pk)) { + SQLPrimaryKey currPrimaryKey = new SQLPrimaryKey( + qualifiedTabName[0], qualifiedTabName[1], pk, cnt++, pkInfos.get(i).constraintName, + false, false, pkInfos.get(i).rely); + primaryKeys.add(currPrimaryKey); + } else { + throw new SemanticException(ErrorMsg.INVALID_COLUMN.getMsg(pk)); + } + } + } + + /** + * Process the foreign keys from the AST and populate the foreign keys in the SQLForeignKey list + * @param parent Parent of the foreign key token node + * @param child Foreign Key token node + * @param foreignKeys SQLForeignKey list + * @throws SemanticException + */ + private static void processForeignKeys( + ASTNode parent, ASTNode child, List foreignKeys) throws SemanticException { + String[] qualifiedTabName = getQualifiedTableName((ASTNode) parent.getChild(0)); + // The ANTLR grammar looks like : + // 1. KW_CONSTRAINT idfr=identifier KW_FOREIGN idfr2=identifier fkCols=columnParenthesesList + // KW_REFERENCES tabName=tableName parCols=columnParenthesesList + // relySpec=relySpecification enableSpec=enableSpecification validateSpec=validateSpecification + // -> {$idfr2.tree.toString().equalsIgnoreCase("key")}? + // ^(TOK_FOREIGN_KEY $idfr $fkCols $tabName $parCols $relySpec $enableSpec $validateSpec) + // -> ^(TOK_FOREIGN_KEY) + // when the user specifies the constraint name (i.e. child.getChildCount() == 11) + // 2. KW_FOREIGN idfr=identifier fkCols=columnParenthesesList + // KW_REFERENCES tabName=tableName parCols=columnParenthesesList relySpec=relySpecification + // enableSpec=enableSpecification validateSpec=validateSpecification + // -> {$idfr.tree.toString().equalsIgnoreCase("key")}? + // ^(TOK_FOREIGN_KEY $fkCols $tabName $parCols $relySpec $enableSpec $validateSpec) + // -> ^(TOK_FOREIGN_KEY) + // when the user does not specify the constraint name (i.e. child.getChildCount() == 10) + boolean userSpecifiedConstraintName = child.getChildCount() == 11; + int fkIndex = userSpecifiedConstraintName ? 2 : 1; + int pkIndex = userSpecifiedConstraintName ? 6 : 5; + int ptIndex = userSpecifiedConstraintName ? 4 : 3; + int relyIndex = child.getChildCount() == 11 ? 8 : 7; + + if (child.getChildCount() <= fkIndex ||child.getChildCount() <= pkIndex || + child.getChildCount() <= ptIndex) { + throw new SemanticException(ErrorMsg.INVALID_FK_SYNTAX.getMsg()); + } + + String[] parentDBTbl = getQualifiedTableName((ASTNode) child.getChild(ptIndex)); + + if (child.getChild(fkIndex).getChildCount() != child.getChild(pkIndex).getChildCount()) { + throw new SemanticException(ErrorMsg.INVALID_FK_SYNTAX.getMsg( + " The number of foreign key columns should be same as number of parent key columns ")); + } + for (int j = 0; j < child.getChild(fkIndex).getChildCount(); j++) { + SQLForeignKey sqlForeignKey = new SQLForeignKey(); + Tree fkgrandChild = child.getChild(fkIndex).getChild(j); + boolean rely = child.getChild(relyIndex).getType() == HiveParser.TOK_VALIDATE; + boolean enable = child.getChild(relyIndex+1).getType() == HiveParser.TOK_ENABLE; + boolean validate = child.getChild(relyIndex+2).getType() == HiveParser.TOK_VALIDATE; + if (enable) { + throw new SemanticException( + ErrorMsg.INVALID_FK_SYNTAX.getMsg(" ENABLE feature not supported yet")); + } + if (validate) { + throw new SemanticException( + ErrorMsg.INVALID_FK_SYNTAX.getMsg(" VALIDATE feature not supported yet")); + } + sqlForeignKey.setRely_cstr(rely); + sqlForeignKey.setPktable_db(parentDBTbl[0]); + sqlForeignKey.setPktable_name(parentDBTbl[1]); + sqlForeignKey.setFktable_db(qualifiedTabName[0]); + sqlForeignKey.setFktable_name(qualifiedTabName[1]); + sqlForeignKey.setFkcolumn_name(unescapeIdentifier(fkgrandChild.getText().toLowerCase())); + Tree pkgrandChild = child.getChild(pkIndex).getChild(j); + sqlForeignKey.setPkcolumn_name(unescapeIdentifier(pkgrandChild.getText().toLowerCase())); + sqlForeignKey.setKey_seq(j+1); + if (userSpecifiedConstraintName) { + sqlForeignKey.setFk_name(unescapeIdentifier(child.getChild(0).getText().toLowerCase())); + } + foreignKeys.add(sqlForeignKey); + } + } + + /** + * Get the list of FieldSchema out of the ASTNode. + * Additionally, populate the primaryKeys and foreignKeys if any. + */ + public static List getColumns(ASTNode ast, boolean lowerCase, + List primaryKeys, List foreignKeys) throws SemanticException { List colList = new ArrayList(); int numCh = ast.getChildCount(); + List pkInfos = new ArrayList(); + Map nametoFS = new HashMap(); + Tree parent = ast.getParent(); + for (int i = 0; i < numCh; i++) { FieldSchema col = new FieldSchema(); ASTNode child = (ASTNode) ast.getChild(i); - Tree grandChild = child.getChild(0); - if(grandChild != null) { - String name = grandChild.getText(); - if(lowerCase) { - name = name.toLowerCase(); - } - // child 0 is the name of the column - col.setName(unescapeIdentifier(name)); - // child 1 is the type of the column - ASTNode typeChild = (ASTNode) (child.getChild(1)); - col.setType(getTypeStringFromAST(typeChild)); - - // child 2 is the optional comment of the column - if (child.getChildCount() == 3) { - col.setComment(unescapeSQLString(child.getChild(2).getText())); + if (child.getToken().getType() == HiveParser.TOK_PRIMARY_KEY) { + processPrimaryKeyInfos(child, pkInfos); + } else if (child.getToken().getType() == HiveParser.TOK_FOREIGN_KEY) { + processForeignKeys((ASTNode)parent, child, foreignKeys); + } + else { + Tree grandChild = child.getChild(0); + if(grandChild != null) { + String name = grandChild.getText(); + if(lowerCase) { + name = name.toLowerCase(); + } + // child 0 is the name of the column + col.setName(unescapeIdentifier(name)); + // child 1 is the type of the column + ASTNode typeChild = (ASTNode) (child.getChild(1)); + col.setType(getTypeStringFromAST(typeChild)); + + // child 2 is the optional comment of the column + if (child.getChildCount() == 3) { + col.setComment(unescapeSQLString(child.getChild(2).getText())); + } } + nametoFS.put(col.getName(), col); + colList.add(col); } - colList.add(col); + } + if (!pkInfos.isEmpty()) { + processPrimaryKeys((ASTNode) parent, pkInfos, primaryKeys, nametoFS); } return colList; } diff --git a/ql/src/java/org/apache/hadoop/hive/ql/parse/HiveParser.g b/ql/src/java/org/apache/hadoop/hive/ql/parse/HiveParser.g index 50c53db..d010d74 100644 --- a/ql/src/java/org/apache/hadoop/hive/ql/parse/HiveParser.g +++ b/ql/src/java/org/apache/hadoop/hive/ql/parse/HiveParser.g @@ -102,6 +102,13 @@ TOK_METADATA; TOK_NULL; TOK_ISNULL; TOK_ISNOTNULL; +TOK_PRIMARY_KEY; +TOK_FOREIGN_KEY; +TOK_VALIDATE; +TOK_NOVALIDATE; +TOK_RELY; +TOK_NORELY; +TOK_INVALID_KEY_SPEC; TOK_TINYINT; TOK_SMALLINT; TOK_INT; @@ -516,7 +523,6 @@ import org.apache.hadoop.hive.conf.HiveConf; xlateMap.put("KW_VALUES", "VALUES"); xlateMap.put("KW_PURGE", "PURGE"); - // Operators xlateMap.put("DOT", "."); xlateMap.put("COLON", ":"); @@ -890,7 +896,7 @@ createTableStatement tableFileFormat? tableLocation? tablePropertiesPrefixed? - | (LPAREN columnNameTypeList RPAREN)? + | (LPAREN columnNameTypeOrPKOrFKList RPAREN)? tableComment? tablePartition? tableBuckets? @@ -903,7 +909,7 @@ createTableStatement ) -> ^(TOK_CREATETABLE $name $temp? $ext? ifNotExists? ^(TOK_LIKETABLE $likeName?) - columnNameTypeList? + columnNameTypeOrPKOrFKList? tableComment? tablePartition? tableBuckets? @@ -1943,6 +1949,11 @@ columnNameTypeList @after { popMsg(state); } : columnNameType (COMMA columnNameType)* -> ^(TOK_TABCOLLIST columnNameType+) ; +columnNameTypeOrPKOrFKList +@init { pushMsg("column name type list with PK and FK", state); } +@after { popMsg(state); } + : columnNameTypeOrPKOrFK (COMMA columnNameTypeOrPKOrFK)* -> ^(TOK_TABCOLLIST columnNameTypeOrPKOrFK+) + ; columnNameColonTypeList @init { pushMsg("column name type list", state); } @@ -1976,6 +1987,71 @@ columnNameOrderList : columnNameOrder (COMMA columnNameOrder)* -> ^(TOK_TABCOLNAME columnNameOrder+) ; +columnParenthesesList +@init { pushMsg("column parentheses list", state); } +@after { popMsg(state); } + : LPAREN columnNameList RPAREN + ; + +enableValidateRelySpecification +@init { pushMsg("enable rely validate specification", state); } +@after { popMsg(state); } + : enableSpec=identifier validateSpec=identifier relySpec=identifier? + -> {$relySpec.tree != null && $relySpec.tree.toString().equalsIgnoreCase("rely") && $enableSpec.tree.toString().equalsIgnoreCase("enable") && $validateSpec.tree.toString().equalsIgnoreCase("validate")}? + ^(TOK_RELY) ^(TOK_ENABLE) ^(TOK_VALIDATE) + -> {$relySpec.tree != null && $relySpec.tree.toString().equalsIgnoreCase("rely") && $enableSpec.tree.toString().equalsIgnoreCase("disable") && $validateSpec.tree.toString().equalsIgnoreCase("validate")}? + ^(TOK_RELY) ^(TOK_DISABLE) ^(TOK_VALIDATE) + -> {$relySpec.tree != null && $relySpec.tree.toString().equalsIgnoreCase("rely") && $enableSpec.tree.toString().equalsIgnoreCase("enable") && $validateSpec.tree.toString().equalsIgnoreCase("novalidate")}? + ^(TOK_RELY) ^(TOK_ENABLE) ^(TOK_NOVALIDATE) + -> {$relySpec.tree != null && $relySpec.tree.toString().equalsIgnoreCase("rely") && $enableSpec.tree.toString().equalsIgnoreCase("disable") && $validateSpec.tree.toString().equalsIgnoreCase("novalidate")}? + ^(TOK_RELY) ^(TOK_DISABLE) ^(TOK_NOVALIDATE) + -> {(($relySpec.tree != null && $relySpec.tree.toString().equalsIgnoreCase("norely")) || $relySpec.tree == null) && $enableSpec.tree.toString().equalsIgnoreCase("enable") && $validateSpec.tree.toString().equalsIgnoreCase("validate")}? + ^(TOK_NORELY) ^(TOK_ENABLE) ^(TOK_VALIDATE) + -> {(($relySpec.tree != null && $relySpec.tree.toString().equalsIgnoreCase("norely")) || $relySpec.tree == null) && $enableSpec.tree.toString().equalsIgnoreCase("disable") && $validateSpec.tree.toString().equalsIgnoreCase("validate")}? + ^(TOK_NORELY) ^(TOK_DISABLE) ^(TOK_VALIDATE) + -> {(($relySpec.tree != null && $relySpec.tree.toString().equalsIgnoreCase("norely")) || $relySpec.tree == null) && $enableSpec.tree.toString().equalsIgnoreCase("enable") && $validateSpec.tree.toString().equalsIgnoreCase("novalidate")}? + ^(TOK_NORELY) ^(TOK_ENABLE) ^(TOK_NOVALIDATE) + -> {(($relySpec.tree != null && $relySpec.tree.toString().equalsIgnoreCase("norely")) || $relySpec.tree == null) && $enableSpec.tree.toString().equalsIgnoreCase("disable") && $validateSpec.tree.toString().equalsIgnoreCase("novalidate")}? + ^(TOK_NORELY) ^(TOK_DISABLE) ^(TOK_NOVALIDATE) + -> ^(TOK_INVALID_KEY_SPEC) + ; + +primaryKeyWithoutName +@init { pushMsg("primary key without key name", state); } +@after { popMsg(state); } + : pkidfr=identifier idfr=identifier columnParenthesesList evrSpec=enableValidateRelySpecification + -> {$pkidfr.tree.toString().equalsIgnoreCase("primary") && $idfr.tree.toString().equalsIgnoreCase("key")}? + ^(TOK_PRIMARY_KEY columnParenthesesList $evrSpec) + -> ^(TOK_PRIMARY_KEY) + ; + +primaryKeyWithName +@init { pushMsg("primary key with key name", state); } +@after { popMsg(state); } + : cidfr=identifier idfr=identifier pkidfr=identifier idfr2=identifier pkCols=columnParenthesesList evrSpec=enableValidateRelySpecification + -> { $cidfr.tree.toString().equalsIgnoreCase("constraint") && $pkidfr.tree.toString().equalsIgnoreCase("primary") && $idfr2.tree.toString().equalsIgnoreCase("key")}? + ^(TOK_PRIMARY_KEY $pkCols $idfr $evrSpec) + -> ^(TOK_PRIMARY_KEY) + ; + +foreignKeyWithName +@init { pushMsg("foreign key with key name", state); } +@after { popMsg(state); } + : cidfr=identifier idfr=identifier fidfr=identifier idfr2=identifier fkCols=columnParenthesesList ridfr=identifier tabName=tableName parCols=columnParenthesesList evrSpec=enableValidateRelySpecification + -> { $cidfr.tree.toString().equalsIgnoreCase("constraint") && $fidfr.tree.toString().equalsIgnoreCase("foreign") && $ridfr.tree.toString().equalsIgnoreCase("references") && $idfr2.tree.toString().equalsIgnoreCase("key")}? + ^(TOK_FOREIGN_KEY $idfr $fkCols $tabName $parCols $evrSpec) + -> ^(TOK_FOREIGN_KEY) + ; + +foreignKeyWithoutName +@init { pushMsg("foreign key without key name", state); } +@after { popMsg(state); } + : fidfr=identifier idfr=identifier fkCols=columnParenthesesList ridfr=identifier tabName=tableName parCols=columnParenthesesList evrSpec=enableValidateRelySpecification + -> {$fidfr.tree.toString().equalsIgnoreCase("foreign") && $ridfr.tree.toString().equalsIgnoreCase("references") && $idfr.tree.toString().equalsIgnoreCase("key")}? + ^(TOK_FOREIGN_KEY $fkCols $tabName $parCols $evrSpec) + -> ^(TOK_FOREIGN_KEY) + ; + skewedValueElement @init { pushMsg("skewed value element", state); } @after { popMsg(state); } @@ -2087,6 +2163,16 @@ columnNameType -> ^(TOK_TABCOL $colName colType $comment) ; +columnNameTypeOrPKOrFK +@init { pushMsg("column name or primary key or foreign key", state); } +@after { popMsg(state); } + : ( foreignKeyWithName ) + | ( primaryKeyWithName ) + | ( primaryKeyWithoutName ) + | ( foreignKeyWithoutName ) + | ( columnNameType ) + ; + columnNameColonType @init { pushMsg("column specification", state); } @after { popMsg(state); } diff --git a/ql/src/java/org/apache/hadoop/hive/ql/parse/ImportSemanticAnalyzer.java b/ql/src/java/org/apache/hadoop/hive/ql/parse/ImportSemanticAnalyzer.java index 549d24f..374ddb1 100644 --- a/ql/src/java/org/apache/hadoop/hive/ql/parse/ImportSemanticAnalyzer.java +++ b/ql/src/java/org/apache/hadoop/hive/ql/parse/ImportSemanticAnalyzer.java @@ -312,7 +312,7 @@ private CreateTableDesc getBaseCreateTableDescFromTable(String dbName, (null == table.getSd().getSkewedInfo()) ? null : table.getSd().getSkewedInfo() .getSkewedColNames(), (null == table.getSd().getSkewedInfo()) ? null : table.getSd().getSkewedInfo() - .getSkewedColValues()); + .getSkewedColValues(), null, null); tblDesc.setStoredAsSubDirectories(table.getSd().isStoredAsSubDirectories()); return tblDesc; } diff --git a/ql/src/java/org/apache/hadoop/hive/ql/parse/SemanticAnalyzer.java b/ql/src/java/org/apache/hadoop/hive/ql/parse/SemanticAnalyzer.java index 96df189..5327fd0 100644 --- a/ql/src/java/org/apache/hadoop/hive/ql/parse/SemanticAnalyzer.java +++ b/ql/src/java/org/apache/hadoop/hive/ql/parse/SemanticAnalyzer.java @@ -68,6 +68,8 @@ import org.apache.hadoop.hive.metastore.api.FieldSchema; import org.apache.hadoop.hive.metastore.api.MetaException; import org.apache.hadoop.hive.metastore.api.Order; +import org.apache.hadoop.hive.metastore.api.SQLForeignKey; +import org.apache.hadoop.hive.metastore.api.SQLPrimaryKey; import org.apache.hadoop.hive.ql.CompilationOpContext; import org.apache.hadoop.hive.ql.Context; import org.apache.hadoop.hive.ql.ErrorMsg; @@ -11293,6 +11295,8 @@ ASTNode analyzeCreateTable( List cols = new ArrayList(); List partCols = new ArrayList(); List bucketCols = new ArrayList(); + List primaryKeys = new ArrayList(); + List foreignKeys = new ArrayList(); List sortCols = new ArrayList(); int numBuckets = -1; String comment = null; @@ -11385,7 +11389,7 @@ ASTNode analyzeCreateTable( selectStmt = child; break; case HiveParser.TOK_TABCOLLIST: - cols = getColumns(child); + cols = getColumns(child, true, primaryKeys, foreignKeys); break; case HiveParser.TOK_TABLECOMMENT: comment = unescapeSQLString(child.getChild(0).getText()); @@ -11495,7 +11499,7 @@ ASTNode analyzeCreateTable( comment, storageFormat.getInputFormat(), storageFormat.getOutputFormat(), location, storageFormat.getSerde(), storageFormat.getStorageHandler(), storageFormat.getSerdeProps(), tblProps, ifNotExists, skewedColNames, - skewedValues); + skewedValues, primaryKeys, foreignKeys); crtTblDesc.setStoredAsSubDirectories(storedAsDirs); crtTblDesc.setNullFormat(rowFormatParams.nullFormat); @@ -11589,7 +11593,7 @@ ASTNode analyzeCreateTable( rowFormatParams.lineDelim, comment, storageFormat.getInputFormat(), storageFormat.getOutputFormat(), location, storageFormat.getSerde(), storageFormat.getStorageHandler(), storageFormat.getSerdeProps(), tblProps, ifNotExists, - skewedColNames, skewedValues, true); + skewedColNames, skewedValues, true, primaryKeys, foreignKeys); tableDesc.setMaterialization(isMaterialization); tableDesc.setStoredAsSubDirectories(storedAsDirs); tableDesc.setNullFormat(rowFormatParams.nullFormat); diff --git a/ql/src/java/org/apache/hadoop/hive/ql/plan/CreateTableDesc.java b/ql/src/java/org/apache/hadoop/hive/ql/plan/CreateTableDesc.java index 03b4d8b..2dc4e11 100644 --- a/ql/src/java/org/apache/hadoop/hive/ql/plan/CreateTableDesc.java +++ b/ql/src/java/org/apache/hadoop/hive/ql/plan/CreateTableDesc.java @@ -28,6 +28,8 @@ import org.apache.hadoop.hive.conf.HiveConf; import org.apache.hadoop.hive.metastore.TableType; import org.apache.hadoop.hive.metastore.api.FieldSchema; +import org.apache.hadoop.hive.metastore.api.SQLPrimaryKey; +import org.apache.hadoop.hive.metastore.api.SQLForeignKey; import org.apache.hadoop.hive.metastore.api.Order; import org.apache.hadoop.hive.ql.ErrorMsg; import org.apache.hadoop.hive.ql.exec.DDLTask; @@ -88,6 +90,8 @@ private boolean isMaterialization = false; private boolean replaceMode = false; private boolean isCTAS = false; + List primaryKeys; + List foreignKeys; public CreateTableDesc() { } @@ -101,13 +105,14 @@ public CreateTableDesc(String databaseName, String tableName, boolean isExternal String storageHandler, Map serdeProps, Map tblProps, - boolean ifNotExists, List skewedColNames, List> skewedColValues) { + boolean ifNotExists, List skewedColNames, List> skewedColValues, + List primaryKeys, List foreignKeys) { this(tableName, isExternal, isTemporary, cols, partCols, bucketCols, sortCols, numBuckets, fieldDelim, fieldEscape, collItemDelim, mapKeyDelim, lineDelim, comment, inputFormat, outputFormat, location, serName, storageHandler, serdeProps, - tblProps, ifNotExists, skewedColNames, skewedColValues); + tblProps, ifNotExists, skewedColNames, skewedColValues, primaryKeys, foreignKeys); this.databaseName = databaseName; } @@ -122,12 +127,12 @@ public CreateTableDesc(String databaseName, String tableName, boolean isExternal Map serdeProps, Map tblProps, boolean ifNotExists, List skewedColNames, List> skewedColValues, - boolean isCTAS) { + boolean isCTAS, List primaryKeys, List foreignKeys) { this(databaseName, tableName, isExternal, isTemporary, cols, partCols, bucketCols, sortCols, numBuckets, fieldDelim, fieldEscape, collItemDelim, mapKeyDelim, lineDelim, comment, inputFormat, outputFormat, location, serName, storageHandler, serdeProps, - tblProps, ifNotExists, skewedColNames, skewedColValues); + tblProps, ifNotExists, skewedColNames, skewedColValues, primaryKeys, foreignKeys); this.isCTAS = isCTAS; } @@ -142,7 +147,8 @@ public CreateTableDesc(String tableName, boolean isExternal, boolean isTemporary String storageHandler, Map serdeProps, Map tblProps, - boolean ifNotExists, List skewedColNames, List> skewedColValues) { + boolean ifNotExists, List skewedColNames, List> skewedColValues, + List primaryKeys, List foreignKeys) { this.tableName = tableName; this.isExternal = isExternal; this.isTemporary = isTemporary; @@ -167,6 +173,16 @@ public CreateTableDesc(String tableName, boolean isExternal, boolean isTemporary this.ifNotExists = ifNotExists; this.skewedColNames = copyList(skewedColNames); this.skewedColValues = copyList(skewedColValues); + if (primaryKeys == null) { + this.primaryKeys = new ArrayList(); + } else { + this.primaryKeys = new ArrayList(primaryKeys); + } + if (foreignKeys == null) { + this.foreignKeys = new ArrayList(); + } else { + this.foreignKeys = new ArrayList(foreignKeys); + } } private static List copyList(List copy) { @@ -221,6 +237,22 @@ public void setPartCols(ArrayList partCols) { this.partCols = partCols; } + public List getPrimaryKeys() { + return primaryKeys; + } + + public void setPrimaryKeys(ArrayList primaryKeys) { + this.primaryKeys = primaryKeys; + } + + public List getForeignKeys() { + return foreignKeys; + } + + public void setForeignKeys(ArrayList foreignKeys) { + this.foreignKeys = foreignKeys; + } + @Explain(displayName = "bucket columns") public List getBucketCols() { return bucketCols; @@ -634,6 +666,7 @@ public Table toTable(HiveConf conf) throws HiveException { if (getPartCols() != null) { tbl.setPartCols(getPartCols()); } + if (getNumBuckets() != -1) { tbl.setNumBuckets(getNumBuckets()); }