diff --git a/src/main/java/com/alibaba/druid/sql/dialect/mysql/ast/MySqlKey.java b/src/main/java/com/alibaba/druid/sql/dialect/mysql/ast/MySqlKey.java index e242751e7c..252dcea43a 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/mysql/ast/MySqlKey.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/mysql/ast/MySqlKey.java @@ -15,6 +15,7 @@ */ package com.alibaba.druid.sql.dialect.mysql.ast; +import com.alibaba.druid.sql.ast.SQLExpr; import com.alibaba.druid.sql.ast.SQLName; import com.alibaba.druid.sql.ast.statement.SQLSelectOrderByItem; import com.alibaba.druid.sql.ast.statement.SQLTableConstraint; @@ -31,6 +32,8 @@ public class MySqlKey extends SQLUnique implements SQLUniqueConstraint, SQLTable private boolean hasConstaint; + private SQLExpr keyBlockSize; + public MySqlKey(){ dbType = JdbcConstants.MYSQL; } @@ -71,6 +74,9 @@ public class MySqlKey extends SQLUnique implements SQLUniqueConstraint, SQLTable super.cloneTo(x); x.indexType = indexType; x.hasConstaint = hasConstaint; + if (keyBlockSize != null) { + this.setKeyBlockSize(keyBlockSize.clone()); + } } public MySqlKey clone() { @@ -78,4 +84,15 @@ public class MySqlKey extends SQLUnique implements SQLUniqueConstraint, SQLTable cloneTo(x); return x; } + + public SQLExpr getKeyBlockSize() { + return keyBlockSize; + } + + public void setKeyBlockSize(SQLExpr x) { + if (x != null) { + x.setParent(this); + } + this.keyBlockSize = x; + } } diff --git a/src/main/java/com/alibaba/druid/sql/dialect/mysql/parser/MySqlCreateTableParser.java b/src/main/java/com/alibaba/druid/sql/dialect/mysql/parser/MySqlCreateTableParser.java index 863459ff6d..523618918a 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/mysql/parser/MySqlCreateTableParser.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/mysql/parser/MySqlCreateTableParser.java @@ -115,6 +115,57 @@ public class MySqlCreateTableParser extends SQLCreateTableParser { stmt.getTableElementList().add(fulltextKey); if (lexer.token() == Token.RPAREN) { break; + } else if (lexer.token() == Token.COMMA) { + lexer.nextToken(); + continue; + } + } else { + lexer.reset(mark); + } + } else if (lexer.identifierEquals(FnvHash.Constants.SPATIAL)) { + Lexer.SavePoint mark = lexer.mark(); + lexer.nextToken(); + if (lexer.token() == Token.INDEX) { + lexer.nextToken(); + MySqlTableIndex idx = new MySqlTableIndex(); + idx.setIndexType("SPATIAL"); + + if (lexer.token() == Token.IDENTIFIER) { + if (!"USING".equalsIgnoreCase(lexer.stringVal())) { + idx.setName(this.exprParser.name()); + } + } + + if (lexer.identifierEquals("USING")) { + lexer.nextToken(); + idx.setIndexType(lexer.stringVal()); + lexer.nextToken(); + } + + accept(Token.LPAREN); + for (;;) { + idx.addColumn(this.exprParser.parseSelectOrderByItem()); + if (!(lexer.token() == (Token.COMMA))) { + break; + } else { + lexer.nextToken(); + } + } + accept(Token.RPAREN); + + if (lexer.identifierEquals("USING")) { + lexer.nextToken(); + idx.setIndexType(lexer.stringVal()); + lexer.nextToken(); + } + + stmt.getTableElementList().add(idx); + + if (lexer.token() == Token.RPAREN) { + break; + } else if (lexer.token() == Token.COMMA) { + lexer.nextToken(); + continue; } } else { lexer.reset(mark); @@ -461,11 +512,22 @@ public class MySqlCreateTableParser extends SQLCreateTableParser { if (lexer.identifierEquals("ENCRYPTION")) { lexer.nextToken(); - accept(Token.EQ); + if (lexer.token() == Token.EQ) { + lexer.nextToken(); + } stmt.getTableOptions().put("ENCRYPTION", this.exprParser.expr()); continue; } + if (lexer.identifierEquals(FnvHash.Constants.COMPRESSION)) { + lexer.nextToken(); + if (lexer.token() == Token.EQ) { + lexer.nextToken(); + } + stmt.getTableOptions().put("COMPRESSION", this.exprParser.expr()); + continue; + } + if (lexer.token() == Token.PARTITION) { lexer.nextToken(); accept(Token.BY); diff --git a/src/main/java/com/alibaba/druid/sql/dialect/mysql/parser/MySqlExprParser.java b/src/main/java/com/alibaba/druid/sql/dialect/mysql/parser/MySqlExprParser.java index fc1c01efa7..a0a7c8e9ee 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/mysql/parser/MySqlExprParser.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/mysql/parser/MySqlExprParser.java @@ -795,6 +795,15 @@ public class MySqlExprParser extends SQLExprParser { lexer.nextToken(); } + if (lexer.identifierEquals(FnvHash.Constants.KEY_BLOCK_SIZE)) { + lexer.nextToken(); + if (lexer.token() == Token.EQ) { + lexer.nextToken(); + } + SQLExpr value = this.primary(); + unique.setKeyBlockSize(value); + } + return unique; } diff --git a/src/main/java/com/alibaba/druid/sql/dialect/mysql/visitor/MySqlOutputVisitor.java b/src/main/java/com/alibaba/druid/sql/dialect/mysql/visitor/MySqlOutputVisitor.java index 9600d9c25f..e6554ba2fc 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/mysql/visitor/MySqlOutputVisitor.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/mysql/visitor/MySqlOutputVisitor.java @@ -399,8 +399,13 @@ public class MySqlOutputVisitor extends SQLASTOutputVisitor implements MySqlASTV public boolean visit(MySqlTableIndex x) { String indexType = x.getIndexType(); + boolean indexTypePrinted = false; if ("FULLTEXT".equalsIgnoreCase(indexType)) { print0(ucase ? "FULLTEXT " : "fulltext "); + indexTypePrinted = true; + } else if ("SPATIAL".equalsIgnoreCase(indexType)) { + print0(ucase ? "SPATIAL " : "spatial "); + indexTypePrinted = true; } print0(ucase ? "INDEX" : "index"); @@ -409,7 +414,7 @@ public class MySqlOutputVisitor extends SQLASTOutputVisitor implements MySqlASTV x.getName().accept(this); } - if (indexType != null && !"FULLTEXT".equalsIgnoreCase(indexType)) { + if (indexType != null && !indexTypePrinted) { print0(ucase ? " USING " : " using "); print0(indexType); } @@ -2720,7 +2725,6 @@ public class MySqlOutputVisitor extends SQLASTOutputVisitor implements MySqlASTV if (x.getIndexType() != null) { print0(ucase ? " USING " : " using "); - ; print0(x.getIndexType()); } @@ -2728,6 +2732,12 @@ public class MySqlOutputVisitor extends SQLASTOutputVisitor implements MySqlASTV printAndAccept(x.getColumns(), ", "); print(')'); + SQLExpr keyBlockSize = x.getKeyBlockSize(); + if (keyBlockSize != null) { + print0(ucase ? " KEY_BLOCK_SIZE = " : " key_block_size = "); + keyBlockSize.accept(this); + } + SQLExpr comment = x.getComment(); if (comment != null) { print0(" COMMENT "); diff --git a/src/main/java/com/alibaba/druid/util/FnvHash.java b/src/main/java/com/alibaba/druid/util/FnvHash.java index e69fffcbfe..04e8f22946 100644 --- a/src/main/java/com/alibaba/druid/util/FnvHash.java +++ b/src/main/java/com/alibaba/druid/util/FnvHash.java @@ -633,7 +633,10 @@ public final class FnvHash { long TBLPROPERTIES = fnv1a_64_lower("TBLPROPERTIES"); long FULLTEXT = fnv1a_64_lower("FULLTEXT"); + long SPATIAL = fnv1a_64_lower("SPATIAL"); long NO = fnv1a_64_lower("NO"); long PATH = fnv1a_64_lower("PATH"); + long COMPRESSION = fnv1a_64_lower("COMPRESSION"); + long KEY_BLOCK_SIZE = fnv1a_64_lower("KEY_BLOCK_SIZE"); } } diff --git a/src/test/java/com/alibaba/druid/bvt/sql/mysql/alter/MySqlAlterTableAlterColumnTest.java b/src/test/java/com/alibaba/druid/bvt/sql/mysql/alter/MySqlAlterTableAlterColumnTest.java index a7d6ce1301..d8a1dc68a4 100644 --- a/src/test/java/com/alibaba/druid/bvt/sql/mysql/alter/MySqlAlterTableAlterColumnTest.java +++ b/src/test/java/com/alibaba/druid/bvt/sql/mysql/alter/MySqlAlterTableAlterColumnTest.java @@ -31,10 +31,10 @@ public class MySqlAlterTableAlterColumnTest extends TestCase { parser.match(Token.EOF); assertEquals("ALTER TABLE `cube_template`\n" + - "\tADD COLUMN `stat_report_json` VARCHAR(256) CHARSET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '' COMMENT '模板的统计报表json数组';", SQLUtils.toMySqlString(stmt)); + "\tADD COLUMN `stat_report_json` VARCHAR(256) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '' COMMENT '模板的统计报表json数组';", SQLUtils.toMySqlString(stmt)); assertEquals("alter table `cube_template`\n" + - "\tadd column `stat_report_json` VARCHAR(256) charset utf8 COLLATE utf8_general_ci not null default '' comment '模板的统计报表json数组';", SQLUtils.toMySqlString(stmt, SQLUtils.DEFAULT_LCASE_FORMAT_OPTION)); + "\tadd column `stat_report_json` VARCHAR(256) character set utf8 collate utf8_general_ci not null default '' comment '模板的统计报表json数组';", SQLUtils.toMySqlString(stmt, SQLUtils.DEFAULT_LCASE_FORMAT_OPTION)); } } diff --git a/src/test/java/com/alibaba/druid/bvt/sql/mysql/createTable/MySqlCreateTableTest100.java b/src/test/java/com/alibaba/druid/bvt/sql/mysql/createTable/MySqlCreateTableTest100.java new file mode 100644 index 0000000000..7fe1dde5da --- /dev/null +++ b/src/test/java/com/alibaba/druid/bvt/sql/mysql/createTable/MySqlCreateTableTest100.java @@ -0,0 +1,45 @@ +package com.alibaba.druid.bvt.sql.mysql.createTable; + +import com.alibaba.druid.sql.MysqlTest; +import com.alibaba.druid.sql.ast.SQLStatement; +import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlCreateTableStatement; +import com.alibaba.druid.sql.dialect.mysql.parser.MySqlStatementParser; + +import java.util.List; + +public class MySqlCreateTableTest100 extends MysqlTest { + + public void test_0() throws Exception { + String sql = "CREATE TABLE IF NOT EXISTS ttable\n" + + "(\n" + + "`id` BIGINT(20) NOT NULL AUTO_INCREMENT,\n" + + "`queue_id` varchar(20) NOT NULL DEFAULT '-1',\n" + + "`status` TINYINT(4) NOT NULL DEFAULT '1',\n" + + "`geometry` geometry not null,\n" + + "CONSTRAINT PRIMARY KEY (`id`),\n" + + "CONSTRAINT UNIQUE KEY `uk_queue_id` USING BTREE (`queue_id`) KEY_BLOCK_SIZE=10,\n" + + "FULLTEXT KEY `ft_status` (`queue_id`),\n" + + "spatial index `spatial` (`geometry`),\n" + + "CONSTRAINT FOREIGN KEY `fk_test`(`queue_id`) REFERENCES `test`(`id`)\n" + + ") ENGINE=INNODB"; + + MySqlStatementParser parser = new MySqlStatementParser(sql); + List statementList = parser.parseStatementList(); + MySqlCreateTableStatement stmt = (MySqlCreateTableStatement)statementList.get(0); + + assertEquals(1, statementList.size()); + assertEquals(9, stmt.getTableElementList().size()); + + assertEquals("CREATE TABLE IF NOT EXISTS ttable (\n" + + "\t`id` BIGINT(20) NOT NULL AUTO_INCREMENT,\n" + + "\t`queue_id` varchar(20) NOT NULL DEFAULT '-1',\n" + + "\t`status` TINYINT(4) NOT NULL DEFAULT '1',\n" + + "\t`geometry` geometry NOT NULL,\n" + + "\tPRIMARY KEY (`id`),\n" + + "\tCONSTRAINT `uk_queue_id` UNIQUE `uk_queue_id` USING BTREE (`queue_id`) KEY_BLOCK_SIZE = 10,\n" + + "\tKEY `ft_status` USING FULLTEXT (`queue_id`),\n" + + "\tSPATIAL INDEX `spatial`(`geometry`),\n" + + "\tCONSTRAINT FOREIGN KEY `fk_test` (`queue_id`) REFERENCES `test` (`id`)\n" + + ") ENGINE = INNODB", stmt.toString()); + } +} \ No newline at end of file diff --git a/src/test/java/com/alibaba/druid/bvt/sql/mysql/createTable/MySqlCreateTableTest60.java b/src/test/java/com/alibaba/druid/bvt/sql/mysql/createTable/MySqlCreateTableTest60.java index 3858c1884f..48b932e660 100644 --- a/src/test/java/com/alibaba/druid/bvt/sql/mysql/createTable/MySqlCreateTableTest60.java +++ b/src/test/java/com/alibaba/druid/bvt/sql/mysql/createTable/MySqlCreateTableTest60.java @@ -40,14 +40,14 @@ public class MySqlCreateTableTest60 extends MysqlTest { String output = SQLUtils.toMySqlString(stmt); Assert.assertEquals("CREATE TABLE t10 (" + "\n\tpk int PRIMARY KEY AUTO_INCREMENT," - + "\n\tf1 varchar(65500) CHARSET latin1 " + + "\n\tf1 varchar(65500) CHARACTER SET latin1" + "\n)", output); } { String output = SQLUtils.toMySqlString(stmt, SQLUtils.DEFAULT_LCASE_FORMAT_OPTION); Assert.assertEquals("create table t10 (" + "\n\tpk int primary key auto_increment," - + "\n\tf1 varchar(65500) charset latin1 " + + "\n\tf1 varchar(65500) character set latin1" + "\n)", output); } } diff --git a/src/test/java/com/alibaba/druid/bvt/sql/mysql/createTable/MySqlCreateTableTest99.java b/src/test/java/com/alibaba/druid/bvt/sql/mysql/createTable/MySqlCreateTableTest99.java new file mode 100644 index 0000000000..5df5667e2a --- /dev/null +++ b/src/test/java/com/alibaba/druid/bvt/sql/mysql/createTable/MySqlCreateTableTest99.java @@ -0,0 +1,40 @@ +package com.alibaba.druid.bvt.sql.mysql.createTable; + +import com.alibaba.druid.sql.MysqlTest; +import com.alibaba.druid.sql.ast.SQLStatement; +import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlCreateTableStatement; +import com.alibaba.druid.sql.dialect.mysql.parser.MySqlStatementParser; + +import java.util.List; + +public class MySqlCreateTableTest99 extends MysqlTest { + + public void test_0() throws Exception { + String sql = "CREATE TABLE IF NOT EXISTS srcTable\n" + + "(\n" + + " `id` BIGINT(20) NOT NULL,\n" + + " `queue_id` BIGINT(20) NOT NULL DEFAULT '-1',\n" + + " `status` TINYINT(4) NOT NULL DEFAULT '1',\n" + + " PRIMARY KEY (`id`)\n" + + ") ENGINE=INNODB AUTO_INCREMENT 10 AVG_ROW_LENGTH 10 DEFAULT CHARACTER SET=utf8 DEFAULT COLLATE = utf8_general_ci\n" + + "CHECKSUM=0 COMPRESSION='NONE' CONNECTION = 'connect_string' DELAY_KEY_WRITE = 0 ENCRYPTION 'N' INSERT_METHOD FIRST\n" + + "MAX_ROWS 1000 MIN_ROWS=10 PACK_KEYS DEFAULT PASSWORD '12345678' STATS_AUTO_RECALC 0 STATS_PERSISTENT 0 \n" + + "STATS_SAMPLE_PAGES 10"; + +// System.out.println(sql); + + MySqlStatementParser parser = new MySqlStatementParser(sql); + List statementList = parser.parseStatementList(); + MySqlCreateTableStatement stmt = (MySqlCreateTableStatement)statementList.get(0); + + assertEquals(1, statementList.size()); + assertEquals(4, stmt.getTableElementList().size()); + + assertEquals("CREATE TABLE IF NOT EXISTS srcTable (\n" + + "\t`id` BIGINT(20) NOT NULL,\n" + + "\t`queue_id` BIGINT(20) NOT NULL DEFAULT '-1',\n" + + "\t`status` TINYINT(4) NOT NULL DEFAULT '1',\n" + + "\tPRIMARY KEY (`id`)\n" + + ") ENGINE = INNODB AUTO_INCREMENT = 10 AVG_ROW_LENGTH = 10 CHARACTER SET = utf8 COLLATE = utf8_general_ci CHECKSUM = 0 COMPRESSION = 'NONE' CONNECTION = 'connect_string' DELAY_KEY_WRITE = 0 ENCRYPTION = 'N' INSERT_METHOD = FIRST MAX_ROWS = 1000 MIN_ROWS = 10 PACK_KEYS = DEFAULT PASSWORD = '12345678' STATS_AUTO_RECALC = 0 STATS_PERSISTENT = 0 STATS_SAMPLE_PAGES = 10", stmt.toString()); + } +} \ No newline at end of file