improved sql parser. add new feature JdbcUtils.getCreateTableScript, for issue #1342

This commit is contained in:
wenshao
2017-07-24 01:16:43 +08:00
parent 2d8ded4f0c
commit 7a241d7da5
28 changed files with 662 additions and 40 deletions
@@ -42,9 +42,7 @@ import com.alibaba.druid.sql.visitor.SQLASTOutputVisitor;
import com.alibaba.druid.sql.visitor.SchemaStatVisitor;
import com.alibaba.druid.support.logging.Log;
import com.alibaba.druid.support.logging.LogFactory;
import com.alibaba.druid.util.JdbcConstants;
import com.alibaba.druid.util.StringUtils;
import com.alibaba.druid.util.Utils;
import com.alibaba.druid.util.*;
public class SQLUtils {
public static FormatOption DEFAULT_FORMAT_OPTION = new FormatOption(true, true);
@@ -774,6 +772,10 @@ public class SQLUtils {
}
public static String normalize(String name) {
return normalize(name, null);
}
public static String normalize(String name, String dbType) {
if (name == null) {
return null;
}
@@ -782,7 +784,19 @@ public class SQLUtils {
char c0 = name.charAt(0);
char x0 = name.charAt(name.length() - 1);
if ((c0 == '"' && x0 == '"') || (c0 == '`' && x0 == '`')) {
name = name.substring(1, name.length() - 1);
String normalizeName = name.substring(1, name.length() - 1);
if (JdbcConstants.ORACLE.equals(dbType)) {
if (OracleUtils.isKeyword(normalizeName)) {
return name;
}
} else if (JdbcConstants.MYSQL.equals(dbType)) {
if (MySqlUtils.isKeyword(normalizeName)) {
return name;
}
}
return normalizeName;
}
}
@@ -20,7 +20,7 @@ import com.alibaba.druid.sql.visitor.SQLASTVisitor;
public abstract class SQLStatementImpl extends SQLObjectImpl implements SQLStatement {
private String dbType;
protected String dbType;
private boolean afterSemi;
@@ -26,8 +26,10 @@ import com.alibaba.druid.sql.ast.SQLObjectImpl;
import com.alibaba.druid.sql.ast.expr.SQLIdentifierExpr;
import com.alibaba.druid.sql.ast.expr.SQLPropertyExpr;
import com.alibaba.druid.sql.visitor.SQLASTVisitor;
import com.alibaba.druid.util.JdbcConstants;
public class SQLColumnDefinition extends SQLObjectImpl implements SQLTableElement {
protected String dbType;
protected SQLName name;
protected SQLDataType dataType;
@@ -287,6 +289,7 @@ public class SQLColumnDefinition extends SQLObjectImpl implements SQLTableElemen
public SQLColumnDefinition clone() {
SQLColumnDefinition x = new SQLColumnDefinition();
x.setDbType(dbType);
if(name != null) {
x.setName(name.clone());
@@ -341,4 +344,26 @@ public class SQLColumnDefinition extends SQLObjectImpl implements SQLTableElemen
return x;
}
public String getDbType() {
return dbType;
}
public void setDbType(String dbType) {
this.dbType = dbType;
}
public void simplify() {
enable = null;
validate = null;
rely = null;
if (this.name instanceof SQLIdentifierExpr) {
SQLIdentifierExpr identExpr = (SQLIdentifierExpr) this.name;
String columnName = identExpr.getName();
columnName = SQLUtils.normalize(columnName, dbType);
identExpr.setName(columnName);
}
}
}
@@ -23,4 +23,6 @@ public interface SQLConstraint extends SQLObject {
SQLName getName();
void setName(SQLName value);
void simplify();
}
@@ -15,13 +15,16 @@
*/
package com.alibaba.druid.sql.ast.statement;
import com.alibaba.druid.sql.SQLUtils;
import com.alibaba.druid.sql.ast.SQLCommentHint;
import com.alibaba.druid.sql.ast.SQLName;
import com.alibaba.druid.sql.ast.SQLObjectImpl;
import com.alibaba.druid.sql.ast.expr.SQLIdentifierExpr;
import java.util.List;
public abstract class SQLConstraintImpl extends SQLObjectImpl implements SQLConstraint {
protected String dbType;
protected SQLName name;
private Boolean enable;
@@ -90,4 +93,21 @@ public abstract class SQLConstraintImpl extends SQLObjectImpl implements SQLCons
public void setRely(Boolean rely) {
this.rely = rely;
}
public String getDbType() {
return dbType;
}
public void setDbType(String dbType) {
this.dbType = dbType;
}
public void simplify() {
if (this.name instanceof SQLIdentifierExpr) {
SQLIdentifierExpr identExpr = (SQLIdentifierExpr) this.name;
String columnName = identExpr.getName();
columnName = SQLUtils.normalize(columnName, dbType);
identExpr.setName(columnName);
}
}
}
@@ -20,11 +20,13 @@ import java.util.*;
import com.alibaba.druid.sql.SQLUtils;
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.statement.MySqlTableIndex;
import com.alibaba.druid.sql.visitor.SQLASTVisitor;
import com.alibaba.druid.util.JdbcConstants;
import com.alibaba.druid.util.ListDG;
import com.alibaba.druid.util.lang.Consumer;
import org.apache.ibatis.jdbc.SQL;
public class SQLCreateTableStatement extends SQLStatementImpl implements SQLDDLStatement {
@@ -59,6 +61,10 @@ public class SQLCreateTableStatement extends SQLStatementImpl implements SQLDDLS
this.setTableSource(new SQLExprTableSource(name));
}
public void setName(String name) {
this.setName(new SQLIdentifierExpr(name));
}
public SQLExprTableSource getTableSource() {
return tableSource;
}
@@ -414,4 +420,37 @@ public class SQLCreateTableStatement extends SQLStatementImpl implements SQLDDLS
}
}
public void simplify() {
SQLName name = getName();
if (name instanceof SQLPropertyExpr) {
String tableName = ((SQLPropertyExpr) name).getName();
tableName = SQLUtils.normalize(tableName);
setName(tableName);
name = getName();
}
if (name instanceof SQLIdentifierExpr) {
SQLIdentifierExpr identExpr = (SQLIdentifierExpr) name;
String tableName = identExpr.getName();
tableName = SQLUtils.normalize(tableName, dbType);
identExpr.setName(tableName);
}
if (name instanceof SQLIdentifierExpr) {
SQLIdentifierExpr identExpr = (SQLIdentifierExpr) name;
String tableName = identExpr.getName();
tableName = SQLUtils.normalize(tableName);
identExpr.setName(tableName);
}
for (SQLTableElement element : this.tableElementList) {
if (element instanceof SQLColumnDefinition) {
SQLColumnDefinition column = (SQLColumnDefinition) element;
column.simplify();
} else if (element instanceof SQLConstraint) {
((SQLConstraint) element).simplify();
}
}
}
}
@@ -18,8 +18,10 @@ package com.alibaba.druid.sql.ast.statement;
import java.util.ArrayList;
import java.util.List;
import com.alibaba.druid.sql.SQLUtils;
import com.alibaba.druid.sql.ast.SQLExpr;
import com.alibaba.druid.sql.ast.SQLName;
import com.alibaba.druid.sql.ast.expr.SQLIdentifierExpr;
import com.alibaba.druid.sql.visitor.SQLASTVisitor;
public class SQLUnique extends SQLConstraintImpl implements SQLUniqueConstraint, SQLTableElement {
@@ -58,4 +60,17 @@ public class SQLUnique extends SQLConstraintImpl implements SQLUniqueConstraint,
x.addColumn(columnClone);
}
}
public void simplify() {
super.simplify();
for (SQLExpr column : columns) {
if (column instanceof SQLIdentifierExpr) {
SQLIdentifierExpr identExpr = (SQLIdentifierExpr) column;
String columnName = identExpr.getName();
columnName = SQLUtils.normalize(columnName, dbType);
identExpr.setName(columnName);
}
}
}
}
@@ -21,17 +21,17 @@ import com.alibaba.druid.sql.ast.statement.SQLUnique;
import com.alibaba.druid.sql.ast.statement.SQLUniqueConstraint;
import com.alibaba.druid.sql.dialect.mysql.visitor.MySqlASTVisitor;
import com.alibaba.druid.sql.visitor.SQLASTVisitor;
import com.alibaba.druid.util.JdbcConstants;
public class MySqlKey extends SQLUnique implements SQLUniqueConstraint, SQLTableConstraint {
private SQLName indexName;
private String indexType;
private boolean hasConstaint;
public MySqlKey(){
dbType = JdbcConstants.MYSQL;
}
@Override
@@ -45,7 +45,7 @@ public class MySqlKey extends SQLUnique implements SQLUniqueConstraint, SQLTable
if (visitor.visit(this)) {
acceptChild(visitor, this.getName());
acceptChild(visitor, this.getColumns());
acceptChild(visitor, indexName);
acceptChild(visitor, name);
}
visitor.endVisit(this);
}
@@ -58,14 +58,6 @@ public class MySqlKey extends SQLUnique implements SQLUniqueConstraint, SQLTable
this.indexType = indexType;
}
public SQLName getIndexName() {
return indexName;
}
public void setIndexName(SQLName indexName) {
this.indexName = indexName;
}
public boolean isHasConstaint() {
return hasConstaint;
}
@@ -17,11 +17,12 @@ package com.alibaba.druid.sql.dialect.mysql.ast;
import com.alibaba.druid.sql.ast.statement.SQLPrimaryKey;
import com.alibaba.druid.sql.dialect.mysql.visitor.MySqlASTVisitor;
import com.alibaba.druid.util.JdbcConstants;
public class MySqlPrimaryKey extends MySqlKey implements SQLPrimaryKey {
public MySqlPrimaryKey(){
dbType = JdbcConstants.MYSQL;
}
protected void accept0(MySqlASTVisitor visitor) {
@@ -28,7 +28,6 @@ public class MySqlUnique extends MySqlKey implements SQLPrimaryKey {
if (visitor.visit(this)) {
acceptChild(visitor, this.getName());
acceptChild(visitor, this.getColumns());
acceptChild(visitor, this.getIndexName());
}
visitor.endVisit(this);
}
@@ -19,6 +19,7 @@ import com.alibaba.druid.sql.ast.SQLName;
import com.alibaba.druid.sql.ast.statement.SQLForeignKeyImpl;
import com.alibaba.druid.sql.dialect.mysql.visitor.MySqlASTVisitor;
import com.alibaba.druid.sql.visitor.SQLASTVisitor;
import com.alibaba.druid.util.JdbcConstants;
/**
* @author kiki
@@ -34,6 +35,10 @@ public class MysqlForeignKey extends SQLForeignKeyImpl {
protected Option onUpdate;
protected Option onDelete;
public MysqlForeignKey() {
dbType = JdbcConstants.MYSQL;
}
public SQLName getIndexName() {
return indexName;
}
@@ -165,8 +165,13 @@ public class MySqlCreateTableStatement extends SQLCreateTableStatement implement
return tableGroup;
}
public void setTableGroup(SQLName tableGroup) {
this.tableGroup = tableGroup;
}
@Override
public void simplify() {
tableOptions.clear();
super.simplify();
}
}
@@ -774,7 +774,7 @@ public class MySqlCreateTableParser extends SQLCreateTableParser {
if (lexer.token() == Token.IDENTIFIER || lexer.token() == Token.LITERAL_ALIAS) {
SQLName indexName = this.exprParser.name();
if (indexName != null) {
key.setIndexName(indexName);
key.setName(indexName);
}
}
@@ -542,6 +542,7 @@ public class MySqlExprParser extends SQLExprParser {
public SQLColumnDefinition parseColumn() {
SQLColumnDefinition column = new SQLColumnDefinition();
column.setDbType(dbType);
column.setName(name());
column.setDataType(parseDataType());
@@ -813,7 +814,7 @@ public class MySqlExprParser extends SQLExprParser {
if (lexer.token() != Token.LPAREN) {
SQLName indexName = name();
unique.setIndexName(indexName);
unique.setName(indexName);
}
//5.5语法 USING BTREE 放在index 名字后
@@ -585,9 +585,10 @@ public class MySqlOutputVisitor extends SQLASTOutputVisitor implements MySqlASTV
print0(ucase ? "KEY" : "key");
if (x.getIndexName() != null) {
SQLName name = x.getName();
if (name != null) {
print(' ');
x.getIndexName().accept(this);
name.accept(this);
}
if (x.getIndexType() != null) {
@@ -2844,9 +2845,10 @@ public class MySqlOutputVisitor extends SQLASTOutputVisitor implements MySqlASTV
print0(ucase ? "UNIQUE" : "unique");
if (x.getIndexName() != null) {
SQLName name = x.getName();
if (name != null) {
print(' ');
x.getIndexName().accept(this);
name.accept(this);
}
if (x.getIndexType() != null) {
@@ -78,6 +78,29 @@ public class OracleCreateTableStatement extends SQLCreateTableStatement implemen
private SQLName of;
private OIDIndex oidIndex;
private boolean monitoring;
public void simplify() {
tablespace = null;
storage = null;
lobStorage = null;
pctfree = null;
pctused = null;
initrans = null;
maxtrans = null;
pctincrease = null;
logging = null;
compress = null;
compressLevel = null;
compressForOltp = false;
onCommitPreserveRows = false;
onCommitDeleteRows = false;
super.simplify();
}
public OracleCreateTableStatement() {
super (JdbcConstants.ORACLE);
@@ -466,4 +489,5 @@ public class OracleCreateTableStatement extends SQLCreateTableStatement implemen
this.name = name;
}
}
}
@@ -192,6 +192,7 @@ public class SQLServerExprParser extends SQLExprParser {
public SQLColumnDefinition createColumnDefinition() {
SQLColumnDefinition column = new SQLColumnDefinition();
column.setDbType(dbType);
return column;
}
@@ -1980,6 +1980,7 @@ public class SQLExprParser extends SQLParser {
public SQLColumnDefinition createColumnDefinition() {
SQLColumnDefinition column = new SQLColumnDefinition();
column.setDbType(dbType);
return column;
}
@@ -1012,13 +1012,14 @@ public class SQLStatementParser extends SQLParser {
lexer.nextToken();
SQLExpr comment = this.exprParser.primary();
SQLColumnDefinition columnDefinition = new SQLColumnDefinition();
columnDefinition.setName(columnName);
columnDefinition.setComment(comment);
SQLColumnDefinition column = new SQLColumnDefinition();
column.setDbType(dbType);
column.setName(columnName);
column.setComment(comment);
SQLAlterTableAlterColumn changeColumn = new SQLAlterTableAlterColumn();
changeColumn.setColumn(columnDefinition);
changeColumn.setColumn(column);
stmt.addItem(changeColumn);
} else {
@@ -2232,6 +2233,7 @@ public class SQLStatementParser extends SQLParser {
createView.addColumn(constraint);
} else {
SQLColumnDefinition column = new SQLColumnDefinition();
column.setDbType(dbType);
SQLName expr = this.exprParser.name();
column.setName(expr);
@@ -1311,8 +1311,11 @@ public class SchemaStatVisitor extends SQLASTVisitorAdapter {
if (aliasMap != null) {
String alias = x.getAlias();
if (alias != null && !aliasMap.containsKey(alias)) {
putAliasMap(aliasMap, alias, ident);
if (alias != null) {
String table = aliasMap.get(alias);
if (!(ident.equals(table) || alias.equals(table))) {
putAliasMap(aliasMap, alias, ident);
}
}
if (!aliasMap.containsKey(ident)) {
putAliasMap(aliasMap, ident, ident);
@@ -766,4 +766,20 @@ public final class JdbcUtils implements JdbcConstants {
close(conn);
}
}
public static String getCreateTableScript(String dbType, Connection conn) throws SQLException {
return getCreateTableScript(dbType, conn, true, true);
}
public static String getCreateTableScript(String dbType, Connection conn, boolean sorted, boolean simplify) throws SQLException {
if (JdbcConstants.MYSQL.equals(dbType)) {
return MySqlUtils.getCreateTableScript(conn, sorted, simplify);
}
if (JdbcConstants.ORACLE.equals(dbType)) {
return OracleUtils.getCreateTableScript(conn, sorted, simplify);
}
throw new SQLException("getCreateTableScript dbType not support for " + dbType);
}
}
@@ -15,7 +15,9 @@
*/
package com.alibaba.druid.util;
import com.alibaba.druid.sql.SQLUtils;
import com.alibaba.druid.sql.ast.SQLStatement;
import com.alibaba.druid.sql.ast.statement.SQLCreateTableStatement;
import com.mysql.jdbc.ConnectionImpl;
import com.mysql.jdbc.MySQLConnection;
import com.mysql.jdbc.MysqlIO;
@@ -25,11 +27,10 @@ import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.sql.Connection;
import java.sql.Driver;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.sql.*;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
public class MySqlUtils {
@@ -126,4 +127,91 @@ public class MySqlUtils {
return words.contains(name_lower);
}
public static List<String> showTables(Connection conn) throws SQLException {
List<String> tables = new ArrayList<String>();
Statement stmt = null;
ResultSet rs = null;
try {
stmt = conn.createStatement();
rs = stmt.executeQuery("show tables");
while (rs.next()) {
String tableName = rs.getString(1);
tables.add(tableName);
}
} finally {
JdbcUtils.close(rs);
JdbcUtils.close(stmt);
}
return tables;
}
public static List<String> getTableDDL(Connection conn, List<String> tables) throws SQLException {
List<String> ddlList = new ArrayList<String>();
Statement stmt = null;
try {
for (String table : tables) {
if (stmt == null) {
stmt = conn.createStatement();
}
if (isKeyword(table)) {
table = "`" + table + "`";
}
ResultSet rs = null;
try {
rs = stmt.executeQuery("show create table " + table);
if (rs.next()) {
String ddl = rs.getString(2);
ddlList.add(ddl);
}
} finally {
JdbcUtils.close(rs);
}
}
} finally {
JdbcUtils.close(stmt);
}
return ddlList;
}
public static String getCreateTableScript(Connection conn) throws SQLException {
return getCreateTableScript(conn, true, true);
}
public static String getCreateTableScript(Connection conn, boolean sorted, boolean simplify) throws SQLException {
List<String> tables = showTables(conn);
List<String> ddlList = getTableDDL(conn, tables);
StringBuilder buf = new StringBuilder();
for (String ddl : ddlList) {
buf.append(ddl);
buf.append(';');
}
String ddlScript = buf.toString();
if (! (sorted || simplify)) {
return ddlScript;
}
List stmtList = SQLUtils.parseStatements(ddlScript, JdbcConstants.MYSQL);
if (simplify) {
for (Object o : stmtList) {
if (o instanceof SQLCreateTableStatement) {
SQLCreateTableStatement createTableStmt = (SQLCreateTableStatement) o;
createTableStmt.simplify();
}
}
}
if (sorted) {
SQLCreateTableStatement.sort(stmtList);
}
return SQLUtils.toSQLString(stmtList, JdbcConstants.MYSQL);
}
}
@@ -16,17 +16,17 @@
package com.alibaba.druid.util;
import java.sql.*;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Set;
import java.util.*;
import javax.sql.XAConnection;
import javax.transaction.xa.XAException;
import com.alibaba.druid.sql.SQLUtils;
import com.alibaba.druid.sql.ast.expr.SQLPropertyExpr;
import com.alibaba.druid.sql.ast.statement.SQLCreateTableStatement;
import oracle.jdbc.OracleConnection;
import oracle.jdbc.OracleResultSet;
import oracle.jdbc.OracleStatement;
import oracle.jdbc.driver.DatabaseError;
import oracle.jdbc.internal.OraclePreparedStatement;
import oracle.jdbc.xa.client.OracleXAConnection;
import oracle.sql.ROWID;
@@ -222,4 +222,99 @@ public class OracleUtils {
return words.contains(name_lower);
}
public static List<String> showTables(Connection conn) throws SQLException {
List<String> tables = new ArrayList<String>();
Statement stmt = null;
ResultSet rs = null;
try {
stmt = conn.createStatement();
rs = stmt.executeQuery("select table_name from user_tables");
while (rs.next()) {
String tableName = rs.getString(1);
tables.add(tableName);
}
} finally {
JdbcUtils.close(rs);
JdbcUtils.close(stmt);
}
return tables;
}
public static List<String> getTableDDL(Connection conn, String... tables) throws SQLException {
return getTableDDL(conn, Arrays.asList(tables));
}
public static List<String> getTableDDL(Connection conn, List<String> tables) throws SQLException {
List<String> ddlList = new ArrayList<String>();
PreparedStatement pstmt = null;
ResultSet rs = null;
try {
String sql = "select DBMS_METADATA.GET_DDL('TABLE', TABLE_NAME) FROM user_tables";
if (tables.size() > 0) {
sql += "IN (";
for (int i = 0; i < tables.size(); ++i) {
if (i != 0) {
sql += ", ?";
} else {
sql += "?";
}
}
sql += ")";
}
pstmt = conn.prepareStatement(sql);
for (int i = 0; i < tables.size(); ++i) {
pstmt.setString(i + 1, tables.get(i));
}
rs = pstmt.executeQuery();
while (rs.next()) {
String ddl = rs.getString(1);
ddlList.add(ddl);
}
} finally {
JdbcUtils.close(rs);
JdbcUtils.close(pstmt);
}
return ddlList;
}
public static String getCreateTableScript(Connection conn) throws SQLException {
return getCreateTableScript(conn, true, true);
}
public static String getCreateTableScript(Connection conn, boolean sorted, boolean simplify) throws SQLException {
List<String> ddlList = OracleUtils.getTableDDL(conn);
StringBuilder buf = new StringBuilder();
for (String ddl : ddlList) {
buf.append(ddl);
buf.append(';');
}
String ddlScript = buf.toString();
if (! (sorted || simplify)) {
return ddlScript;
}
List stmtList = SQLUtils.parseStatements(ddlScript, JdbcConstants.ORACLE);
if (simplify) {
for (Object o : stmtList) {
if (o instanceof SQLCreateTableStatement) {
SQLCreateTableStatement createTableStmt = (SQLCreateTableStatement) o;
createTableStmt.simplify();
}
}
}
if (sorted) {
SQLCreateTableStatement.sort(stmtList);
}
return SQLUtils.toSQLString(stmtList, JdbcConstants.ORACLE);
}
}