/* * 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.sql.dialect.mysql.parser; import com.alibaba.druid.sql.ast.SQLDataTypeImpl; import com.alibaba.druid.sql.ast.SQLExpr; import com.alibaba.druid.sql.ast.SQLName; import com.alibaba.druid.sql.ast.SQLOrderingSpecification; import com.alibaba.druid.sql.ast.SQLPartition; import com.alibaba.druid.sql.ast.SQLPartitionBy; import com.alibaba.druid.sql.ast.SQLPartitionByHash; import com.alibaba.druid.sql.ast.SQLPartitionByList; import com.alibaba.druid.sql.ast.SQLPartitionByRange; import com.alibaba.druid.sql.ast.SQLSubPartitionBy; import com.alibaba.druid.sql.ast.SQLSubPartitionByHash; import com.alibaba.druid.sql.ast.expr.SQLIdentifierExpr; import com.alibaba.druid.sql.ast.expr.SQLIntegerExpr; import com.alibaba.druid.sql.ast.expr.SQLNumberExpr; import com.alibaba.druid.sql.ast.statement.SQLAssignItem; import com.alibaba.druid.sql.ast.statement.SQLCheck; import com.alibaba.druid.sql.ast.statement.SQLColumnDefinition; import com.alibaba.druid.sql.ast.statement.SQLCreateTableStatement; import com.alibaba.druid.sql.ast.statement.SQLForeignKeyConstraint; import com.alibaba.druid.sql.ast.statement.SQLSelect; import com.alibaba.druid.sql.ast.statement.SQLTableConstraint; import com.alibaba.druid.sql.ast.statement.SQLTableSource; import com.alibaba.druid.sql.dialect.mysql.ast.MySqlKey; import com.alibaba.druid.sql.dialect.mysql.ast.MySqlPrimaryKey; import com.alibaba.druid.sql.dialect.mysql.ast.MySqlUnique; import com.alibaba.druid.sql.dialect.mysql.ast.MysqlForeignKey; import com.alibaba.druid.sql.dialect.mysql.ast.expr.MySqlOrderingExpr; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlCreateTableStatement; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlCreateTableStatement.TableSpaceOption; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlPartitionByKey; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlSubPartitionByKey; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlSubPartitionByList; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlTableIndex; import com.alibaba.druid.sql.parser.ParserException; import com.alibaba.druid.sql.parser.SQLCreateTableParser; import com.alibaba.druid.sql.parser.SQLExprParser; import com.alibaba.druid.sql.parser.Token; public class MySqlCreateTableParser extends SQLCreateTableParser { public MySqlCreateTableParser(String sql){ super(new MySqlExprParser(sql)); } public MySqlCreateTableParser(SQLExprParser exprParser){ super(exprParser); } public SQLCreateTableStatement parseCrateTable() { return parseCrateTable(true); } public MySqlExprParser getExprParser() { return (MySqlExprParser) exprParser; } public MySqlCreateTableStatement parseCrateTable(boolean acceptCreate) { if (acceptCreate) { accept(Token.CREATE); } MySqlCreateTableStatement stmt = new MySqlCreateTableStatement(); if (identifierEquals("TEMPORARY")) { lexer.nextToken(); stmt.setType(SQLCreateTableStatement.Type.GLOBAL_TEMPORARY); } accept(Token.TABLE); if (lexer.token() == Token.IF || identifierEquals("IF")) { lexer.nextToken(); accept(Token.NOT); accept(Token.EXISTS); stmt.setIfNotExiists(true); } stmt.setName(this.exprParser.name()); if (lexer.token() == Token.LIKE) { lexer.nextToken(); SQLName name = this.exprParser.name(); stmt.setLike(name); } if (lexer.token() == (Token.LPAREN)) { lexer.nextToken(); if (lexer.token() == Token.LIKE) { lexer.nextToken(); SQLName name = this.exprParser.name(); stmt.setLike(name); } else { for (;;) { if (lexer.token() == Token.IDENTIFIER // || lexer.token() == Token.LITERAL_CHARS) { SQLColumnDefinition column = this.exprParser.parseColumn(); stmt.getTableElementList().add(column); } else if (lexer.token() == Token.CONSTRAINT // || lexer.token() == Token.PRIMARY // || lexer.token() == Token.UNIQUE) { SQLTableConstraint constraint = this.parseConstraint(); stmt.getTableElementList().add(constraint); } else if (lexer.token() == (Token.INDEX)) { lexer.nextToken(); MySqlTableIndex idx = new MySqlTableIndex(); if (lexer.token() == Token.IDENTIFIER) { if (!"USING".equalsIgnoreCase(lexer.stringVal())) { idx.setName(this.exprParser.name()); } } if (identifierEquals("USING")) { lexer.nextToken(); idx.setIndexType(lexer.stringVal()); lexer.nextToken(); } accept(Token.LPAREN); for (;;) { idx.addColumn(this.exprParser.expr()); if (!(lexer.token() == (Token.COMMA))) { break; } else { lexer.nextToken(); } } accept(Token.RPAREN); if (identifierEquals("USING")) { lexer.nextToken(); idx.setIndexType(lexer.stringVal()); lexer.nextToken(); } stmt.getTableElementList().add(idx); } else if (lexer.token() == (Token.KEY)) { stmt.getTableElementList().add(parseConstraint()); } else if (lexer.token() == (Token.PRIMARY)) { SQLTableConstraint pk = parseConstraint(); pk.setParent(stmt); stmt.getTableElementList().add(pk); } else if (lexer.token() == (Token.FOREIGN)) { SQLForeignKeyConstraint fk = this.getExprParser().parseForeignKey(); fk.setParent(stmt); stmt.getTableElementList().add(fk); } else if (lexer.token() == Token.CHECK) { SQLCheck check = this.exprParser.parseCheck(); stmt.getTableElementList().add(check); } else { SQLColumnDefinition column = this.exprParser.parseColumn(); stmt.getTableElementList().add(column); } if (!(lexer.token() == (Token.COMMA))) { break; } else { lexer.nextToken(); } } } accept(Token.RPAREN); } for (;;) { if (identifierEquals("ENGINE")) { lexer.nextToken(); if (lexer.token() == Token.EQ) { lexer.nextToken(); } stmt.getTableOptions().put("ENGINE", this.exprParser.expr()); continue; } if (identifierEquals("AUTO_INCREMENT")) { lexer.nextToken(); if (lexer.token() == Token.EQ) { lexer.nextToken(); } stmt.getTableOptions().put("AUTO_INCREMENT", this.exprParser.expr()); continue; } if (identifierEquals("AVG_ROW_LENGTH")) { lexer.nextToken(); if (lexer.token() == Token.EQ) { lexer.nextToken(); } stmt.getTableOptions().put("AVG_ROW_LENGTH", this.exprParser.expr()); continue; } if (lexer.token() == Token.DEFAULT) { lexer.nextToken(); parseTableOptionCharsetOrCollate(stmt); continue; } if (parseTableOptionCharsetOrCollate(stmt)) { continue; } if (identifierEquals("CHECKSUM")) { lexer.nextToken(); if (lexer.token() == Token.EQ) { lexer.nextToken(); } stmt.getTableOptions().put("CHECKSUM", this.exprParser.expr()); continue; } if (lexer.token() == Token.COMMENT) { lexer.nextToken(); if (lexer.token() == Token.EQ) { lexer.nextToken(); } stmt.getTableOptions().put("COMMENT", this.exprParser.expr()); continue; } if (identifierEquals("CONNECTION")) { lexer.nextToken(); if (lexer.token() == Token.EQ) { lexer.nextToken(); } stmt.getTableOptions().put("CONNECTION", this.exprParser.expr()); continue; } if (identifierEquals("DATA")) { lexer.nextToken(); acceptIdentifier("DIRECTORY"); if (lexer.token() == Token.EQ) { lexer.nextToken(); } stmt.getTableOptions().put("DATA DIRECTORY", this.exprParser.expr()); continue; } if (identifierEquals("DELAY_KEY_WRITE")) { lexer.nextToken(); if (lexer.token() == Token.EQ) { lexer.nextToken(); } stmt.getTableOptions().put("DELAY_KEY_WRITE", this.exprParser.expr()); continue; } if (identifierEquals("INDEX")) { lexer.nextToken(); acceptIdentifier("DIRECTORY"); if (lexer.token() == Token.EQ) { lexer.nextToken(); } stmt.getTableOptions().put("INDEX DIRECTORY", this.exprParser.expr()); continue; } if (identifierEquals("INSERT_METHOD")) { lexer.nextToken(); if (lexer.token() == Token.EQ) { lexer.nextToken(); } stmt.getTableOptions().put("INSERT_METHOD", this.exprParser.expr()); continue; } if (identifierEquals("KEY_BLOCK_SIZE")) { lexer.nextToken(); if (lexer.token() == Token.EQ) { lexer.nextToken(); } stmt.getTableOptions().put("KEY_BLOCK_SIZE", this.exprParser.expr()); continue; } if (identifierEquals("MAX_ROWS")) { lexer.nextToken(); if (lexer.token() == Token.EQ) { lexer.nextToken(); } stmt.getTableOptions().put("MAX_ROWS", this.exprParser.expr()); continue; } if (identifierEquals("MIN_ROWS")) { lexer.nextToken(); if (lexer.token() == Token.EQ) { lexer.nextToken(); } stmt.getTableOptions().put("MIN_ROWS", this.exprParser.expr()); continue; } if (identifierEquals("PACK_KEYS")) { lexer.nextToken(); if (lexer.token() == Token.EQ) { lexer.nextToken(); } stmt.getTableOptions().put("PACK_KEYS", this.exprParser.expr()); continue; } if (identifierEquals("PASSWORD")) { lexer.nextToken(); if (lexer.token() == Token.EQ) { lexer.nextToken(); } stmt.getTableOptions().put("PASSWORD", this.exprParser.expr()); continue; } if (identifierEquals("ROW_FORMAT")) { lexer.nextToken(); if (lexer.token() == Token.EQ) { lexer.nextToken(); } stmt.getTableOptions().put("ROW_FORMAT", this.exprParser.expr()); continue; } if (identifierEquals("STATS_AUTO_RECALC")) { lexer.nextToken(); if (lexer.token() == Token.EQ) { lexer.nextToken(); } stmt.getTableOptions().put("STATS_AUTO_RECALC", this.exprParser.expr()); continue; } if (identifierEquals("STATS_PERSISTENT")) { lexer.nextToken(); if (lexer.token() == Token.EQ) { lexer.nextToken(); } stmt.getTableOptions().put("STATS_PERSISTENT", this.exprParser.expr()); continue; } if (identifierEquals("STATS_SAMPLE_PAGES")) { lexer.nextToken(); if (lexer.token() == Token.EQ) { lexer.nextToken(); } stmt.getTableOptions().put("STATS_SAMPLE_PAGES", this.exprParser.expr()); continue; } if (lexer.token() == Token.UNION) { lexer.nextToken(); if (lexer.token() == Token.EQ) { lexer.nextToken(); } accept(Token.LPAREN); SQLTableSource tableSrc = this.createSQLSelectParser().parseTableSource(); stmt.getTableOptions().put("UNION", tableSrc); accept(Token.RPAREN); continue; } if (lexer.token() == Token.TABLESPACE) { lexer.nextToken(); TableSpaceOption option = new TableSpaceOption(); option.setName(this.exprParser.name()); if (identifierEquals("STORAGE")) { lexer.nextToken(); option.setStorage(this.exprParser.name()); } stmt.getTableOptions().put("TABLESPACE", option); continue; } if (identifierEquals("TABLEGROUP")) { lexer.nextToken(); SQLName tableGroup = this.exprParser.name(); stmt.setTableGroup(tableGroup); continue; } if (identifierEquals("TYPE")) { lexer.nextToken(); accept(Token.EQ); stmt.getTableOptions().put("TYPE", this.exprParser.expr()); lexer.nextToken(); continue; } if (lexer.token() == Token.PARTITION) { lexer.nextToken(); accept(Token.BY); SQLPartitionBy partitionClause; boolean linera = false; if (identifierEquals("LINEAR")) { lexer.nextToken(); linera = true; } if (lexer.token() == Token.KEY) { MySqlPartitionByKey clause = new MySqlPartitionByKey(); lexer.nextToken(); if (linera) { clause.setLinear(true); } accept(Token.LPAREN); if (lexer.token() != Token.RPAREN) { for (;;) { clause.addColumn(this.exprParser.name()); if (lexer.token() == Token.COMMA) { lexer.nextToken(); continue; } break; } } accept(Token.RPAREN); partitionClause = clause; partitionClauseRest(clause); } else if (identifierEquals("HASH")) { lexer.nextToken(); SQLPartitionByHash clause = new SQLPartitionByHash(); if (linera) { clause.setLinear(true); } if (lexer.token() == Token.KEY) { lexer.nextToken(); clause.setKey(true); } accept(Token.LPAREN); clause.setExpr(this.exprParser.expr()); accept(Token.RPAREN); partitionClause = clause; partitionClauseRest(clause); } else if (identifierEquals("RANGE")) { SQLPartitionByRange clause = partitionByRange(); partitionClause = clause; partitionClauseRest(clause); } else if (identifierEquals("LIST")) { lexer.nextToken(); SQLPartitionByList clause = new SQLPartitionByList(); if (lexer.token() == Token.LPAREN) { lexer.nextToken(); clause.setExpr(this.exprParser.expr()); accept(Token.RPAREN); } else { acceptIdentifier("COLUMNS"); accept(Token.LPAREN); for (;;) { clause.addColumn(this.exprParser.name()); if (lexer.token() == Token.COMMA) { lexer.nextToken(); continue; } break; } accept(Token.RPAREN); } partitionClause = clause; partitionClauseRest(clause); } else { throw new ParserException("TODO " + lexer.token() + " " + lexer.stringVal()); } if (lexer.token() == Token.LPAREN) { lexer.nextToken(); for (;;) { SQLPartition partitionDef = this.getExprParser().parsePartition(); partitionClause.addPartition(partitionDef); if (lexer.token() == Token.COMMA) { lexer.nextToken(); continue; } else { break; } } accept(Token.RPAREN); } stmt.setPartitioning(partitionClause); continue; } break; } if (lexer.token() == (Token.ON)) { throw new ParserException("TODO"); } if (lexer.token() == (Token.AS)) { lexer.nextToken(); } if (lexer.token() == (Token.SELECT)) { SQLSelect query = new MySqlSelectParser(this.exprParser).select(); stmt.setSelect(query); } while (lexer.token() == (Token.HINT)) { this.exprParser.parseHints(stmt.getOptionHints()); } return stmt; } protected SQLPartitionByRange partitionByRange() { acceptIdentifier("RANGE"); SQLPartitionByRange clause = new SQLPartitionByRange(); if (lexer.token() == Token.LPAREN) { lexer.nextToken(); clause.setExpr(this.exprParser.expr()); accept(Token.RPAREN); } else { acceptIdentifier("COLUMNS"); accept(Token.LPAREN); for (;;) { clause.addColumn(this.exprParser.name()); if (lexer.token() == Token.COMMA) { lexer.nextToken(); continue; } break; } accept(Token.RPAREN); } return clause; } protected void partitionClauseRest(SQLPartitionBy clause) { if (identifierEquals("PARTITIONS")) { lexer.nextToken(); SQLIntegerExpr countExpr = this.exprParser.integerExpr(); clause.setPartitionsCount(countExpr); } if (lexer.token() == Token.PARTITION) { lexer.nextToken(); if (identifierEquals("NUM")) { lexer.nextToken(); } clause.setPartitionsCount(this.exprParser.expr()); clause.putAttribute("ads.partition", Boolean.TRUE); } if (identifierEquals("SUBPARTITION")) { lexer.nextToken(); accept(Token.BY); SQLSubPartitionBy subPartitionByClause = null; boolean linear = false; if (identifierEquals("LINEAR")) { lexer.nextToken(); linear = true; } if (lexer.token() == Token.KEY) { MySqlSubPartitionByKey subPartitionKey = new MySqlSubPartitionByKey(); lexer.nextToken(); if (linear) { clause.setLinear(true); } accept(Token.LPAREN); for (;;) { subPartitionKey.addColumn(this.exprParser.name()); if (lexer.token() == Token.COMMA) { lexer.nextToken(); continue; } break; } accept(Token.RPAREN); subPartitionByClause = subPartitionKey; } else if (identifierEquals("HASH")) { lexer.nextToken(); SQLSubPartitionByHash subPartitionHash = new SQLSubPartitionByHash(); if (linear) { clause.setLinear(true); } if (lexer.token() == Token.KEY) { lexer.nextToken(); subPartitionHash.setKey(true); } accept(Token.LPAREN); subPartitionHash.setExpr(this.exprParser.expr()); accept(Token.RPAREN); subPartitionByClause = subPartitionHash; } else if (identifierEquals("LIST")) { lexer.nextToken(); MySqlSubPartitionByList subPartitionList = new MySqlSubPartitionByList(); if (lexer.token() == Token.LPAREN) { lexer.nextToken(); SQLExpr expr = this.exprParser.expr(); if (expr instanceof SQLIdentifierExpr && (identifierEquals("bigint") || identifierEquals("long"))) { String dataType = lexer.stringVal(); lexer.nextToken(); SQLColumnDefinition column = this.exprParser.createColumnDefinition(); column.setName((SQLIdentifierExpr) expr); column.setDataType(new SQLDataTypeImpl(dataType)); subPartitionList.addColumn(column); subPartitionList.putAttribute("ads.subPartitionList", Boolean.TRUE); } else { subPartitionList.setExpr(expr); } accept(Token.RPAREN); } else { acceptIdentifier("COLUMNS"); accept(Token.LPAREN); for (;;) { subPartitionList.addColumn(this.exprParser.parseColumn()); if (lexer.token() == Token.COMMA) { lexer.nextToken(); continue; } break; } accept(Token.RPAREN); } subPartitionByClause = subPartitionList; } if (identifierEquals("SUBPARTITION")) { lexer.nextToken(); acceptIdentifier("OPTIONS"); accept(Token.LPAREN); SQLAssignItem option = this.exprParser.parseAssignItem(); accept(Token.RPAREN); option.setParent(subPartitionByClause); subPartitionByClause.getOptions().add(option); } if (identifierEquals("SUBPARTITIONS")) { lexer.nextToken(); Number intValue = lexer.integerValue(); SQLNumberExpr numExpr = new SQLNumberExpr(intValue); subPartitionByClause.setSubPartitionsCount(numExpr); lexer.nextToken(); } if (subPartitionByClause != null) { subPartitionByClause.setLinear(linear); clause.setSubPartitionBy(subPartitionByClause); } } } private boolean parseTableOptionCharsetOrCollate(MySqlCreateTableStatement stmt) { if (identifierEquals("CHARACTER")) { lexer.nextToken(); accept(Token.SET); if (lexer.token() == Token.EQ) { lexer.nextToken(); } stmt.getTableOptions().put("CHARACTER SET", this.exprParser.expr()); return true; } if (identifierEquals("CHARSET")) { lexer.nextToken(); if (lexer.token() == Token.EQ) { lexer.nextToken(); } stmt.getTableOptions().put("CHARSET", this.exprParser.expr()); return true; } if (identifierEquals("COLLATE")) { lexer.nextToken(); if (lexer.token() == Token.EQ) { lexer.nextToken(); } stmt.getTableOptions().put("COLLATE", this.exprParser.expr()); return true; } return false; } protected SQLTableConstraint parseConstraint() { SQLName name = null; boolean hasConstaint = false; if (lexer.token() == (Token.CONSTRAINT)) { hasConstaint = true; lexer.nextToken(); } if (lexer.token() == Token.IDENTIFIER) { name = this.exprParser.name(); } if (lexer.token() == (Token.KEY)) { lexer.nextToken(); MySqlKey key = new MySqlKey(); key.setHasConstaint(hasConstaint); // if (identifierEquals("USING")) { // lexer.nextToken(); // key.setIndexType(lexer.stringVal()); // lexer.nextToken(); // } if (lexer.token() == Token.IDENTIFIER || lexer.token() == Token.LITERAL_ALIAS) { SQLName indexName = this.exprParser.name(); if (indexName != null) { key.setIndexName(indexName); } } // 5.5语法 USING BTREE 放在index 名字后 if (identifierEquals("USING")) { lexer.nextToken(); key.setIndexType(lexer.stringVal()); lexer.nextToken(); } accept(Token.LPAREN); for (;;) { SQLExpr expr = this.exprParser.expr(); if (lexer.token() == Token.ASC) { lexer.nextToken(); expr = new MySqlOrderingExpr(expr, SQLOrderingSpecification.ASC); } else if (lexer.token() == Token.DESC) { lexer.nextToken(); expr = new MySqlOrderingExpr(expr, SQLOrderingSpecification.DESC); } key.addColumn(expr); if (!(lexer.token() == (Token.COMMA))) { break; } else { lexer.nextToken(); } } accept(Token.RPAREN); if (name != null) { key.setName(name); } if (identifierEquals("USING")) { lexer.nextToken(); key.setIndexType(lexer.stringVal()); lexer.nextToken(); } return key; } if (lexer.token() == Token.PRIMARY) { MySqlPrimaryKey pk = this.getExprParser().parsePrimaryKey(); pk.setName(name); pk.setHasConstaint(hasConstaint); return (SQLTableConstraint) pk; } if (lexer.token() == Token.UNIQUE) { MySqlUnique uk = this.getExprParser().parseUnique(); uk.setName(name); uk.setHasConstaint(hasConstaint); return (SQLTableConstraint) uk; } if (lexer.token() == Token.FOREIGN) { MysqlForeignKey fk = this.getExprParser().parseForeignKey(); fk.setName(name); fk.setHasConstraint(hasConstaint); return (SQLTableConstraint) fk; } throw new ParserException("TODO :" + lexer.token()); } }