improved sql parser.

This commit is contained in:
wenshao
2017-07-19 00:40:03 +08:00
parent 1ef867781d
commit 811aa9b4b5
17 changed files with 1042 additions and 34 deletions
@@ -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);
}
}
@@ -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() {
@@ -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;
}
}
@@ -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();
@@ -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;
}
@@ -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) {
@@ -0,0 +1,73 @@
/*
* Copyright 1999-2017 Alibaba Group Holding Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.druid.support.calcite;
import com.alibaba.druid.sql.ast.statement.SQLColumnDefinition;
import org.apache.calcite.rel.type.RelDataType;
import org.apache.calcite.rel.type.RelDataTypeField;
/**
* Created by wenshao on 17/07/2017.
*/
public class DDLColumn implements RelDataTypeField {
private DDLTable table;
private final SQLColumnDefinition column;
private final int index;
private final String name;
public DDLColumn(DDLTable table, SQLColumnDefinition column, int index) {
this.table = table;
this.column = column;
this.name = column.getName().getSimpleName();
this.index = index;
}
@Override
public String getName() {
return name;
}
@Override
public int getIndex() {
return index;
}
@Override
public RelDataType getType() {
return table;
}
@Override
public boolean isDynamicStar() {
return false;
}
@Override
public String getKey() {
return null;
}
@Override
public RelDataType getValue() {
return null;
}
@Override
public RelDataType setValue(RelDataType value) {
return null;
}
}
@@ -0,0 +1,136 @@
/*
* Copyright 1999-2017 Alibaba Group Holding Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.druid.support.calcite;
import com.google.common.collect.*;
import org.apache.calcite.linq4j.tree.Expression;
import org.apache.calcite.materialize.Lattice;
import org.apache.calcite.schema.Function;
import org.apache.calcite.schema.Schema;
import org.apache.calcite.schema.SchemaPlus;
import org.apache.calcite.schema.Table;
import java.util.Collection;
import java.util.Map;
import java.util.Set;
/**
* Created by wenshao on 17/07/2017.
*/
public class DDLSchema implements Schema, SchemaPlus {
private Map<String, DDLTable> tables;
private Map<String, DDLTable> functions;
private Map<String, DDLSchema> subSchemas;
@Override
public Table getTable(String name) {
return tables.get(name);
}
@Override
public Set<String> getTableNames() {
return tables.keySet();
}
@Override
public Collection<Function> getFunctions(String name) {
return null;
}
@Override
public Set<String> getFunctionNames() {
return null;
}
@Override
public SchemaPlus getParentSchema() {
return null;
}
@Override
public String getName() {
return null;
}
@Override
public SchemaPlus getSubSchema(String name) {
return null;
}
@Override
public SchemaPlus add(String name, Schema schema) {
return null;
}
@Override
public void add(String name, Table table) {
}
@Override
public void add(String name, Function function) {
}
@Override
public void add(String name, Lattice lattice) {
}
@Override
public Set<String> getSubSchemaNames() {
return subSchemas.keySet();
}
@Override
public Expression getExpression(SchemaPlus parentSchema, String name) {
return null;
}
@Override
public boolean isMutable() {
return false;
}
@Override
public <T> T unwrap(Class<T> clazz) {
return null;
}
@Override
public void setCacheEnabled(boolean cache) {
}
@Override
public boolean isCacheEnabled() {
return false;
}
public void setPath(ImmutableList<ImmutableList<String>> path) {
}
@Override
public boolean contentsHaveChangedSince(long lastCheck, long now) {
return false;
}
@Override
public Schema snapshot(long now) {
return null;
}
}
@@ -0,0 +1,184 @@
/*
* Copyright 1999-2017 Alibaba Group Holding Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.druid.support.calcite;
import com.alibaba.druid.sql.ast.statement.SQLColumnDefinition;
import com.alibaba.druid.sql.ast.statement.SQLCreateTableStatement;
import com.alibaba.druid.sql.ast.statement.SQLTableElement;
import org.apache.calcite.rel.type.*;
import org.apache.calcite.schema.Schema;
import org.apache.calcite.schema.Statistic;
import org.apache.calcite.schema.Table;
import org.apache.calcite.sql.SqlCollation;
import org.apache.calcite.sql.SqlIdentifier;
import org.apache.calcite.sql.SqlIntervalQualifier;
import org.apache.calcite.sql.type.SqlTypeName;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;
/**
* Created by wenshao on 17/07/2017.
*/
public class DDLTable implements Table, RelDataType {
private SQLCreateTableStatement stmt;
private List<String> fieldNames = new ArrayList<String>();
private List<SQLColumnDefinition> columns = new ArrayList<SQLColumnDefinition>();
private List<RelDataTypeField> fields = new ArrayList<RelDataTypeField>();
public DDLTable(SQLCreateTableStatement stmt) {
this.stmt = stmt;
for (SQLTableElement e : stmt.getTableElementList()) {
if (e instanceof SQLColumnDefinition) {
SQLColumnDefinition column = (SQLColumnDefinition) e;
String fieldName = column.getName().getSimpleName();
fieldNames.add(fieldName);
columns.add(column);
DDLColumn field = new DDLColumn(this, column, fields.size());
this.fields.add(field);
}
}
}
@Override
public RelDataType getRowType(RelDataTypeFactory typeFactory) {
return this;
}
@Override
public Statistic getStatistic() {
return null;
}
@Override
public Schema.TableType getJdbcTableType() {
return Schema.TableType.TABLE;
}
@Override
public boolean isStruct() {
return false;
}
@Override
public List<RelDataTypeField> getFieldList() {
return fields;
}
@Override
public List<String> getFieldNames() {
return fieldNames;
}
@Override
public int getFieldCount() {
return fieldNames.size();
}
@Override
public StructKind getStructKind() {
return null;
}
@Override
public RelDataTypeField getField(String fieldName, boolean caseSensitive, boolean elideRecord) {
return null;
}
@Override
public boolean isNullable() {
return false;
}
@Override
public RelDataType getComponentType() {
return null;
}
@Override
public RelDataType getKeyType() {
return null;
}
@Override
public RelDataType getValueType() {
return null;
}
@Override
public Charset getCharset() {
return null;
}
@Override
public SqlCollation getCollation() {
return null;
}
@Override
public SqlIntervalQualifier getIntervalQualifier() {
return null;
}
@Override
public int getPrecision() {
return 0;
}
@Override
public int getScale() {
return 0;
}
@Override
public SqlTypeName getSqlTypeName() {
return null;
}
@Override
public SqlIdentifier getSqlIdentifier() {
return null;
}
@Override
public String getFullTypeString() {
return null;
}
@Override
public RelDataTypeFamily getFamily() {
return null;
}
@Override
public RelDataTypePrecedenceList getPrecedenceList() {
return null;
}
@Override
public RelDataTypeComparability getComparability() {
return null;
}
@Override
public boolean isDynamicStruct() {
return false;
}
}