mirror of
https://github.com/alibaba/druid.git
synced 2023-03-04 19:17:16 +00:00
improved sql parser.
This commit is contained in:
@@ -788,6 +788,25 @@ public class SQLUtils {
|
||||
return name;
|
||||
}
|
||||
|
||||
public static boolean nameEquals(String a, String b) {
|
||||
if (a == b) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (a == null || b == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (a.equalsIgnoreCase(b)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
String normalize_a = normalize(a);
|
||||
String normalize_b = normalize(b);
|
||||
|
||||
return normalize_a.equalsIgnoreCase(normalize_b);
|
||||
}
|
||||
|
||||
public static boolean isValue(SQLExpr expr) {
|
||||
if (expr instanceof SQLLiteralExpr) {
|
||||
return true;
|
||||
|
||||
@@ -15,9 +15,7 @@
|
||||
*/
|
||||
package com.alibaba.druid.sql.ast.statement;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.*;
|
||||
|
||||
import com.alibaba.druid.sql.SQLUtils;
|
||||
import com.alibaba.druid.sql.ast.SQLDataType;
|
||||
@@ -237,4 +235,165 @@ public class SQLCreateTableStatement extends SQLStatementImpl implements SQLDDLS
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public List<SQLForeignKeyConstraint> findForeignKey() {
|
||||
List<SQLForeignKeyConstraint> fkList = new ArrayList<SQLForeignKeyConstraint>();
|
||||
for (SQLTableElement element : this.tableElementList) {
|
||||
if (element instanceof SQLForeignKeyConstraint) {
|
||||
fkList.add((SQLForeignKeyConstraint) element);
|
||||
}
|
||||
}
|
||||
return fkList;
|
||||
}
|
||||
|
||||
public boolean hashForeignKey() {
|
||||
for (SQLTableElement element : this.tableElementList) {
|
||||
if (element instanceof SQLForeignKeyConstraint) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean isReferenced(SQLName tableName) {
|
||||
if (tableName == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return isReferenced(tableName.getSimpleName());
|
||||
}
|
||||
|
||||
public boolean isReferenced(String tableName) {
|
||||
if (tableName == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
tableName = SQLUtils.normalize(tableName);
|
||||
|
||||
for (SQLTableElement element : this.tableElementList) {
|
||||
if (element instanceof SQLForeignKeyConstraint) {
|
||||
SQLForeignKeyConstraint fk = (SQLForeignKeyConstraint) element;
|
||||
String refTableName = fk.getReferencedTableName().getSimpleName();
|
||||
|
||||
if (SQLUtils.nameEquals(tableName, refTableName)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static class DependencyComparator implements Comparator {
|
||||
private final Map<String, SQLCreateTableStatement> tables = new HashMap<String, SQLCreateTableStatement>();
|
||||
private final Set<String> referencedTableNames = new HashSet<String>();
|
||||
|
||||
public void add(SQLCreateTableStatement stmt) {
|
||||
String tableName = stmt.getName().getSimpleName();
|
||||
tableName = SQLUtils.normalize(tableName).toLowerCase();
|
||||
tables.put(tableName, stmt);
|
||||
|
||||
for (SQLTableElement element : stmt.tableElementList) {
|
||||
if (element instanceof SQLForeignKeyConstraint) {
|
||||
SQLForeignKeyConstraint fk = (SQLForeignKeyConstraint) element;
|
||||
String refTableName = fk.getReferencedTableName().getSimpleName();
|
||||
refTableName = SQLUtils.normalize(refTableName).toLowerCase();
|
||||
referencedTableNames.add(refTableName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public int compare(Object a, Object b) {
|
||||
return compareStmt((SQLCreateTableStatement)a, (SQLCreateTableStatement) b);
|
||||
}
|
||||
|
||||
public int compareStmt(SQLCreateTableStatement a, SQLCreateTableStatement b) {
|
||||
if (a == b) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (a == null) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (b == null) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
int refVal = isReferenced(a, b);
|
||||
if (refVal == -1) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
refVal = isReferenced(b, a);
|
||||
|
||||
if (refVal == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
boolean a_referenced = isReferenced(a);
|
||||
boolean b_referenced = isReferenced(b);
|
||||
|
||||
if (a_referenced != b_referenced) {
|
||||
return a_referenced ? 1 : -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
boolean isReferenced(SQLCreateTableStatement stmt) {
|
||||
String tableName = stmt.getName().getSimpleName();
|
||||
tableName = SQLUtils.normalize(tableName).toLowerCase();
|
||||
return referencedTableNames.contains(tableName);
|
||||
}
|
||||
|
||||
int isReferenced(SQLCreateTableStatement a, SQLCreateTableStatement b) {
|
||||
if (a == b) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (a.isReferenced(b.getName())) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (SQLTableElement element : a.tableElementList) {
|
||||
if (element instanceof SQLForeignKeyConstraint) {
|
||||
SQLForeignKeyConstraint fk = (SQLForeignKeyConstraint) element;
|
||||
SQLName refTableName = fk.getReferencedTableName();
|
||||
|
||||
SQLCreateTableStatement refStmt = findStatement(refTableName);
|
||||
if (refStmt != null) {
|
||||
int refVal = isReferenced(refStmt, b);
|
||||
if (refVal != 0) {
|
||||
return refVal;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public SQLCreateTableStatement findStatement(SQLName name) {
|
||||
if (name == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
String refTableName = name.getSimpleName();
|
||||
refTableName = SQLUtils.normalize(refTableName).toLowerCase();
|
||||
return tables.get(refTableName);
|
||||
}
|
||||
}
|
||||
|
||||
public static void sort(List<SQLCreateTableStatement> stmtList) {
|
||||
final DependencyComparator comparator = new DependencyComparator();
|
||||
|
||||
for (SQLCreateTableStatement stmt : stmtList) {
|
||||
comparator.add(stmt);
|
||||
}
|
||||
|
||||
Collections.sort(stmtList, comparator);
|
||||
}
|
||||
}
|
||||
|
||||
+11
-10
@@ -58,8 +58,9 @@ public class OracleCreateTableStatement extends SQLCreateTableStatement implemen
|
||||
private Boolean compress;
|
||||
private Integer compressLevel;
|
||||
private boolean compressForOltp;
|
||||
private boolean onCommit;
|
||||
private boolean preserveRows;
|
||||
|
||||
private boolean onCommitPreserveRows;
|
||||
private boolean onCommitDeleteRows;
|
||||
|
||||
private Boolean cache;
|
||||
|
||||
@@ -114,20 +115,20 @@ public class OracleCreateTableStatement extends SQLCreateTableStatement implemen
|
||||
this.cache = cache;
|
||||
}
|
||||
|
||||
public boolean isOnCommit() {
|
||||
return onCommit;
|
||||
public boolean isOnCommitPreserveRows() {
|
||||
return onCommitPreserveRows;
|
||||
}
|
||||
|
||||
public void setOnCommit(boolean onCommit) {
|
||||
this.onCommit = onCommit;
|
||||
public void setOnCommitPreserveRows(boolean onCommitPreserveRows) {
|
||||
this.onCommitPreserveRows = onCommitPreserveRows;
|
||||
}
|
||||
|
||||
public boolean isPreserveRows() {
|
||||
return preserveRows;
|
||||
public boolean isOnCommitDeleteRows() {
|
||||
return onCommitDeleteRows;
|
||||
}
|
||||
|
||||
public void setPreserveRows(boolean preserveRows) {
|
||||
this.preserveRows = preserveRows;
|
||||
public void setOnCommitDeleteRows(boolean onCommitDeleteRows) {
|
||||
this.onCommitDeleteRows = onCommitDeleteRows;
|
||||
}
|
||||
|
||||
public Boolean getLogging() {
|
||||
|
||||
+8
@@ -24,6 +24,7 @@ import com.alibaba.druid.sql.dialect.sqlserver.visitor.SQLServerASTVisitor;
|
||||
public class OracleSetTransactionStatement extends OracleStatementImpl implements OracleStatement {
|
||||
|
||||
private boolean readOnly = false;
|
||||
private boolean write = false;
|
||||
|
||||
private SQLExpr name;
|
||||
|
||||
@@ -51,4 +52,11 @@ public class OracleSetTransactionStatement extends OracleStatementImpl implement
|
||||
this.readOnly = readOnly;
|
||||
}
|
||||
|
||||
public boolean isWrite() {
|
||||
return write;
|
||||
}
|
||||
|
||||
public void setWrite(boolean write) {
|
||||
this.write = write;
|
||||
}
|
||||
}
|
||||
|
||||
+10
-6
@@ -124,12 +124,16 @@ public class OracleCreateTableParser extends SQLCreateTableParser {
|
||||
} else if (lexer.token() == Token.ON) {
|
||||
lexer.nextToken();
|
||||
accept(Token.COMMIT);
|
||||
stmt.setOnCommit(true);
|
||||
continue;
|
||||
} else if (identifierEquals("PRESERVE")) {
|
||||
lexer.nextToken();
|
||||
acceptIdentifier("ROWS");
|
||||
stmt.setPreserveRows(true);
|
||||
|
||||
if (identifierEquals("PRESERVE")) {
|
||||
lexer.nextToken();
|
||||
acceptIdentifier("ROWS");
|
||||
stmt.setOnCommitPreserveRows(true);
|
||||
} else {
|
||||
accept(Token.DELETE);
|
||||
acceptIdentifier("ROWS");
|
||||
stmt.setOnCommitDeleteRows(true);
|
||||
}
|
||||
continue;
|
||||
} else if (identifierEquals("STORAGE")) {
|
||||
OracleStorageClause storage = ((OracleExprParser) this.exprParser).parseStorage();
|
||||
|
||||
+13
-4
@@ -995,13 +995,22 @@ public class OracleStatementParser extends SQLStatementParser {
|
||||
|
||||
if (identifierEquals("READ")) {
|
||||
lexer.nextToken();
|
||||
acceptIdentifier("ONLY");
|
||||
stmt.setReadOnly(true);
|
||||
|
||||
if (identifierEquals("ONLY")) {
|
||||
lexer.nextToken();
|
||||
stmt.setReadOnly(true);
|
||||
} else {
|
||||
acceptIdentifier("WRITE");
|
||||
stmt.setWrite(true);
|
||||
}
|
||||
}
|
||||
|
||||
acceptIdentifier("NAME");
|
||||
if (identifierEquals("NAME")) {
|
||||
lexer.nextToken();
|
||||
|
||||
stmt.setName(this.exprParser.expr());
|
||||
}
|
||||
|
||||
stmt.setName(this.exprParser.expr());
|
||||
return stmt;
|
||||
}
|
||||
|
||||
|
||||
+12
-9
@@ -1503,11 +1503,16 @@ public class OracleOutputVisitor extends SQLASTOutputVisitor implements OracleAS
|
||||
@Override
|
||||
public boolean visit(OracleSetTransactionStatement x) {
|
||||
if (x.isReadOnly()) {
|
||||
print0(ucase ? "SET TRANSACTION READ ONLY NAME " : "set transaction read only name ");
|
||||
print0(ucase ? "SET TRANSACTION READ ONLY" : "set transaction read only");
|
||||
} else {
|
||||
print0(ucase ? "SET TRANSACTION NAME " : "set transaction name ");
|
||||
print0(ucase ? "SET TRANSACTION" : "set transaction");
|
||||
}
|
||||
|
||||
SQLExpr name = x.getName();
|
||||
if (name != null) {
|
||||
print0(ucase ? " NAME " : " name ");
|
||||
name.accept(this);
|
||||
}
|
||||
x.getName().accept(this);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -2127,14 +2132,12 @@ public class OracleOutputVisitor extends SQLASTOutputVisitor implements OracleAS
|
||||
x.getLobStorage().accept(this);
|
||||
}
|
||||
|
||||
if (x.isOnCommit()) {
|
||||
if (x.isOnCommitPreserveRows()) {
|
||||
println();
|
||||
print0(ucase ? "ON COMMIT" : "on commit");
|
||||
}
|
||||
|
||||
if (x.isPreserveRows()) {
|
||||
print0(ucase ? "ON COMMIT PRESERVE ROWS" : "on commit preserve rows");
|
||||
} else if (x.isOnCommitDeleteRows()) {
|
||||
println();
|
||||
print0(ucase ? "PRESERVE ROWS" : "preserve rows");
|
||||
print0(ucase ? "ON COMMIT DELETE ROWS" : "on commit delete rows");
|
||||
}
|
||||
|
||||
if (x.isMonitoring()) {
|
||||
|
||||
@@ -89,6 +89,10 @@ public class Lexer {
|
||||
|
||||
protected String dbType;
|
||||
|
||||
private int startPos;
|
||||
private int posLine;
|
||||
private int posColumn;
|
||||
|
||||
public Lexer(String input){
|
||||
this(input, null);
|
||||
}
|
||||
@@ -249,7 +253,35 @@ public class Lexer {
|
||||
}
|
||||
|
||||
public String info() {
|
||||
return "pos " + this.pos + ", " + this.token + " " + this.stringVal();
|
||||
int line = 1;
|
||||
int column = 1;
|
||||
for (int i = 0; i < startPos; ++i, column++) {
|
||||
char ch = text.charAt(i);
|
||||
if (ch == '\n') {
|
||||
column = 1;
|
||||
line++;
|
||||
}
|
||||
}
|
||||
|
||||
this.posLine = line;
|
||||
this.posColumn = posColumn;
|
||||
|
||||
StringBuilder buf = new StringBuilder();
|
||||
buf
|
||||
.append("pos ")
|
||||
.append(pos)
|
||||
.append(", line ")
|
||||
.append(line)
|
||||
.append(", column ")
|
||||
.append(column)
|
||||
.append(", token ")
|
||||
.append(token);
|
||||
|
||||
if (token == Token.IDENTIFIER || token == Token.LITERAL_ALIAS || token == Token.LITERAL_CHARS) {
|
||||
buf.append(" ").append(stringVal);
|
||||
}
|
||||
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
public final void nextTokenComma() {
|
||||
@@ -286,6 +318,7 @@ public class Lexer {
|
||||
}
|
||||
|
||||
public final void nextTokenValue() {
|
||||
this.startPos = pos;
|
||||
if (ch == ' ') {
|
||||
scanChar();
|
||||
}
|
||||
@@ -317,6 +350,7 @@ public class Lexer {
|
||||
}
|
||||
|
||||
public final void nextToken() {
|
||||
startPos = pos;
|
||||
bufPos = 0;
|
||||
if (comments != null && comments.size() > 0) {
|
||||
comments = null;
|
||||
@@ -1549,4 +1583,27 @@ public class Lexer {
|
||||
public int getLine() {
|
||||
return line;
|
||||
}
|
||||
|
||||
public void computeRowAndColumn() {
|
||||
int line = 1;
|
||||
int column = 1;
|
||||
for (int i = 0; i < pos; ++i) {
|
||||
char ch = text.charAt(i);
|
||||
if (ch == '\n') {
|
||||
column = 1;
|
||||
line++;
|
||||
}
|
||||
}
|
||||
|
||||
this.posLine = line;
|
||||
this.posColumn = posColumn;
|
||||
}
|
||||
|
||||
public int getPosLine() {
|
||||
return posLine;
|
||||
}
|
||||
|
||||
public int getPosColumn() {
|
||||
return posColumn;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -277,7 +277,7 @@ public class SQLParser {
|
||||
// + token + ", actual " + lexer.token() + " "
|
||||
// + lexer.stringVal() + ", pos " + this.lexer.pos());
|
||||
throw new ParserException("syntax error, error in :'" + arround + "',expect " + token + ", actual "
|
||||
+ lexer.token() + " " + lexer.stringVal());
|
||||
+ lexer.token() + " " + lexer.info());
|
||||
}
|
||||
|
||||
public void accept(Token token) {
|
||||
|
||||
Reference in New Issue
Block a user