improved sql parser.

This commit is contained in:
wenshao
2017-08-01 21:01:33 +08:00
parent 544da04ee4
commit bd125342e2
14 changed files with 313 additions and 53 deletions
@@ -20,6 +20,9 @@ import java.util.List;
import com.alibaba.druid.sql.ast.SQLName;
import com.alibaba.druid.sql.ast.SQLObjectImpl;
import com.alibaba.druid.sql.dialect.mysql.ast.MySqlKey;
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;
public class SQLAlterTableAddIndex extends SQLObjectImpl implements SQLAlterTableItem {
@@ -95,4 +98,28 @@ public class SQLAlterTableAddIndex extends SQLObjectImpl implements SQLAlterTabl
public void setKey(boolean key) {
this.key = key;
}
public void cloneTo(MySqlTableIndex x) {
if (name != null) {
x.setName(name.clone());
}
for (SQLSelectOrderByItem item : items) {
SQLSelectOrderByItem item2 = item.clone();
item2.setParent(x);
x.getColumns().add(item);
}
x.setIndexType(type);
}
public void cloneTo(MySqlKey x) {
if (name != null) {
x.setName(name.clone());
}
for (SQLSelectOrderByItem item : items) {
SQLSelectOrderByItem item2 = item.clone();
item2.setParent(x);
x.getColumns().add(item);
}
x.setIndexType(type);
}
}
@@ -108,6 +108,10 @@ public class SQLColumnDefinition extends SQLObjectImpl implements SQLTableElemen
this.name = name;
}
public void setName(String name) {
this.setName(new SQLIdentifierExpr(name));
}
public SQLDataType getDataType() {
return dataType;
}
@@ -23,6 +23,7 @@ import com.alibaba.druid.sql.ast.*;
import com.alibaba.druid.sql.ast.expr.SQLIdentifierExpr;
import com.alibaba.druid.sql.ast.expr.SQLPropertyExpr;
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.statement.MySqlTableIndex;
import com.alibaba.druid.sql.visitor.SQLASTVisitor;
@@ -210,7 +211,7 @@ public class SQLCreateTableStatement extends SQLStatementImpl implements SQLDDLS
if (element instanceof MySqlUnique) {
MySqlUnique unique = (MySqlUnique) element;
SQLExpr column = unique.getColumns().get(0);
SQLExpr column = unique.getColumns().get(0).getExpr();
if (column instanceof SQLIdentifierExpr
&& SQLUtils.nameEquals(columnName, ((SQLIdentifierExpr) column).getName())) {
return unique.columns.size() > 1;
@@ -218,7 +219,7 @@ public class SQLCreateTableStatement extends SQLStatementImpl implements SQLDDLS
} else if (element instanceof MySqlKey) {
MySqlKey unique = (MySqlKey) element;
SQLExpr column = unique.getColumns().get(0);
SQLExpr column = unique.getColumns().get(0).getExpr();
if (column instanceof SQLIdentifierExpr
&& SQLUtils.nameEquals(columnName, ((SQLIdentifierExpr) column).getName())) {
return true;
@@ -236,7 +237,7 @@ public class SQLCreateTableStatement extends SQLStatementImpl implements SQLDDLS
if (element instanceof MySqlUnique) {
MySqlUnique unique = (MySqlUnique) element;
SQLExpr column = unique.getColumns().get(0);
SQLExpr column = unique.getColumns().get(0).getExpr();
if (column instanceof SQLIdentifierExpr
&& SQLUtils.nameEquals(columnName, ((SQLIdentifierExpr) column).getName())) {
return unique.columns.size() == 1;
@@ -264,7 +265,8 @@ public class SQLCreateTableStatement extends SQLStatementImpl implements SQLDDLS
for (SQLTableElement element : tableElementList) {
if (element instanceof SQLUniqueConstraint) {
SQLUniqueConstraint unique = (SQLUniqueConstraint) element;
for (SQLExpr columnExpr : unique.getColumns()) {
for (SQLSelectOrderByItem item : unique.getColumns()) {
SQLExpr columnExpr = item.getExpr();
if (columnExpr instanceof SQLIdentifierExpr) {
String keyColumName = ((SQLIdentifierExpr) columnExpr).getName();
keyColumName = SQLUtils.normalize(keyColumName);
@@ -571,6 +573,9 @@ public class SQLCreateTableStatement extends SQLStatementImpl implements SQLDDLS
} else if (item instanceof SQLAlterTableRenameColumn) {
return apply((SQLAlterTableRenameColumn) item);
} else if (item instanceof SQLAlterTableAddIndex) {
return apply((SQLAlterTableAddIndex) item);
}
return false;
@@ -695,6 +700,29 @@ public class SQLCreateTableStatement extends SQLStatementImpl implements SQLDDLS
return true;
}
private boolean apply(SQLAlterTableAddIndex item) {
if (item.isKey()) {
MySqlPrimaryKey x = new MySqlPrimaryKey();
item.cloneTo(x);
x.setParent(this);
this.tableElementList.add(x);
return true;
}
if (item.isUnique()) {
MySqlUnique x = new MySqlUnique();
item.cloneTo(x);
x.setParent(this);
this.tableElementList.add(x);
return true;
}
MySqlTableIndex x = new MySqlTableIndex();
item.cloneTo(x);
x.setParent(this);
this.tableElementList.add(x);
return true;
}
private boolean apply(SQLAlterTableAddColumn item) {
int startIndex = tableElementList.size();
if (item.isFirst()) {
@@ -16,6 +16,7 @@
package com.alibaba.druid.sql.ast.statement;
import com.alibaba.druid.sql.SQLUtils;
import com.alibaba.druid.sql.ast.SQLDataType;
import com.alibaba.druid.sql.ast.SQLExpr;
import com.alibaba.druid.sql.ast.SQLObjectImpl;
import com.alibaba.druid.sql.ast.SQLReplaceable;
@@ -80,6 +81,26 @@ public class SQLSelectItem extends SQLObjectImpl implements SQLReplaceable {
return SQLUtils.normalize(alias);
}
public SQLDataType computeDataType() {
if (expr instanceof SQLIdentifierExpr) {
SQLIdentifierExpr identExpr = (SQLIdentifierExpr) expr;
if (identExpr.getResolvedColumn() != null) {
return identExpr.getResolvedColumn().getDataType();
}
return null;
}
if (expr instanceof SQLPropertyExpr) {
SQLPropertyExpr propertyExpr = (SQLPropertyExpr) expr;
if (propertyExpr.getResolvedColumn() != null) {
return propertyExpr.getResolvedColumn().getDataType();
}
return null;
}
return null;
}
public String getAlias() {
return this.alias;
}
@@ -26,13 +26,13 @@ import com.alibaba.druid.sql.visitor.SQLASTVisitor;
public class SQLUnique extends SQLConstraintImpl implements SQLUniqueConstraint, SQLTableElement {
protected final List<SQLExpr> columns = new ArrayList<SQLExpr>();
protected final List<SQLSelectOrderByItem> columns = new ArrayList<SQLSelectOrderByItem>();
public SQLUnique(){
}
public List<SQLExpr> getColumns() {
public List<SQLSelectOrderByItem> getColumns() {
return columns;
}
@@ -40,7 +40,7 @@ public class SQLUnique extends SQLConstraintImpl implements SQLUniqueConstraint,
if (column != null) {
column.setParent(this);
}
this.columns.add(column);
this.columns.add(new SQLSelectOrderByItem(column));
}
@Override
@@ -53,7 +53,8 @@ public class SQLUnique extends SQLConstraintImpl implements SQLUniqueConstraint,
}
public boolean containsColumn(String column) {
for (SQLExpr expr : columns) {
for (SQLSelectOrderByItem item : columns) {
SQLExpr expr = item.getExpr();
if (expr instanceof SQLIdentifierExpr) {
if (SQLUtils.nameEquals(((SQLIdentifierExpr) expr).getName(), column)) {
return true;
@@ -66,9 +67,10 @@ public class SQLUnique extends SQLConstraintImpl implements SQLUniqueConstraint,
public void cloneTo(SQLUnique x) {
super.cloneTo(x);
for (SQLExpr column : columns) {
SQLExpr columnClone = column.clone();
x.addColumn(columnClone);
for (SQLSelectOrderByItem column : columns) {
SQLSelectOrderByItem column2 = column.clone();
column2.setParent(x);
x.columns.add(column2);
}
}
@@ -81,7 +83,8 @@ public class SQLUnique extends SQLConstraintImpl implements SQLUniqueConstraint,
public void simplify() {
super.simplify();
for (SQLExpr column : columns) {
for (SQLSelectOrderByItem item : columns) {
SQLExpr column = item.getExpr();
if (column instanceof SQLIdentifierExpr) {
SQLIdentifierExpr identExpr = (SQLIdentifierExpr) column;
String columnName = identExpr.getName();
@@ -21,7 +21,7 @@ import com.alibaba.druid.sql.ast.SQLExpr;
public interface SQLUniqueConstraint extends SQLConstraint {
List<SQLExpr> getColumns();
List<SQLSelectOrderByItem> getColumns();
boolean containsColumn(String column);
}
@@ -40,7 +40,7 @@ public class MySqlShowColumnOutpuVisitor extends MySqlOutputVisitor {
List<String> dataTypes = new ArrayList<String>();
List<String> defaultValues = new ArrayList<String>();
int name_len = -1, dataType_len = -1, defaultVal_len = -1, extra_len = 5;
int name_len = -1, dataType_len = -1, defaultVal_len = 7, extra_len = 5;
for (SQLTableElement element : x.getTableElementList()) {
if (element instanceof SQLColumnDefinition) {
SQLColumnDefinition column = (SQLColumnDefinition) element;
@@ -60,10 +60,6 @@ public class OraclePrimaryKey extends SQLPrimaryKeyImpl implements OracleConstra
this.deferrable = deferrable;
}
public List<SQLExpr> getColumns() {
return columns;
}
public OracleUsingIndexClause getUsing() {
return using;
}
@@ -1019,7 +1019,7 @@ public class OracleExprParser extends SQLExprParser {
OraclePrimaryKey primaryKey = new OraclePrimaryKey();
accept(Token.LPAREN);
exprList(primaryKey.getColumns(), primaryKey);
orderBy(primaryKey.getColumns(), primaryKey);
accept(Token.RPAREN);
@@ -1349,7 +1349,7 @@ public class OracleExprParser extends SQLExprParser {
OracleUnique unique = new OracleUnique();
accept(Token.LPAREN);
exprList(unique.getColumns(), unique);
orderBy(unique.getColumns(), unique);
accept(Token.RPAREN);
if (lexer.token() == Token.USING) {
@@ -1273,12 +1273,7 @@ public class SQLExprParser extends SQLParser {
accept(Token.BY);
orderBy.addItem(parseSelectOrderByItem());
while (lexer.token() == Token.COMMA) {
lexer.nextToken();
orderBy.addItem(parseSelectOrderByItem());
}
orderBy(orderBy.getItems(), orderBy);
return orderBy;
}
@@ -1286,6 +1281,18 @@ public class SQLExprParser extends SQLParser {
return null;
}
public void orderBy(List<SQLSelectOrderByItem> items, SQLObject parent) {
SQLSelectOrderByItem item = parseSelectOrderByItem();
item.setParent(parent);
items.add(item);
while (lexer.token() == Token.COMMA) {
lexer.nextToken();
item = parseSelectOrderByItem();
item.setParent(parent);
items.add(item);
}
}
public SQLSelectOrderByItem parseSelectOrderByItem() {
SQLSelectOrderByItem item = new SQLSelectOrderByItem();
@@ -2135,7 +2142,7 @@ public class SQLExprParser extends SQLParser {
SQLPrimaryKeyImpl pk = new SQLPrimaryKeyImpl();
accept(Token.LPAREN);
exprList(pk.getColumns(), pk);
orderBy(pk.getColumns(), pk);
accept(Token.RPAREN);
return pk;
@@ -2146,7 +2153,7 @@ public class SQLExprParser extends SQLParser {
SQLUnique unique = new SQLUnique();
accept(Token.LPAREN);
exprList(unique.getColumns(), unique);
orderBy(unique.getColumns(), unique);
accept(Token.RPAREN);
if (lexer.token() == Token.DISABLE) {
@@ -16,6 +16,7 @@
package com.alibaba.druid.sql.repository;
import com.alibaba.druid.sql.SQLUtils;
import com.alibaba.druid.sql.ast.SQLDataType;
import com.alibaba.druid.sql.ast.SQLExpr;
import com.alibaba.druid.sql.ast.SQLName;
import com.alibaba.druid.sql.ast.SQLStatement;
@@ -140,15 +141,7 @@ public class Schema {
}
public boolean visit(SQLCreateTableStatement x) {
String name = x.computeName();
SchemaObject object = new SchemaObjectImpl(name, SchemaObjectType.Table, x);
String name_lower = name.toLowerCase();
if (objects.containsKey(name_lower)) {
return false;
}
objects.put(name_lower, object);
acceptCreateTable(x);
return false;
}
@@ -197,6 +190,39 @@ public class Schema {
}
}
boolean acceptCreateTable(SQLCreateTableStatement x) {
SQLSelect select = x.getSelect();
if (select != null) {
select.accept(
repository.createResolveVisitor());
SQLSelectQueryBlock queryBlock = select.getQueryBlock();
if (queryBlock != null) {
List<SQLSelectItem> selectList = queryBlock.getSelectList();
for (SQLSelectItem selectItem : selectList) {
String name = selectItem.computeAlias();
SQLDataType dataType = selectItem.computeDataType();
SQLColumnDefinition column = new SQLColumnDefinition();
column.setName(name);
column.setDataType(dataType);
column.setDbType(repository.dbType);
x.getTableElementList().add(column);
}
x.setSelect(null);
}
}
String name = x.computeName();
SchemaObject object = new SchemaObjectImpl(name, SchemaObjectType.Table, x);
String name_lower = name.toLowerCase();
if (objects.containsKey(name_lower)) {
return false;
}
objects.put(name_lower, object);
return true;
}
private class MySqlSchemaVisitor extends MySqlASTVisitorAdapter {
public boolean visit(SQLDropSequenceStatement x) {
@@ -221,23 +247,16 @@ public class Schema {
MySqlCreateTableStatement stmt = (MySqlCreateTableStatement) table.getStatement();
MySqlCreateTableStatement stmtCloned = stmt.clone();
stmtCloned.setName(x.getName().clone());
return visit((SQLCreateTableStatement) stmtCloned);
acceptCreateTable(stmtCloned);
return false;
}
}
visit((SQLCreateTableStatement) x);
acceptCreateTable(x);
return false;
}
public boolean visit(SQLCreateTableStatement x) {
String name = x.computeName();
SchemaObject object = new SchemaObjectImpl(name, SchemaObjectType.Table, x);
String name_lower = name.toLowerCase();
if (objects.containsKey(name_lower)) {
return false;
}
objects.put(name_lower, object);
acceptCreateTable(x);
return false;
}
@@ -26,17 +26,19 @@ import com.alibaba.druid.sql.ast.expr.SQLIdentifierExpr;
import com.alibaba.druid.sql.ast.expr.SQLPropertyExpr;
import com.alibaba.druid.sql.ast.statement.*;
import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlCreateTableStatement;
import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlSelectQueryBlock;
import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlShowColumnsStatement;
import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlShowCreateTableStatement;
import com.alibaba.druid.sql.dialect.mysql.visitor.MySqlASTVisitor;
import com.alibaba.druid.sql.dialect.mysql.visitor.MySqlASTVisitorAdapter;
import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleCreateTableStatement;
import com.alibaba.druid.sql.dialect.oracle.visitor.OracleASTVisitorAdapter;
import com.alibaba.druid.sql.visitor.SQLASTVisitor;
import com.alibaba.druid.util.JdbcConstants;
import com.alibaba.druid.util.JdbcUtils;
import java.io.IOException;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.*;
import java.util.concurrent.ConcurrentSkipListMap;
/**
@@ -253,4 +255,66 @@ public class SchemaRepository {
return null;
}
public SQLASTVisitor createResolveVisitor() {
if (JdbcConstants.MYSQL.equals(dbType)) {
return new MySqlResolveVisitor();
}
throw new DruidRuntimeException("dbType not support : " + dbType);
}
public class MySqlResolveVisitor extends MySqlASTVisitorAdapter {
public boolean visit(MySqlSelectQueryBlock x) {
List<SQLSelectItem> selectList = x.getSelectList();
for (int i = selectList.size() - 1; i >= 0; i--) {
SQLSelectItem selectItem = selectList.get(i);
SQLExpr expr = selectItem.getExpr();
if (expr instanceof SQLAllColumnExpr) {
SQLTableSource from = x.getFrom();
List<SQLSelectItem> columns = new ArrayList<SQLSelectItem>();
if (from instanceof SQLExprTableSource) {
SchemaObject table = findTable((SQLExprTableSource) from);
if (table != null) {
SQLCreateTableStatement createTableStmt = (SQLCreateTableStatement) table.getStatement();
for (SQLTableElement e : createTableStmt.getTableElementList()) {
if (e instanceof SQLColumnDefinition) {
SQLColumnDefinition column = (SQLColumnDefinition) e;
SQLIdentifierExpr name = (SQLIdentifierExpr) column.getName().clone();
name.setResolvedColumn(column);
columns.add(new SQLSelectItem(name));
}
}
}
}
for (SQLSelectItem column : columns) {
column.setParent(x);
}
selectList.remove(i);
selectList.addAll(i, columns);
}
}
return super.visit(x);
}
}
public SchemaObject findTable(SQLExprTableSource x) {
if (x == null) {
return null;
}
SQLExpr expr = x.getExpr();
if (expr instanceof SQLName) {
return findTable((SQLName) expr);
}
return null;
}
public class OracleResolveVisitor extends OracleASTVisitorAdapter {
}
}
@@ -804,7 +804,7 @@ public class SQLASTVisitorAdapter implements SQLASTVisitor {
@Override
public boolean visit(SQLUnique x) {
for (SQLExpr column : x.getColumns()) {
for (SQLSelectOrderByItem column : x.getColumns()) {
column.accept(this);
}
return false;
@@ -322,5 +322,96 @@ public class MySqlCreateTable_showColumns_repository_test extends MysqlTest {
"\t`c_longblob` longblob COMMENT 'longblob',\n" +
"\tKEY `k_c` (`c_tinyint`, `c_int`)\n" +
") ENGINE = InnoDB AUTO_INCREMENT = 1769503 CHARSET = utf8mb4 COMMENT = '10000000'", repository.console("show create table test2"));
repository.console("CREATE TABLE test3 as select * from test2");
assertEquals("CREATE TABLE test3 (\n" +
"\tc_decimal_1 INT(11),\n" +
"\tid bigint(20),\n" +
"\tc_tinyint tinyint(4),\n" +
"\tc_smallint smallint(6),\n" +
"\tc_mediumint mediumint(9),\n" +
"\tc_int int(11),\n" +
"\tc_bigint bigint(20),\n" +
"\tc_date date,\n" +
"\tc_datetime datetime,\n" +
"\tc_timestamp timestamp,\n" +
"\tc_time time,\n" +
"\tc_char char(10),\n" +
"\tc_varchar varchar(10),\n" +
"\tc_blob blob,\n" +
"\tc_text text,\n" +
"\tc_mediumtext mediumtext,\n" +
"\tc_longblob longblob\n" +
")", repository.console("show create table test3"));
assertEquals("+--------------+--------------+------+-----+---------+-------+\n" +
"| Field | Type | Null | Key | Default | Extra |\n" +
"+--------------+--------------+------+-----+---------+-------+\n" +
"| c_decimal_1 | INT(11) | NO | | NULL | |\n" +
"| id | bigint(20) | NO | | NULL | |\n" +
"| c_tinyint | tinyint(4) | NO | | NULL | |\n" +
"| c_smallint | smallint(6) | NO | | NULL | |\n" +
"| c_mediumint | mediumint(9) | NO | | NULL | |\n" +
"| c_int | int(11) | NO | | NULL | |\n" +
"| c_bigint | bigint(20) | NO | | NULL | |\n" +
"| c_date | date | NO | | NULL | |\n" +
"| c_datetime | datetime | NO | | NULL | |\n" +
"| c_timestamp | timestamp | NO | | NULL | |\n" +
"| c_time | time | NO | | NULL | |\n" +
"| c_char | char(10) | NO | | NULL | |\n" +
"| c_varchar | varchar(10) | NO | | NULL | |\n" +
"| c_blob | blob | NO | | NULL | |\n" +
"| c_text | text | NO | | NULL | |\n" +
"| c_mediumtext | mediumtext | NO | | NULL | |\n" +
"| c_longblob | longblob | NO | | NULL | |\n" +
"+--------------+--------------+------+-----+---------+-------+\n", repository.console("show columns from test3"));
repository.console("alter TABLE test3 add UNIQUE(c_tinyint)");
assertEquals("+--------------+--------------+------+-----+---------+-------+\n" +
"| Field | Type | Null | Key | Default | Extra |\n" +
"+--------------+--------------+------+-----+---------+-------+\n" +
"| c_decimal_1 | INT(11) | NO | | NULL | |\n" +
"| id | bigint(20) | NO | | NULL | |\n" +
"| c_tinyint | tinyint(4) | NO | UNI | NULL | |\n" +
"| c_smallint | smallint(6) | NO | | NULL | |\n" +
"| c_mediumint | mediumint(9) | NO | | NULL | |\n" +
"| c_int | int(11) | NO | | NULL | |\n" +
"| c_bigint | bigint(20) | NO | | NULL | |\n" +
"| c_date | date | NO | | NULL | |\n" +
"| c_datetime | datetime | NO | | NULL | |\n" +
"| c_timestamp | timestamp | NO | | NULL | |\n" +
"| c_time | time | NO | | NULL | |\n" +
"| c_char | char(10) | NO | | NULL | |\n" +
"| c_varchar | varchar(10) | NO | | NULL | |\n" +
"| c_blob | blob | NO | | NULL | |\n" +
"| c_text | text | NO | | NULL | |\n" +
"| c_mediumtext | mediumtext | NO | | NULL | |\n" +
"| c_longblob | longblob | NO | | NULL | |\n" +
"+--------------+--------------+------+-----+---------+-------+\n", repository.console("show columns from test3"));
repository.console("alter TABLE test3 add PRIMARY KEY(id)");
assertEquals("+--------------+--------------+------+-----+---------+-------+\n" +
"| Field | Type | Null | Key | Default | Extra |\n" +
"+--------------+--------------+------+-----+---------+-------+\n" +
"| c_decimal_1 | INT(11) | NO | | NULL | |\n" +
"| id | bigint(20) | NO | PRI | NULL | |\n" +
"| c_tinyint | tinyint(4) | NO | UNI | NULL | |\n" +
"| c_smallint | smallint(6) | NO | | NULL | |\n" +
"| c_mediumint | mediumint(9) | NO | | NULL | |\n" +
"| c_int | int(11) | NO | | NULL | |\n" +
"| c_bigint | bigint(20) | NO | | NULL | |\n" +
"| c_date | date | NO | | NULL | |\n" +
"| c_datetime | datetime | NO | | NULL | |\n" +
"| c_timestamp | timestamp | NO | | NULL | |\n" +
"| c_time | time | NO | | NULL | |\n" +
"| c_char | char(10) | NO | | NULL | |\n" +
"| c_varchar | varchar(10) | NO | | NULL | |\n" +
"| c_blob | blob | NO | | NULL | |\n" +
"| c_text | text | NO | | NULL | |\n" +
"| c_mediumtext | mediumtext | NO | | NULL | |\n" +
"| c_longblob | longblob | NO | | NULL | |\n" +
"+--------------+--------------+------+-----+---------+-------+\n", repository.console("show columns from test3"));
}
}