improved mysql parser. for issue #2340

This commit is contained in:
wenshao
2018-02-19 22:23:26 +08:00
parent b5ee153efe
commit eb68a19fbc
9 changed files with 193 additions and 7 deletions
@@ -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;
}
}
@@ -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);
@@ -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;
}
@@ -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 ");
@@ -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");
}
}
@@ -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));
}
}
@@ -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<SQLStatement> 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());
}
}
@@ -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);
}
}
@@ -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<SQLStatement> 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());
}
}