From ac4b6059f0cc95edb04d12f90bc4bd2427206f1e Mon Sep 17 00:00:00 2001 From: wenshao Date: Sat, 20 May 2017 22:03:21 +0800 Subject: [PATCH] improved mysql/oracle sql parser. --- .../alibaba/druid/pool/DruidDataSource.java | 18 ++ .../com/alibaba/druid/sql/ast/SQLExpr.java | 4 +- .../alibaba/druid/sql/ast/SQLExprImpl.java | 4 + .../com/alibaba/druid/sql/ast/SQLName.java | 2 + .../com/alibaba/druid/sql/ast/SQLOver.java | 4 + .../alibaba/druid/sql/ast/SQLPartition.java | 103 +++++++++ .../druid/sql/ast/expr/SQLIdentifierExpr.java | 4 + .../druid/sql/ast/expr/SQLPropertyExpr.java | 10 + .../druid/sql/ast/statement/SQLCheck.java | 7 + .../ast/statement/SQLColumnDefinition.java | 18 ++ .../sql/ast/statement/SQLConstraintImpl.java | 23 ++ .../ast/statement/SQLCreateViewStatement.java | 9 + .../sql/ast/statement/SQLForeignKeyImpl.java | 37 +++ .../druid/sql/ast/statement/SQLUnique.java | 11 +- .../dialect/mysql/ast/expr/MySqlExprImpl.java | 4 +- .../mysql/ast/expr/MySqlOutFileExpr.java | 4 + .../dialect/mysql/ast/expr/MySqlUserName.java | 9 +- .../mysql/parser/MySqlSelectParser.java | 32 +++ .../oracle/ast/OracleSQLObjectImpl.java | 5 + .../ast/clause/OracleLobStorageClause.java | 34 +++ .../ast/clause/OracleStorageClause.java | 2 + .../ast/expr/OracleAnalyticWindowing.java | 9 + .../oracle/ast/expr/OracleDbLinkExpr.java | 11 + .../ast/stmt/OracleCreateTableStatement.java | 25 ++- .../oracle/ast/stmt/OraclePrimaryKey.java | 14 +- .../dialect/oracle/ast/stmt/OracleSelect.java | 11 - .../ast/stmt/OracleSelectQueryBlock.java | 17 ++ .../ast/stmt/OracleSupplementalIdKey.java | 78 +++++++ ...ate.java => OracleSupplementalLogGrp.java} | 145 ++++++------ .../ast/stmt/OracleUsingIndexClause.java | 62 ++++++ .../parser/OracleCreateTableParser.java | 210 +++++++++++++++++- .../oracle/parser/OracleExprParser.java | 96 +++++++- .../dialect/oracle/parser/OracleLexer.java | 4 +- .../oracle/parser/OracleSelectParser.java | 85 +++++-- .../oracle/parser/OracleStatementParser.java | 12 +- .../oracle/visitor/OracleASTVisitor.java | 12 +- .../visitor/OracleASTVisitorAdapter.java | 30 ++- .../oracle/visitor/OracleOutputVisitor.java | 184 +++++++++++---- .../visitor/OracleSchemaStatVisitor.java | 31 ++- .../expr/SQLServerObjectReferenceExpr.java | 7 + .../com/alibaba/druid/sql/parser/Lexer.java | 2 +- .../sql/parser/SQLCreateTableParser.java | 27 ++- .../druid/sql/parser/SQLExprParser.java | 45 +++- .../druid/sql/parser/SQLSelectParser.java | 36 ++- .../druid/sql/parser/SQLStatementParser.java | 12 +- .../sql/visitor/SQLASTOutputVisitor.java | 12 +- .../mysql/create/MySqlCreateTableTest76.java | 113 +++++----- .../select/MySqlSelectTest_comma_src.java | 66 ++++++ .../oracle/alter/OracleAlterTableTest19.java | 9 +- .../sql/oracle/block/OracleBlockTest10.java | 2 +- .../create/OracleCreateTableTest18.java | 27 +-- .../visitor/OracleASTVisitorAdapterTest.java | 2 - src/test/resources/bvt/parser/mysql-8.txt | 5 +- 53 files changed, 1449 insertions(+), 296 deletions(-) create mode 100644 src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/stmt/OracleSupplementalIdKey.java rename src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/stmt/{OracleSelectForUpdate.java => OracleSupplementalLogGrp.java} (50%) create mode 100644 src/test/java/com/alibaba/druid/bvt/sql/mysql/select/MySqlSelectTest_comma_src.java diff --git a/src/main/java/com/alibaba/druid/pool/DruidDataSource.java b/src/main/java/com/alibaba/druid/pool/DruidDataSource.java index dc04de2c51..bcb398c1f8 100644 --- a/src/main/java/com/alibaba/druid/pool/DruidDataSource.java +++ b/src/main/java/com/alibaba/druid/pool/DruidDataSource.java @@ -181,6 +181,24 @@ public class DruidDataSource extends DruidAbstractDataSource implements DruidDat } public void configFromPropety(Properties properties) { + { + String property = properties.getProperty("druid.url"); + if (property != null) { + this.setUrl(property); + } + } + { + String property = properties.getProperty("druid.username"); + if (property != null) { + this.setUsername(property); + } + } + { + String property = properties.getProperty("druid.password"); + if (property != null) { + this.setPassword(property); + } + } { Boolean value = getBoolean(properties, "druid.testWhileIdle"); if (value != null) { diff --git a/src/main/java/com/alibaba/druid/sql/ast/SQLExpr.java b/src/main/java/com/alibaba/druid/sql/ast/SQLExpr.java index 65580e2367..fa52741ea8 100644 --- a/src/main/java/com/alibaba/druid/sql/ast/SQLExpr.java +++ b/src/main/java/com/alibaba/druid/sql/ast/SQLExpr.java @@ -15,6 +15,6 @@ */ package com.alibaba.druid.sql.ast; -public interface SQLExpr extends SQLObject { - +public interface SQLExpr extends SQLObject, Cloneable { + SQLExpr clone(); } diff --git a/src/main/java/com/alibaba/druid/sql/ast/SQLExprImpl.java b/src/main/java/com/alibaba/druid/sql/ast/SQLExprImpl.java index e12cdc7f25..6343a0dc1b 100644 --- a/src/main/java/com/alibaba/druid/sql/ast/SQLExprImpl.java +++ b/src/main/java/com/alibaba/druid/sql/ast/SQLExprImpl.java @@ -24,4 +24,8 @@ public abstract class SQLExprImpl extends SQLObjectImpl implements SQLExpr { public abstract boolean equals(Object o); public abstract int hashCode(); + + public SQLExpr clone() { + throw new UnsupportedOperationException(); + } } diff --git a/src/main/java/com/alibaba/druid/sql/ast/SQLName.java b/src/main/java/com/alibaba/druid/sql/ast/SQLName.java index 806cf375a3..fd0e02f8fb 100644 --- a/src/main/java/com/alibaba/druid/sql/ast/SQLName.java +++ b/src/main/java/com/alibaba/druid/sql/ast/SQLName.java @@ -17,4 +17,6 @@ package com.alibaba.druid.sql.ast; public interface SQLName extends SQLExpr { String getSimpleName(); + + SQLName clone(); } diff --git a/src/main/java/com/alibaba/druid/sql/ast/SQLOver.java b/src/main/java/com/alibaba/druid/sql/ast/SQLOver.java index b43a52477c..60addd4546 100644 --- a/src/main/java/com/alibaba/druid/sql/ast/SQLOver.java +++ b/src/main/java/com/alibaba/druid/sql/ast/SQLOver.java @@ -208,6 +208,10 @@ public class SQLOver extends SQLObjectImpl { return result; } + public SQLExpr clone() { + throw new UnsupportedOperationException(); + } + public static enum WindowingType { ROWS, RANGE } diff --git a/src/main/java/com/alibaba/druid/sql/ast/SQLPartition.java b/src/main/java/com/alibaba/druid/sql/ast/SQLPartition.java index 9cae45f536..bc3a7a2ba9 100644 --- a/src/main/java/com/alibaba/druid/sql/ast/SQLPartition.java +++ b/src/main/java/com/alibaba/druid/sql/ast/SQLPartition.java @@ -18,6 +18,8 @@ package com.alibaba.druid.sql.ast; import java.util.ArrayList; import java.util.List; +import com.alibaba.druid.sql.dialect.oracle.ast.clause.OracleLobStorageClause; +import com.alibaba.druid.sql.dialect.oracle.ast.clause.OracleStorageClause; import com.alibaba.druid.sql.visitor.SQLASTVisitor; public class SQLPartition extends SQLObjectImpl { @@ -39,6 +41,16 @@ public class SQLPartition extends SQLObjectImpl { protected SQLExpr engine; protected SQLExpr comment; + private SQLExpr ptcfree; + private SQLExpr pctused; + private SQLExpr initrans; + private SQLExpr maxtrans; + private OracleStorageClause storage; + private SQLName tablespace; + private Boolean compress; + + private OracleLobStorageClause lobStorage; + public SQLName getName() { return name; @@ -174,7 +186,98 @@ public class SQLPartition extends SQLObjectImpl { acceptChild(visitor, minRows); acceptChild(visitor, engine); acceptChild(visitor, comment); + + acceptChild(visitor, ptcfree); + acceptChild(visitor, pctused); + acceptChild(visitor, initrans); + acceptChild(visitor, maxtrans); + acceptChild(visitor, storage); } visitor.endVisit(this); } + + public SQLExpr getPtcfree() { + return ptcfree; + } + + public void setPtcfree(SQLExpr ptcfree) { + if (ptcfree != null) { + ptcfree.setParent(this); + } + this.ptcfree = ptcfree; + } + + public SQLExpr getPctused() { + return pctused; + } + + public void setPctused(SQLExpr pctused) { + if (pctused != null) { + pctused.setParent(this); + } + this.pctused = pctused; + } + + public SQLExpr getInitrans() { + return initrans; + } + + public void setInitrans(SQLExpr initrans) { + if (initrans != null) { + initrans.setParent(this); + } + this.initrans = initrans; + } + + public SQLExpr getMaxtrans() { + return maxtrans; + } + + public void setMaxtrans(SQLExpr maxtrans) { + if (maxtrans != null) { + maxtrans.setParent(this); + } + this.maxtrans = maxtrans; + } + + public OracleStorageClause getStorage() { + return storage; + } + + public void setStorage(OracleStorageClause storage) { + if (storage != null) { + storage.setParent(this); + } + this.storage = storage; + } + + public SQLName getTablespace() { + return tablespace; + } + + public void setTablespace(SQLName tablespace) { + if (tablespace != null) { + tablespace.setParent(this); + } + this.tablespace = tablespace; + } + + public Boolean getCompress() { + return compress; + } + + public void setCompress(Boolean compress) { + this.compress = compress; + } + + public OracleLobStorageClause getLobStorage() { + return lobStorage; + } + + public void setLobStorage(OracleLobStorageClause lobStorage) { + if (lobStorage != null) { + lobStorage.setParent(this); + } + this.lobStorage = lobStorage; + } } diff --git a/src/main/java/com/alibaba/druid/sql/ast/expr/SQLIdentifierExpr.java b/src/main/java/com/alibaba/druid/sql/ast/expr/SQLIdentifierExpr.java index d2b8cf84bc..38cf6be547 100644 --- a/src/main/java/com/alibaba/druid/sql/ast/expr/SQLIdentifierExpr.java +++ b/src/main/java/com/alibaba/druid/sql/ast/expr/SQLIdentifierExpr.java @@ -100,4 +100,8 @@ public class SQLIdentifierExpr extends SQLExprImpl implements SQLName { public String toString() { return this.name; } + + public SQLIdentifierExpr clone() { + return new SQLIdentifierExpr(this.name); + } } diff --git a/src/main/java/com/alibaba/druid/sql/ast/expr/SQLPropertyExpr.java b/src/main/java/com/alibaba/druid/sql/ast/expr/SQLPropertyExpr.java index af72791e1f..34b03dd676 100644 --- a/src/main/java/com/alibaba/druid/sql/ast/expr/SQLPropertyExpr.java +++ b/src/main/java/com/alibaba/druid/sql/ast/expr/SQLPropertyExpr.java @@ -18,6 +18,7 @@ package com.alibaba.druid.sql.ast.expr; import com.alibaba.druid.sql.ast.SQLExpr; import com.alibaba.druid.sql.ast.SQLExprImpl; import com.alibaba.druid.sql.ast.SQLName; +import com.alibaba.druid.sql.parser.ParserException; import com.alibaba.druid.sql.visitor.SQLASTVisitor; public class SQLPropertyExpr extends SQLExprImpl implements SQLName { @@ -109,4 +110,13 @@ public class SQLPropertyExpr extends SQLExprImpl implements SQLName { return true; } + public SQLPropertyExpr clone() { + SQLPropertyExpr propertyExpr = new SQLPropertyExpr(); + propertyExpr.name = this.name; + if (owner != null) { + propertyExpr.setOwner(owner.clone()); + } + return propertyExpr; + } + } diff --git a/src/main/java/com/alibaba/druid/sql/ast/statement/SQLCheck.java b/src/main/java/com/alibaba/druid/sql/ast/statement/SQLCheck.java index a329f8bb43..28340a3002 100644 --- a/src/main/java/com/alibaba/druid/sql/ast/statement/SQLCheck.java +++ b/src/main/java/com/alibaba/druid/sql/ast/statement/SQLCheck.java @@ -50,4 +50,11 @@ public class SQLCheck extends SQLConstraintImpl implements SQLTableElement { visitor.endVisit(this); } + public void cloneTo(SQLCheck x) { + super.cloneTo(x); + + if (expr != null) { + expr = expr.clone(); + } + } } diff --git a/src/main/java/com/alibaba/druid/sql/ast/statement/SQLColumnDefinition.java b/src/main/java/com/alibaba/druid/sql/ast/statement/SQLColumnDefinition.java index 309adb160c..a9aeea79de 100644 --- a/src/main/java/com/alibaba/druid/sql/ast/statement/SQLColumnDefinition.java +++ b/src/main/java/com/alibaba/druid/sql/ast/statement/SQLColumnDefinition.java @@ -33,6 +33,8 @@ public class SQLColumnDefinition extends SQLObjectImpl implements SQLTableElemen protected SQLExpr comment; protected Boolean enable; + protected Boolean validate; + protected Boolean rely; // for mysql protected boolean autoIncrement = false; @@ -69,6 +71,22 @@ public class SQLColumnDefinition extends SQLObjectImpl implements SQLTableElemen this.enable = enable; } + public Boolean getValidate() { + return validate; + } + + public void setValidate(Boolean validate) { + this.validate = validate; + } + + public Boolean getRely() { + return rely; + } + + public void setRely(Boolean rely) { + this.rely = rely; + } + public SQLName getName() { return name; } diff --git a/src/main/java/com/alibaba/druid/sql/ast/statement/SQLConstraintImpl.java b/src/main/java/com/alibaba/druid/sql/ast/statement/SQLConstraintImpl.java index 8a4a10f996..2efc338538 100644 --- a/src/main/java/com/alibaba/druid/sql/ast/statement/SQLConstraintImpl.java +++ b/src/main/java/com/alibaba/druid/sql/ast/statement/SQLConstraintImpl.java @@ -25,6 +25,8 @@ public abstract class SQLConstraintImpl extends SQLObjectImpl implements SQLCons private SQLName name; private Boolean enable; + private Boolean validate; + private Boolean rely; public List hints; @@ -58,4 +60,25 @@ public abstract class SQLConstraintImpl extends SQLObjectImpl implements SQLCons this.enable = enable; } + public void cloneTo(SQLConstraint x) { + if (name != null) { + x.setName(name.clone()); + } + } + + public Boolean getValidate() { + return validate; + } + + public void setValidate(Boolean validate) { + this.validate = validate; + } + + public Boolean getRely() { + return rely; + } + + public void setRely(Boolean rely) { + this.rely = rely; + } } diff --git a/src/main/java/com/alibaba/druid/sql/ast/statement/SQLCreateViewStatement.java b/src/main/java/com/alibaba/druid/sql/ast/statement/SQLCreateViewStatement.java index 11d69c76d9..7d61109a63 100644 --- a/src/main/java/com/alibaba/druid/sql/ast/statement/SQLCreateViewStatement.java +++ b/src/main/java/com/alibaba/druid/sql/ast/statement/SQLCreateViewStatement.java @@ -29,6 +29,7 @@ import com.alibaba.druid.sql.visitor.SQLASTVisitor; public class SQLCreateViewStatement extends SQLStatementImpl implements SQLDDLStatement { private boolean orReplace = false; + private boolean force = false; protected SQLName name; protected SQLSelect subQuery; protected boolean ifNotExists = false; @@ -140,6 +141,14 @@ public class SQLCreateViewStatement extends SQLStatementImpl implements SQLDDLSt this.sqlSecurity = sqlSecurity; } + public boolean isForce() { + return force; + } + + public void setForce(boolean force) { + this.force = force; + } + public void output(StringBuffer buf) { buf.append("CREATE VIEW "); this.name.output(buf); diff --git a/src/main/java/com/alibaba/druid/sql/ast/statement/SQLForeignKeyImpl.java b/src/main/java/com/alibaba/druid/sql/ast/statement/SQLForeignKeyImpl.java index bf7948ac63..50876750d9 100644 --- a/src/main/java/com/alibaba/druid/sql/ast/statement/SQLForeignKeyImpl.java +++ b/src/main/java/com/alibaba/druid/sql/ast/statement/SQLForeignKeyImpl.java @@ -26,6 +26,8 @@ public class SQLForeignKeyImpl extends SQLConstraintImpl implements SQLForeignKe private SQLName referencedTableName; private List referencingColumns = new ArrayList(); private List referencedColumns = new ArrayList(); + private boolean onDeleteCascade = false; + private boolean onDeleteSetNull = false; public SQLForeignKeyImpl(){ @@ -51,6 +53,22 @@ public class SQLForeignKeyImpl extends SQLConstraintImpl implements SQLForeignKe return referencedColumns; } + public boolean isOnDeleteCascade() { + return onDeleteCascade; + } + + public void setOnDeleteCascade(boolean onDeleteCascade) { + this.onDeleteCascade = onDeleteCascade; + } + + public boolean isOnDeleteSetNull() { + return onDeleteSetNull; + } + + public void setOnDeleteSetNull(boolean onDeleteSetNull) { + this.onDeleteSetNull = onDeleteSetNull; + } + @Override protected void accept0(SQLASTVisitor visitor) { if (visitor.visit(this)) { @@ -62,4 +80,23 @@ public class SQLForeignKeyImpl extends SQLConstraintImpl implements SQLForeignKe visitor.endVisit(this); } + public void cloneTo(SQLForeignKeyConstraint x) { + super.cloneTo(x); + + if (referencedTableName != null) { + x.setReferencedTableName(referencedTableName.clone()); + } + + for (SQLName column : referencingColumns) { + SQLName columnClone = column.clone(); + columnClone.setParent(x); + x.getReferencingColumns().add(columnClone); + } + + for (SQLName column : referencedColumns) { + SQLName columnClone = column.clone(); + columnClone.setParent(x); + x.getReferencedColumns().add(columnClone); + } + } } diff --git a/src/main/java/com/alibaba/druid/sql/ast/statement/SQLUnique.java b/src/main/java/com/alibaba/druid/sql/ast/statement/SQLUnique.java index 497be5b4ec..11e07be5fd 100644 --- a/src/main/java/com/alibaba/druid/sql/ast/statement/SQLUnique.java +++ b/src/main/java/com/alibaba/druid/sql/ast/statement/SQLUnique.java @@ -19,11 +19,12 @@ import java.util.ArrayList; import java.util.List; import com.alibaba.druid.sql.ast.SQLExpr; +import com.alibaba.druid.sql.ast.SQLName; import com.alibaba.druid.sql.visitor.SQLASTVisitor; public class SQLUnique extends SQLConstraintImpl implements SQLUniqueConstraint, SQLTableElement { - private final List columns = new ArrayList(); + protected final List columns = new ArrayList(); public SQLUnique(){ @@ -49,4 +50,12 @@ public class SQLUnique extends SQLConstraintImpl implements SQLUniqueConstraint, visitor.endVisit(this); } + public void cloneTo(SQLUnique x) { + super.cloneTo(x); + + for (SQLExpr column : columns) { + SQLExpr columnClone = column.clone(); + x.addColumn(columnClone); + } + } } diff --git a/src/main/java/com/alibaba/druid/sql/dialect/mysql/ast/expr/MySqlExprImpl.java b/src/main/java/com/alibaba/druid/sql/dialect/mysql/ast/expr/MySqlExprImpl.java index b8ec66838e..e9426a3bb0 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/mysql/ast/expr/MySqlExprImpl.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/mysql/ast/expr/MySqlExprImpl.java @@ -19,5 +19,7 @@ import com.alibaba.druid.sql.ast.SQLExpr; import com.alibaba.druid.sql.dialect.mysql.ast.MySqlObjectImpl; public abstract class MySqlExprImpl extends MySqlObjectImpl implements SQLExpr { - + public SQLExpr clone() { + throw new UnsupportedOperationException(); + } } diff --git a/src/main/java/com/alibaba/druid/sql/dialect/mysql/ast/expr/MySqlOutFileExpr.java b/src/main/java/com/alibaba/druid/sql/dialect/mysql/ast/expr/MySqlOutFileExpr.java index 373220f96c..cc243d33b5 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/mysql/ast/expr/MySqlOutFileExpr.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/mysql/ast/expr/MySqlOutFileExpr.java @@ -122,4 +122,8 @@ public class MySqlOutFileExpr extends MySqlObjectImpl implements SQLExpr { this.ignoreLinesNumber = ignoreLinesNumber; } + public SQLExpr clone() { + throw new UnsupportedOperationException(); + } + } diff --git a/src/main/java/com/alibaba/druid/sql/dialect/mysql/ast/expr/MySqlUserName.java b/src/main/java/com/alibaba/druid/sql/dialect/mysql/ast/expr/MySqlUserName.java index 870337d9ae..92b33896a2 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/mysql/ast/expr/MySqlUserName.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/mysql/ast/expr/MySqlUserName.java @@ -18,7 +18,7 @@ package com.alibaba.druid.sql.dialect.mysql.ast.expr; import com.alibaba.druid.sql.ast.SQLName; import com.alibaba.druid.sql.dialect.mysql.visitor.MySqlASTVisitor; -public class MySqlUserName extends MySqlExprImpl implements SQLName { +public class MySqlUserName extends MySqlExprImpl implements SQLName, Cloneable { private String userName; private String host; @@ -52,4 +52,11 @@ public class MySqlUserName extends MySqlExprImpl implements SQLName { public String toString() { return getSimpleName(); } + + public MySqlUserName clone() { + MySqlUserName x = new MySqlUserName(); + x.userName = userName; + x.host = host; + return x; + } } diff --git a/src/main/java/com/alibaba/druid/sql/dialect/mysql/parser/MySqlSelectParser.java b/src/main/java/com/alibaba/druid/sql/dialect/mysql/parser/MySqlSelectParser.java index 3516e9eed3..b7afc8df33 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/mysql/parser/MySqlSelectParser.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/mysql/parser/MySqlSelectParser.java @@ -374,6 +374,38 @@ public class MySqlSelectParser extends SQLSelectParser { } } + protected SQLTableSource primaryTableSourceRest(SQLTableSource tableSource) { + if (lexer.token() == Token.USE) { + lexer.nextToken(); + MySqlUseIndexHint hint = new MySqlUseIndexHint(); + parseIndexHint(hint); + tableSource.getHints().add(hint); + } + + if (identifierEquals("IGNORE")) { + lexer.nextToken(); + MySqlIgnoreIndexHint hint = new MySqlIgnoreIndexHint(); + parseIndexHint(hint); + tableSource.getHints().add(hint); + } + + if (identifierEquals("FORCE")) { + lexer.nextToken(); + MySqlForceIndexHint hint = new MySqlForceIndexHint(); + parseIndexHint(hint); + tableSource.getHints().add(hint); + } + + if (lexer.token() == Token.PARTITION) { + lexer.nextToken(); + accept(Token.LPAREN); + this.exprParser.names(((SQLExprTableSource) tableSource).getPartitions(), tableSource); + accept(Token.RPAREN); + } + + return tableSource; + } + protected SQLTableSource parseTableSourceRest(SQLTableSource tableSource) { if (identifierEquals("USING")) { return tableSource; diff --git a/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/OracleSQLObjectImpl.java b/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/OracleSQLObjectImpl.java index f12c004b2c..3dc5088b86 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/OracleSQLObjectImpl.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/OracleSQLObjectImpl.java @@ -15,6 +15,7 @@ */ package com.alibaba.druid.sql.dialect.oracle.ast; +import com.alibaba.druid.sql.ast.SQLExpr; import com.alibaba.druid.sql.ast.SQLObjectImpl; import com.alibaba.druid.sql.dialect.oracle.visitor.OracleASTVisitor; import com.alibaba.druid.sql.visitor.SQLASTVisitor; @@ -31,4 +32,8 @@ public abstract class OracleSQLObjectImpl extends SQLObjectImpl implements Oracl } public abstract void accept0(OracleASTVisitor visitor); + + public SQLExpr clone() { + throw new UnsupportedOperationException(); + } } diff --git a/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/clause/OracleLobStorageClause.java b/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/clause/OracleLobStorageClause.java index af717bdc2f..f1c7c0a138 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/clause/OracleLobStorageClause.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/clause/OracleLobStorageClause.java @@ -41,6 +41,11 @@ public class OracleLobStorageClause extends OracleSQLObjectImpl { private Boolean compress; private Boolean keepDuplicate; + private boolean retention; + + private OracleStorageClause storageClause; + + private SQLExpr pctversion; @Override public void accept0(OracleASTVisitor visitor) { @@ -127,4 +132,33 @@ public class OracleLobStorageClause extends OracleSQLObjectImpl { this.keepDuplicate = keepDuplicate; } + public boolean isRetention() { + return retention; + } + + public void setRetention(boolean retention) { + this.retention = retention; + } + + public OracleStorageClause getStorageClause() { + return storageClause; + } + + public void setStorageClause(OracleStorageClause storageClause) { + if (storageClause != null) { + storageClause.setParent(this); + } + this.storageClause = storageClause; + } + + public SQLExpr getPctversion() { + return pctversion; + } + + public void setPctversion(SQLExpr pctversion) { + if (pctversion != null) { + pctversion.setParent(this); + } + this.pctversion = pctversion; + } } diff --git a/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/clause/OracleStorageClause.java b/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/clause/OracleStorageClause.java index 9ea145bc3f..030897e458 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/clause/OracleStorageClause.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/clause/OracleStorageClause.java @@ -150,4 +150,6 @@ public class OracleStorageClause extends OracleSQLObjectImpl { public static enum FlashCacheType { KEEP, NONE, DEFAULT } + + } diff --git a/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/expr/OracleAnalyticWindowing.java b/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/expr/OracleAnalyticWindowing.java index 82c4bc4f0d..cdcd2dee27 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/expr/OracleAnalyticWindowing.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/expr/OracleAnalyticWindowing.java @@ -57,6 +57,15 @@ public class OracleAnalyticWindowing extends SQLObjectImpl implements OracleExpr this.type = type; } + public SQLExpr clone() { + OracleAnalyticWindowing x = new OracleAnalyticWindowing(); + x.type = type; + if (expr != null) { + this.setExpr(expr.clone()); + } + return x; + } + public static enum Type { ROWS, RANGE; } diff --git a/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/expr/OracleDbLinkExpr.java b/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/expr/OracleDbLinkExpr.java index 4ab494371e..0a7d2f1297 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/expr/OracleDbLinkExpr.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/expr/OracleDbLinkExpr.java @@ -106,4 +106,15 @@ public class OracleDbLinkExpr extends SQLExprImpl implements SQLName, OracleExpr } return true; } + + public OracleDbLinkExpr clone() { + OracleDbLinkExpr x = new OracleDbLinkExpr(); + + if (expr != null) { + expr = expr.clone(); + } + x.dbLink = dbLink; + + return x; + } } diff --git a/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/stmt/OracleCreateTableStatement.java b/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/stmt/OracleCreateTableStatement.java index 27ff1499a9..514c7e7d0e 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/stmt/OracleCreateTableStatement.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/stmt/OracleCreateTableStatement.java @@ -53,9 +53,13 @@ public class OracleCreateTableStatement extends SQLCreateTableStatement implemen private Boolean cache; - private SQLPartitionBy partitioning; + private SQLPartitionBy partitioning; private DeferredSegmentCreation deferredSegmentCreation; + + private boolean forOLTP; + + private Boolean enableRowMovement; public OracleCreateTableStatement() { super (JdbcConstants.ORACLE); @@ -206,9 +210,28 @@ public class OracleCreateTableStatement extends SQLCreateTableStatement implemen } public void setStorage(OracleStorageClause storage) { + if (storage != null) { + storage.setParent(this); + } this.storage = storage; } + public boolean isForOLTP() { + return forOLTP; + } + + public void setForOLTP(boolean forOLTP) { + this.forOLTP = forOLTP; + } + + public Boolean getEnableRowMovement() { + return enableRowMovement; + } + + public void setEnableRowMovement(Boolean enableRowMovement) { + this.enableRowMovement = enableRowMovement; + } + public void accept0(OracleASTVisitor visitor) { if (visitor.visit(this)) { this.acceptChild(visitor, tableSource); diff --git a/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/stmt/OraclePrimaryKey.java b/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/stmt/OraclePrimaryKey.java index 22acdeda7f..cdd7595fa6 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/stmt/OraclePrimaryKey.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/stmt/OraclePrimaryKey.java @@ -21,14 +21,15 @@ import java.util.List; import com.alibaba.druid.sql.ast.SQLExpr; import com.alibaba.druid.sql.ast.SQLName; import com.alibaba.druid.sql.ast.statement.SQLPrimaryKey; +import com.alibaba.druid.sql.ast.statement.SQLPrimaryKeyImpl; import com.alibaba.druid.sql.ast.statement.SQLTableElement; import com.alibaba.druid.sql.dialect.oracle.ast.OracleSQLObjectImpl; import com.alibaba.druid.sql.dialect.oracle.visitor.OracleASTVisitor; +import com.alibaba.druid.sql.visitor.SQLASTVisitor; -public class OraclePrimaryKey extends OracleSQLObjectImpl implements OracleConstraint, SQLPrimaryKey, SQLTableElement { +public class OraclePrimaryKey extends SQLPrimaryKeyImpl implements OracleConstraint, SQLPrimaryKey, SQLTableElement { private SQLName name; - private List columns = new ArrayList(); private OracleUsingIndexClause using; private SQLName exceptionsInto; @@ -36,6 +37,11 @@ public class OraclePrimaryKey extends OracleSQLObjectImpl implements OracleConst private Initially initially; private Boolean deferrable; + @Override + protected void accept0(SQLASTVisitor visitor) { + this.accept0((OracleASTVisitor) visitor); + } + @Override public void accept0(OracleASTVisitor visitor) { if (visitor.visit(this)) { @@ -67,10 +73,6 @@ public class OraclePrimaryKey extends OracleSQLObjectImpl implements OracleConst return columns; } - public void setColumns(List columns) { - this.columns = columns; - } - public OracleUsingIndexClause getUsing() { return using; } diff --git a/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/stmt/OracleSelect.java b/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/stmt/OracleSelect.java index 9816870e35..eff7331295 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/stmt/OracleSelect.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/stmt/OracleSelect.java @@ -21,8 +21,6 @@ import com.alibaba.druid.sql.dialect.oracle.visitor.OracleASTVisitor; import com.alibaba.druid.sql.visitor.SQLASTVisitor; public class OracleSelect extends SQLSelect { - - private OracleSelectForUpdate forUpdate; private OracleSelectRestriction restriction; public OracleSelect(){ @@ -37,14 +35,6 @@ public class OracleSelect extends SQLSelect { this.restriction = restriction; } - public OracleSelectForUpdate getForUpdate() { - return this.forUpdate; - } - - public void setForUpdate(OracleSelectForUpdate forUpdate) { - this.forUpdate = forUpdate; - } - public void output(StringBuffer buf) { this.query.output(buf); buf.append(" "); @@ -64,7 +54,6 @@ public class OracleSelect extends SQLSelect { acceptChild(visitor, this.query); acceptChild(visitor, this.restriction); acceptChild(visitor, this.orderBy); - acceptChild(visitor, this.forUpdate); } visitor.endVisit(this); } diff --git a/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/stmt/OracleSelectQueryBlock.java b/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/stmt/OracleSelectQueryBlock.java index d8cabbf2ed..8270b401cc 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/stmt/OracleSelectQueryBlock.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/stmt/OracleSelectQueryBlock.java @@ -20,6 +20,7 @@ import java.util.List; import com.alibaba.druid.sql.SQLUtils; import com.alibaba.druid.sql.ast.SQLCommentHint; +import com.alibaba.druid.sql.ast.SQLExpr; import com.alibaba.druid.sql.ast.statement.SQLSelectQueryBlock; import com.alibaba.druid.sql.dialect.oracle.ast.clause.ModelClause; import com.alibaba.druid.sql.dialect.oracle.visitor.OracleASTVisitor; @@ -31,6 +32,9 @@ public class OracleSelectQueryBlock extends SQLSelectQueryBlock { private ModelClause modelClause; + private final List forUpdateOf = new ArrayList(1); + private boolean skipLocked = false; + public OracleSelectQueryBlock(){ } @@ -47,6 +51,18 @@ public class OracleSelectQueryBlock extends SQLSelectQueryBlock { return this.hints; } + public List getForUpdateOf() { + return forUpdateOf; + } + + public boolean isSkipLocked() { + return skipLocked; + } + + public void setSkipLocked(boolean skipLocked) { + this.skipLocked = skipLocked; + } + @Override protected void accept0(SQLASTVisitor visitor) { if (visitor instanceof OracleASTVisitor) { @@ -68,6 +84,7 @@ public class OracleSelectQueryBlock extends SQLSelectQueryBlock { acceptChild(visitor, this.connectBy); acceptChild(visitor, this.groupBy); acceptChild(visitor, this.modelClause); + acceptChild(visitor, this.forUpdateOf); } visitor.endVisit(this); } diff --git a/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/stmt/OracleSupplementalIdKey.java b/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/stmt/OracleSupplementalIdKey.java new file mode 100644 index 0000000000..8acc62d165 --- /dev/null +++ b/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/stmt/OracleSupplementalIdKey.java @@ -0,0 +1,78 @@ +/* + * 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.sql.dialect.oracle.ast.stmt; + +import com.alibaba.druid.sql.ast.statement.SQLTableElement; +import com.alibaba.druid.sql.dialect.oracle.ast.OracleSQLObjectImpl; +import com.alibaba.druid.sql.dialect.oracle.visitor.OracleASTVisitor; + +/** + * Created by wenshao on 20/05/2017. + */ +public class OracleSupplementalIdKey extends OracleSQLObjectImpl implements SQLTableElement { + + private boolean all; + private boolean primaryKey; + private boolean unique; + private boolean uniqueIndex; + private boolean foreignKey; + + @Override + public void accept0(OracleASTVisitor visitor) { + visitor.visit(this); + visitor.endVisit(this); + } + + public boolean isAll() { + return all; + } + + public void setAll(boolean all) { + this.all = all; + } + + public boolean isPrimaryKey() { + return primaryKey; + } + + public void setPrimaryKey(boolean primaryKey) { + this.primaryKey = primaryKey; + } + + public boolean isUnique() { + return unique; + } + + public void setUnique(boolean unique) { + this.unique = unique; + } + + public boolean isForeignKey() { + return foreignKey; + } + + public void setForeignKey(boolean foreignKey) { + this.foreignKey = foreignKey; + } + + public boolean isUniqueIndex() { + return uniqueIndex; + } + + public void setUniqueIndex(boolean uniqueIndex) { + this.uniqueIndex = uniqueIndex; + } +} diff --git a/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/stmt/OracleSelectForUpdate.java b/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/stmt/OracleSupplementalLogGrp.java similarity index 50% rename from src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/stmt/OracleSelectForUpdate.java rename to src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/stmt/OracleSupplementalLogGrp.java index 035fc1f1fd..ccddb62d1d 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/stmt/OracleSelectForUpdate.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/stmt/OracleSupplementalLogGrp.java @@ -1,73 +1,72 @@ -/* - * 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.sql.dialect.oracle.ast.stmt; - -import java.util.ArrayList; -import java.util.List; - -import com.alibaba.druid.sql.ast.SQLExpr; -import com.alibaba.druid.sql.dialect.oracle.ast.OracleSQLObjectImpl; -import com.alibaba.druid.sql.dialect.oracle.visitor.OracleASTVisitor; - -public class OracleSelectForUpdate extends OracleSQLObjectImpl { - - private final List of = new ArrayList(); - - private boolean notWait = false; - private SQLExpr wait; - private boolean skipLocked = false; - - public OracleSelectForUpdate(){ - - } - - public boolean isNotWait() { - return notWait; - } - - public void setNotWait(boolean notWait) { - this.notWait = notWait; - } - - public SQLExpr getWait() { - return wait; - } - - public void setWait(SQLExpr wait) { - this.wait = wait; - } - - public boolean isSkipLocked() { - return skipLocked; - } - - public void setSkipLocked(boolean skipLocked) { - this.skipLocked = skipLocked; - } - - public void accept0(OracleASTVisitor visitor) { - if (visitor.visit(this)) { - acceptChild(visitor, this.of); - acceptChild(visitor, this.wait); - } - - visitor.endVisit(this); - } - - public List getOf() { - return this.of; - } -} +/* + * 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.sql.dialect.oracle.ast.stmt; + +import com.alibaba.druid.sql.ast.SQLName; +import com.alibaba.druid.sql.ast.statement.SQLTableElement; +import com.alibaba.druid.sql.dialect.oracle.ast.OracleSQLObjectImpl; +import com.alibaba.druid.sql.dialect.oracle.visitor.OracleASTVisitor; + +import java.util.ArrayList; +import java.util.List; + +/** + * Created by wenshao on 20/05/2017. + */ +public class OracleSupplementalLogGrp extends OracleSQLObjectImpl implements SQLTableElement { + private SQLName group; + private List columns = new ArrayList(); + private boolean always = false; + + @Override + public void accept0(OracleASTVisitor visitor) { + if (visitor.visit(this)) { + acceptChild(visitor, group); + acceptChild(visitor, columns); + } + visitor.endVisit(this); + } + + public SQLName getGroup() { + return group; + } + + public void setGroup(SQLName group) { + if (group != null) { + group.setParent(this); + } + this.group = group; + } + + public List getColumns() { + return columns; + } + + public void addColumn(SQLName column) { + if (column != null) { + column.setParent(this); + } + this.columns.add(column); + } + + public boolean isAlways() { + return always; + } + + public void setAlways(boolean always) { + this.always = always; + } +} diff --git a/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/stmt/OracleUsingIndexClause.java b/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/stmt/OracleUsingIndexClause.java index b82cc212b6..a08c0961eb 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/stmt/OracleUsingIndexClause.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/stmt/OracleUsingIndexClause.java @@ -21,6 +21,9 @@ import com.alibaba.druid.sql.dialect.oracle.ast.OracleSQLObjectImpl; import com.alibaba.druid.sql.dialect.oracle.ast.clause.OracleStorageClause; import com.alibaba.druid.sql.dialect.oracle.visitor.OracleASTVisitor; +import java.util.ArrayList; +import java.util.List; + public class OracleUsingIndexClause extends OracleSQLObjectImpl { private SQLName index; @@ -37,6 +40,12 @@ public class OracleUsingIndexClause extends OracleSQLObjectImpl { private OracleStorageClause storage; + private Integer compress = null; + private boolean nocompress = false; + private Boolean logging = null; + + private List localPartitionIndex = new ArrayList(); + public OracleUsingIndexClause(){ } @@ -123,4 +132,57 @@ public class OracleUsingIndexClause extends OracleSQLObjectImpl { this.maxtrans = maxtrans; } + public Integer getCompress() { + return compress; + } + + public void setCompress(Integer compress) { + this.compress = compress; + } + + public boolean isNocompress() { + return nocompress; + } + + public void setNocompress(boolean nocompress) { + this.nocompress = nocompress; + } + + public Boolean getLogging() { + return logging; + } + + public void setLogging(Boolean logging) { + this.logging = logging; + } + + public List getLocalPartitionIndex() { + return localPartitionIndex; + } + + public static class PartitionedItem extends OracleSQLObjectImpl { + private SQLName partition; + private SQLName tablespace; + + @Override + public void accept0(OracleASTVisitor visitor) { + + } + + public SQLName getPartition() { + return partition; + } + + public void setPartition(SQLName partition) { + this.partition = partition; + } + + public SQLName getTablespace() { + return tablespace; + } + + public void setTablespace(SQLName tablespace) { + this.tablespace = tablespace; + } + } } diff --git a/src/main/java/com/alibaba/druid/sql/dialect/oracle/parser/OracleCreateTableParser.java b/src/main/java/com/alibaba/druid/sql/dialect/oracle/parser/OracleCreateTableParser.java index 72a16a8985..fe4d255e86 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/oracle/parser/OracleCreateTableParser.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/oracle/parser/OracleCreateTableParser.java @@ -27,16 +27,21 @@ import com.alibaba.druid.sql.ast.SQLSubPartition; import com.alibaba.druid.sql.ast.SQLSubPartitionBy; import com.alibaba.druid.sql.ast.SQLSubPartitionByHash; import com.alibaba.druid.sql.ast.SQLSubPartitionByList; +import com.alibaba.druid.sql.ast.expr.SQLIdentifierExpr; import com.alibaba.druid.sql.ast.expr.SQLIntegerExpr; +import com.alibaba.druid.sql.ast.statement.SQLCreateTableStatement; import com.alibaba.druid.sql.dialect.oracle.ast.clause.OracleLobStorageClause; import com.alibaba.druid.sql.dialect.oracle.ast.clause.OracleStorageClause; import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleCreateTableStatement; import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleCreateTableStatement.DeferredSegmentCreation; import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleSelect; +import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleSupplementalIdKey; +import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleSupplementalLogGrp; import com.alibaba.druid.sql.parser.Lexer; import com.alibaba.druid.sql.parser.ParserException; import com.alibaba.druid.sql.parser.SQLCreateTableParser; import com.alibaba.druid.sql.parser.Token; +import com.sun.org.apache.xpath.internal.operations.Bool; public class OracleCreateTableParser extends SQLCreateTableParser { @@ -76,6 +81,10 @@ public class OracleCreateTableParser extends SQLCreateTableParser { lexer.nextToken(); stmt.setLogging(Boolean.TRUE); continue; + } else if (identifierEquals("NOLOGGING")) { + lexer.nextToken(); + stmt.setLogging(Boolean.FALSE); + continue; } else if (lexer.token() == Token.CACHE) { lexer.nextToken(); stmt.setCache(Boolean.TRUE); @@ -84,10 +93,48 @@ public class OracleCreateTableParser extends SQLCreateTableParser { lexer.nextToken(); stmt.setCache(Boolean.FALSE); continue; - } else if (lexer.token() == Token.NOCOMPRESS) { + } else if (lexer.token() == Token.ENABLE) { + lexer.nextToken(); + if (lexer.token() == Token.ROW) { + lexer.nextToken(); + acceptIdentifier("MOVEMENT"); + stmt.setEnableRowMovement(Boolean.TRUE); + } else { + throw new ParserException("TODO : " + lexer.info()); + } + //stmt.setEnable(Boolean.TRUE); + continue; + } else if (lexer.token() == Token.DISABLE) { + lexer.nextToken(); + if (lexer.token() == Token.ROW) { + lexer.nextToken(); + acceptIdentifier("MOVEMENT"); + stmt.setEnableRowMovement(Boolean.FALSE); + } else { + throw new ParserException("TODO : " + lexer.info()); + } + //stmt.setEnable(Boolean.FALSE); + continue; + } else if (lexer.token() == Token.NOCOMPRESS || identifierEquals("NOCOMPRESS")) { lexer.nextToken(); stmt.setCompress(Boolean.FALSE); continue; + } else if (identifierEquals("COMPRESS")) { + lexer.nextToken(); + stmt.setCompress(Boolean.TRUE); + continue; + } else if (lexer.token() == Token.FOR) { + lexer.nextToken(); + if (lexer.token() == Token.ALL) { + lexer.nextToken(); + throw new ParserException("TODO : " + lexer.info()); + } else if (identifierEquals("OLTP")) { + lexer.nextToken(); + stmt.setForOLTP(true); + } else { + throw new ParserException("TODO : " + lexer.info()); + } + continue; } else if (lexer.token() == Token.ON) { lexer.nextToken(); accept(Token.COMMIT); @@ -104,8 +151,16 @@ public class OracleCreateTableParser extends SQLCreateTableParser { continue; } else if (identifierEquals("organization")) { lexer.nextToken(); - accept(Token.INDEX); - stmt.setOrganizationIndex(true); + if (lexer.token() == Token.INDEX) { + lexer.nextToken(); + stmt.setOrganizationIndex(true); + } else if (identifierEquals("EXTERNAL")) { + lexer.nextToken(); + accept(Token.LPAREN); + throw new ParserException("TODO " + lexer.info()); + } else { + throw new ParserException("TODO " + lexer.info()); + } continue; } else if (lexer.token() == Token.PCTFREE) { lexer.nextToken(); @@ -142,7 +197,7 @@ public class OracleCreateTableParser extends SQLCreateTableParser { stmt.setDeferredSegmentCreation(DeferredSegmentCreation.DEFERRED); } continue; - } else if (identifierEquals("PARTITION")) { + } else if (lexer.token() == Token.PARTITION) { lexer.nextToken(); accept(Token.BY); @@ -155,6 +210,22 @@ public class OracleCreateTableParser extends SQLCreateTableParser { } else if (identifierEquals("HASH")) { SQLPartitionByHash partitionByHash = partitionByHash(); partitionClauseRest(partitionByHash); + + if (lexer.token() == Token.LPAREN) { + lexer.nextToken(); + for (;;) { + SQLPartition partition = this.parsePartition(); + partitionByHash.addPartition(partition); + if (lexer.token() == Token.COMMA) { + lexer.nextToken(); + continue; + } else if (lexer.token() == Token.RPAREN) { + lexer.nextToken(); + break; + } + throw new ParserException("TODO : " + lexer.token() + " " + lexer.stringVal()); + } + } stmt.setPartitioning(partitionByHash); continue; } else if (identifierEquals("LIST")) { @@ -260,6 +331,7 @@ public class OracleCreateTableParser extends SQLCreateTableParser { clause.setSubPartitionBy(subPartitionBy); } + accept(Token.LPAREN); for (;;) { @@ -279,7 +351,7 @@ public class OracleCreateTableParser extends SQLCreateTableParser { } protected SQLPartition parsePartition() { - acceptIdentifier("PARTITION"); + accept(Token.PARTITION); SQLPartition partition = new SQLPartition(); partition.setName(this.exprParser.name()); @@ -310,6 +382,47 @@ public class OracleCreateTableParser extends SQLCreateTableParser { SQLExpr subPartitionsCount = this.exprParser.primary(); partition.setSubPartitionsCount(subPartitionsCount); } + + for (;;) { + if (lexer.token() == Token.TABLESPACE) { + lexer.nextToken(); + partition.setTablespace(this.exprParser.name()); + continue; + } else if (lexer.token() == Token.PCTFREE) { + lexer.nextToken(); + partition.setPtcfree(this.exprParser.expr()); + continue; + } else if (identifierEquals("PCTUSED")) { + lexer.nextToken(); + partition.setPctused(this.exprParser.expr()); + continue; + } else if (lexer.token() == Token.STORAGE) { + OracleStorageClause storage = ((OracleExprParser) this.exprParser).parseStorage(); + partition.setStorage(storage); + continue; + } else if (lexer.token() == Token.INITRANS) { + lexer.nextToken(); + partition.setInitrans(this.exprParser.expr()); + continue; + } else if (lexer.token() == Token.MAXTRANS) { + lexer.nextToken(); + partition.setMaxtrans(this.exprParser.expr()); + continue; + } else if (lexer.token() == Token.NOCOMPRESS) { + lexer.nextToken(); + partition.setCompress(Boolean.FALSE); + continue; + } else if (identifierEquals("COMPRESS")) { + lexer.nextToken(); + partition.setCompress(Boolean.TRUE); + continue; + } else if (lexer.token() == Token.LOB) { + OracleLobStorageClause lobStorage = ((OracleExprParser) this.exprParser).parseLobStorage(); + partition.setLobStorage(lobStorage); + continue; + } + break; + } return partition; } @@ -393,4 +506,91 @@ public class OracleCreateTableParser extends SQLCreateTableParser { throw new ParserException("TODO : " + lexer.token() + " " + lexer.stringVal()); } + protected void parseCreateTableSupplementalLogingProps(SQLCreateTableStatement stmt) { + acceptIdentifier("SUPPLEMENTAL"); + acceptIdentifier("LOG"); + + if (lexer.token() == Token.GROUP) { + lexer.nextToken(); + + OracleSupplementalLogGrp logGrp = new OracleSupplementalLogGrp(); + logGrp.setGroup(this.exprParser.name()); + + accept(Token.LPAREN); + for (;;) { + SQLName column = this.exprParser.name(); + + if (identifierEquals("NO")) { + lexer.nextToken(); + acceptIdentifier("LOG"); + column.putAttribute("NO LOG", Boolean.TRUE); + } + + logGrp.addColumn(column); + + if (lexer.token() == Token.COMMA) { + lexer.nextToken(); + continue; + } + + if (lexer.token() == Token.RPAREN) { + break; + } + + throw new ParserException("TODO " + lexer.info()); + } + accept(Token.RPAREN); + + if (identifierEquals("ALWAYS")) { + lexer.nextToken(); + logGrp.setAlways(true); + } + + logGrp.setParent(stmt); + stmt.getTableElementList().add(logGrp); + } else { + acceptIdentifier("DATA"); + + OracleSupplementalIdKey idKey = new OracleSupplementalIdKey(); + accept(Token.LPAREN); + for (;;) { + if (lexer.token() == Token.ALL) { + lexer.nextToken(); + idKey.setAll(true); + } else if (lexer.token() == Token.PRIMARY) { + lexer.nextToken(); + accept(Token.KEY); + idKey.setPrimaryKey(true); + } else if (lexer.token() == Token.UNIQUE) { + lexer.nextToken(); + + if (lexer.token() == Token.INDEX) { + lexer.nextToken(); + idKey.setUniqueIndex(true); + } else { + idKey.setUnique(true); + } + } else if (lexer.token() == Token.FOREIGN) { + lexer.nextToken(); + accept(Token.KEY); + idKey.setForeignKey(true); + } + + if (lexer.token() == Token.COMMA) { + lexer.nextToken(); + continue; + } + + if (lexer.token() == Token.RPAREN) { + break; + } + + throw new ParserException("TODO " + lexer.info()); + } + accept(Token.RPAREN); + acceptIdentifier("COLUMNS"); + idKey.setParent(stmt); + stmt.getTableElementList().add(idKey); + } + } } diff --git a/src/main/java/com/alibaba/druid/sql/dialect/oracle/parser/OracleExprParser.java b/src/main/java/com/alibaba/druid/sql/dialect/oracle/parser/OracleExprParser.java index a013b38e6e..898ed1f205 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/oracle/parser/OracleExprParser.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/oracle/parser/OracleExprParser.java @@ -211,8 +211,13 @@ public class OracleExprParser extends SQLExprParser { lexer.nextToken(); } else if (identifierEquals("LONG")) { lexer.nextToken(); - acceptIdentifier("RAW"); - typeName = "LONG RAW"; + + if (identifierEquals("RAW")) { + lexer.nextToken(); + typeName = "LONG RAW"; + } else { + typeName = "LONG"; + } } else { SQLName typeExpr = name(); typeName = typeExpr.toString(); @@ -788,7 +793,7 @@ public class OracleExprParser extends SQLExprParser { lexer.nextToken(); accept(Token.LPAREN); - if (identifierEquals("PARTITION")) { + if (lexer.token() == Token.PARTITION) { lexer.nextToken(); accept(Token.BY); @@ -1028,7 +1033,7 @@ public class OracleExprParser extends SQLExprParser { private OracleUsingIndexClause parseUsingIndex() { accept(Token.USING); accept(Token.INDEX); - + OracleUsingIndexClause using = new OracleUsingIndexClause(); for (;;) { @@ -1065,9 +1070,50 @@ public class OracleExprParser extends SQLExprParser { OracleStorageClause storage = parseStorage(); using.setStorage(storage); continue; + } else if (identifierEquals("LOCAL")) { + lexer.nextToken(); + accept(Token.LPAREN); + + // http://docs.oracle.com/cd/B19306_01/server.102/b14200/statements_5010.htm#i2125897 + for (;;) { + accept(Token.PARTITION); + OracleUsingIndexClause.PartitionedItem item = new OracleUsingIndexClause.PartitionedItem(); + item.setPartition(this.name()); + if (lexer.token() == Token.TABLESPACE) { + lexer.nextToken(); + item.setTablespace(this.name()); + } + + using.getLocalPartitionIndex().add(item); + + if (lexer.token() == Token.COMMA) { + lexer.nextToken(); + continue; + } else if (lexer.token() == Token.RPAREN) { + break; + } else { + throw new ParserException("TODO " + lexer.token()); + } + } + accept(Token.RPAREN); + continue; + } else if (identifierEquals("NOLOGGING")) { + lexer.nextToken(); + using.setLogging(Boolean.FALSE); + continue; + } else if (identifierEquals("COMPRESS")) { + throw new ParserException("TODO " + lexer.token()); } else if (lexer.token() == Token.IDENTIFIER) { using.setTablespace(this.name()); break; + } else if (lexer.token() == Token.NOCOMPRESS || identifierEquals("NOCOMPRESS")) { + lexer.nextToken(); + using.setNocompress(true); + continue; + } else if (lexer.token() == Token.LOGGING || identifierEquals("LOGGING")) { + lexer.nextToken(); + using.setLogging(Boolean.TRUE); + continue; } else { break; } @@ -1081,6 +1127,21 @@ public class OracleExprParser extends SQLExprParser { if (lexer.token() == Token.ENABLE) { lexer.nextToken(); column.setEnable(Boolean.TRUE); + } else if (lexer.token() == Token.ENABLE) { + lexer.nextToken(); + column.setEnable(true); + } else if (identifierEquals("VALIDATE")) { + lexer.nextToken(); + column.setValidate(Boolean.TRUE); + } else if (identifierEquals("NOVALIDATE")) { + lexer.nextToken(); + column.setValidate(Boolean.FALSE); + } else if (identifierEquals("RELY")) { + lexer.nextToken(); + column.setRely(Boolean.TRUE); + } else if (identifierEquals("NORELY")) { + lexer.nextToken(); + column.setRely(Boolean.FALSE); } return column; @@ -1140,6 +1201,13 @@ public class OracleExprParser extends SQLExprParser { accept(Token.ROW); clause.setEnable(true); continue; + } else if (lexer.token() == Token.DISABLE) { + lexer.nextToken(); + accept(Token.STORAGE); + accept(Token.IN); + accept(Token.ROW); + clause.setEnable(false); + continue; } if (lexer.token() == Token.CHUNK) { @@ -1169,6 +1237,24 @@ public class OracleExprParser extends SQLExprParser { clause.setKeepDuplicate(true); continue; } + + if (identifierEquals("PCTVERSION")) { + lexer.nextToken(); + clause.setPctversion(this.expr()); + continue; + } + + if (identifierEquals("RETENTION")) { + lexer.nextToken(); + clause.setRetention(true); + continue; + } + + if (lexer.token() == Token.STORAGE) { + OracleStorageClause storageClause = this.parseStorage(); + clause.setStorageClause(storageClause); + continue; + } break; } @@ -1342,7 +1428,7 @@ public class OracleExprParser extends SQLExprParser { return constraint; } - protected SQLForeignKeyConstraint createForeignKey() { + protected OracleForeignKey createForeignKey() { return new OracleForeignKey(); } diff --git a/src/main/java/com/alibaba/druid/sql/dialect/oracle/parser/OracleLexer.java b/src/main/java/com/alibaba/druid/sql/dialect/oracle/parser/OracleLexer.java index 14f3879ed0..ede9e97477 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/oracle/parser/OracleLexer.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/oracle/parser/OracleLexer.java @@ -112,6 +112,8 @@ public class OracleLexer extends Lexer { map.put("INITIALLY", Token.INITIALLY); map.put("FETCH", Token.FETCH); + map.put("TABLESPACE", Token.TABLESPACE); + map.put("PARTITION", Token.PARTITION); DEFAULT_ORACLE_KEYWORDS = new Keywords(map); } @@ -216,7 +218,7 @@ public class OracleLexer extends Lexer { bufPos++; } - for (;;) { + for (;!isEOF();) { if (ch == '*' && charAt(pos + 1) == '/') { bufPos += 2; scanChar(); diff --git a/src/main/java/com/alibaba/druid/sql/dialect/oracle/parser/OracleSelectParser.java b/src/main/java/com/alibaba/druid/sql/dialect/oracle/parser/OracleSelectParser.java index 3bc78381ca..3e0508b737 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/oracle/parser/OracleSelectParser.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/oracle/parser/OracleSelectParser.java @@ -48,7 +48,6 @@ import com.alibaba.druid.sql.dialect.oracle.ast.clause.PartitionExtensionClause; import com.alibaba.druid.sql.dialect.oracle.ast.clause.SampleClause; import com.alibaba.druid.sql.dialect.oracle.ast.clause.SearchClause; import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleSelect; -import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleSelectForUpdate; import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleSelectJoin; import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleSelectPivot; import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleSelectQueryBlock; @@ -82,35 +81,43 @@ public class OracleSelectParser extends SQLSelectParser { SQLOrderBy orderBy = this.parseOrderBy(); select.setOrderBy(orderBy); - if (orderBy != null && query instanceof SQLSelectQueryBlock) { - SQLSelectQueryBlock queryBlock = (SQLSelectQueryBlock) query; - parseFetchClause(queryBlock); + + OracleSelectQueryBlock queryBlock = null; + if (query instanceof SQLSelectQueryBlock) { + queryBlock = (OracleSelectQueryBlock) query; + if (orderBy != null) { + parseFetchClause(queryBlock); + } } if (lexer.token() == (Token.FOR)) { + if (queryBlock == null) { + throw new ParserException("TODO"); + } + lexer.nextToken(); accept(Token.UPDATE); - OracleSelectForUpdate forUpdate = new OracleSelectForUpdate(); + queryBlock.setForUpdate(true); + + // OracleSelectForUpdate forUpdate = new OracleSelectForUpdate(); if (lexer.token() == Token.OF) { lexer.nextToken(); - this.exprParser.exprList(forUpdate.getOf(), forUpdate); + this.exprParser.exprList(queryBlock.getForUpdateOf(), queryBlock); } if (lexer.token() == Token.NOWAIT) { lexer.nextToken(); - forUpdate.setNotWait(true); + queryBlock.setNoWait(true); } else if (lexer.token() == Token.WAIT) { lexer.nextToken(); - forUpdate.setWait(this.exprParser.primary()); + queryBlock.setWaitTime(this.exprParser.primary()); } else if (identifierEquals("SKIP")) { lexer.nextToken(); acceptIdentifier("LOCKED"); - forUpdate.setSkipLocked(true); + queryBlock.setSkipLocked(true); } - - select.setForUpdate(forUpdate); } if (select.getOrderBy() == null) { @@ -518,7 +525,7 @@ public class OracleSelectParser extends SQLSelectParser { } private void parseQueryPartitionClause(ModelColumnClause modelColumnClause) { - if (identifierEquals("PARTITION")) { + if (lexer.token() == Token.PARTITION) { QueryPartitionClause queryPartitionClause = new QueryPartitionClause(); lexer.nextToken(); @@ -568,6 +575,16 @@ public class OracleSelectParser extends SQLSelectParser { @Override public SQLTableSource parseTableSource() { + SQLTableSource tableSource = parseTableSourcePrimary(); + + if (tableSource instanceof OracleSelectTableSource) { + return parseTableSourceRest((OracleSelectTableSource) tableSource); + } + + return parseTableSourceRest(tableSource); + } + + public SQLTableSource parseTableSourcePrimary() { if (lexer.token() == (Token.LPAREN)) { lexer.nextToken(); OracleSelectSubqueryTableSource tableSource; @@ -582,7 +599,7 @@ public class OracleSelectParser extends SQLSelectParser { parsePivot((OracleSelectTableSource) tableSource); - return parseTableSourceRest(tableSource); + return tableSource; } if (lexer.token() == (Token.SELECT)) { @@ -602,7 +619,7 @@ public class OracleSelectParser extends SQLSelectParser { parsePivot(tableReference); } - return parseTableSourceRest(tableReference); + return tableReference; } private void parseTableSourceQueryTableExpr(OracleSelectTableReference tableReference) { @@ -637,7 +654,7 @@ public class OracleSelectParser extends SQLSelectParser { tableReference.setSampleClause(sample); } - if (identifierEquals("PARTITION")) { + if (lexer.token() == Token.PARTITION) { lexer.nextToken(); PartitionExtensionClause partition = new PartitionExtensionClause(); @@ -742,6 +759,22 @@ public class OracleSelectParser extends SQLSelectParser { return null; } + protected SQLTableSource primaryTableSourceRest(SQLTableSource tableSource) { + if (tableSource instanceof OracleSelectTableSource) { + if (lexer.token() == Token.AS) { + lexer.nextToken(); + + if (lexer.token() == Token.OF) { + ((OracleSelectTableSource)tableSource).setFlashback(flashback()); + } + + tableSource.setAlias(tableAlias()); + } + } + + return tableSource; + } + protected SQLTableSource parseTableSourceRest(OracleSelectTableSource tableSource) { if (lexer.token() == Token.AS) { lexer.nextToken(); @@ -761,7 +794,7 @@ public class OracleSelectParser extends SQLSelectParser { this.exprParser.parseHints(tableSource.getHints()); } - OracleSelectJoin.JoinType joinType = null; + SQLJoinTableSource.JoinType joinType = null; if (lexer.token() == Token.LEFT) { lexer.nextToken(); @@ -769,7 +802,7 @@ public class OracleSelectParser extends SQLSelectParser { lexer.nextToken(); } accept(Token.JOIN); - joinType = OracleSelectJoin.JoinType.LEFT_OUTER_JOIN; + joinType = SQLJoinTableSource.JoinType.LEFT_OUTER_JOIN; } if (lexer.token() == Token.RIGHT) { @@ -778,7 +811,7 @@ public class OracleSelectParser extends SQLSelectParser { lexer.nextToken(); } accept(Token.JOIN); - joinType = OracleSelectJoin.JoinType.RIGHT_OUTER_JOIN; + joinType = SQLJoinTableSource.JoinType.RIGHT_OUTER_JOIN; } if (lexer.token() == Token.FULL) { @@ -787,35 +820,39 @@ public class OracleSelectParser extends SQLSelectParser { lexer.nextToken(); } accept(Token.JOIN); - joinType = OracleSelectJoin.JoinType.FULL_OUTER_JOIN; + joinType = SQLJoinTableSource.JoinType.FULL_OUTER_JOIN; } if (lexer.token() == Token.INNER) { lexer.nextToken(); accept(Token.JOIN); - joinType = OracleSelectJoin.JoinType.INNER_JOIN; + joinType = SQLJoinTableSource.JoinType.INNER_JOIN; } if (lexer.token() == Token.CROSS) { lexer.nextToken(); accept(Token.JOIN); - joinType = OracleSelectJoin.JoinType.CROSS_JOIN; + joinType = SQLJoinTableSource.JoinType.CROSS_JOIN; } if (lexer.token() == Token.JOIN) { lexer.nextToken(); - joinType = OracleSelectJoin.JoinType.JOIN; + joinType = SQLJoinTableSource.JoinType.JOIN; } if (lexer.token() == (Token.COMMA)) { lexer.nextToken(); - joinType = OracleSelectJoin.JoinType.COMMA; + joinType = SQLJoinTableSource.JoinType.COMMA; } if (joinType != null) { OracleSelectJoin join = new OracleSelectJoin(); join.setLeft(tableSource); join.setJoinType(joinType); - join.setRight(parseTableSource()); + + SQLTableSource right; + right = parseTableSourcePrimary(); + right.setAlias(this.tableAlias()); + join.setRight(right); if (lexer.token() == Token.ON) { lexer.nextToken(); diff --git a/src/main/java/com/alibaba/druid/sql/dialect/oracle/parser/OracleStatementParser.java b/src/main/java/com/alibaba/druid/sql/dialect/oracle/parser/OracleStatementParser.java index 452a1d1828..1065348e4d 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/oracle/parser/OracleStatementParser.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/oracle/parser/OracleStatementParser.java @@ -262,7 +262,7 @@ public class OracleStatementParser extends SQLStatementParser { statementList.add(this.parseSelect()); continue; } else { - throw new ParserException("TODO : " + lexer.token() + " " + lexer.stringVal()); + throw new ParserException("TODO : " + lexer.info()); } } @@ -495,7 +495,7 @@ public class OracleStatementParser extends SQLStatementParser { break; } - throw new ParserException("TODO : " + lexer.token() + " " + lexer.stringVal()); + throw new ParserException("TODO : " + lexer.info()); } } @@ -935,7 +935,7 @@ public class OracleStatementParser extends SQLStatementParser { continue; } else if (lexer.token() == Token.TRUNCATE) { lexer.nextToken(); - if (identifierEquals("PARTITION")) { + if (lexer.token() == Token.PARTITION) { lexer.nextToken(); OracleAlterTableTruncatePartition item = new OracleAlterTableTruncatePartition(); item.setName(this.exprParser.name()); @@ -1005,7 +1005,7 @@ public class OracleStatementParser extends SQLStatementParser { SQLAlterTableDropColumnItem item = new SQLAlterTableDropColumnItem(); this.exprParser.names(item.getColumns()); stmt.addItem(item); - } else if (identifierEquals("PARTITION")) { + } else if (lexer.token() == Token.PARTITION) { lexer.nextToken(); OracleAlterTableDropPartition item = new OracleAlterTableDropPartition(); item.setName(this.exprParser.name()); @@ -1023,7 +1023,7 @@ public class OracleStatementParser extends SQLStatementParser { private void parseAlterTableSplit(SQLAlterTableStatement stmt) { lexer.nextToken(); - if (identifierEquals("PARTITION")) { + if (lexer.token() == Token.PARTITION) { lexer.nextToken(); OracleAlterTableSplitPartition item = new OracleAlterTableSplitPartition(); item.setName(this.exprParser.name()); @@ -1043,7 +1043,7 @@ public class OracleStatementParser extends SQLStatementParser { for (;;) { NestedTablePartitionSpec spec = new NestedTablePartitionSpec(); - acceptIdentifier("PARTITION"); + accept(Token.PARTITION); spec.setPartition(this.exprParser.name()); for (;;) { diff --git a/src/main/java/com/alibaba/druid/sql/dialect/oracle/visitor/OracleASTVisitor.java b/src/main/java/com/alibaba/druid/sql/dialect/oracle/visitor/OracleASTVisitor.java index baf6d0b414..e769222c49 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/oracle/visitor/OracleASTVisitor.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/oracle/visitor/OracleASTVisitor.java @@ -69,8 +69,6 @@ public interface OracleASTVisitor extends SQLASTVisitor { void endVisit(OracleOuterExpr x); - void endVisit(OracleSelectForUpdate x); - void endVisit(OracleSelectJoin x); void endVisit(OracleSelectPivot x); @@ -103,8 +101,6 @@ public interface OracleASTVisitor extends SQLASTVisitor { boolean visit(OracleOuterExpr x); - boolean visit(OracleSelectForUpdate x); - boolean visit(OracleSelectJoin x); boolean visit(OracleSelectPivot x); @@ -444,4 +440,12 @@ public interface OracleASTVisitor extends SQLASTVisitor { boolean visit(OracleCheck x); void endVisit(OracleCheck x); + + boolean visit(OracleSupplementalIdKey x); + + void endVisit(OracleSupplementalIdKey x); + + boolean visit(OracleSupplementalLogGrp x); + + void endVisit(OracleSupplementalLogGrp x); } diff --git a/src/main/java/com/alibaba/druid/sql/dialect/oracle/visitor/OracleASTVisitorAdapter.java b/src/main/java/com/alibaba/druid/sql/dialect/oracle/visitor/OracleASTVisitorAdapter.java index 27aff053dc..b1aeb06632 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/oracle/visitor/OracleASTVisitorAdapter.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/oracle/visitor/OracleASTVisitorAdapter.java @@ -107,11 +107,6 @@ public class OracleASTVisitorAdapter extends SQLASTVisitorAdapter implements Ora } - @Override - public void endVisit(OracleSelectForUpdate x) { - - } - @Override public void endVisit(OracleSelectJoin x) { @@ -194,12 +189,6 @@ public class OracleASTVisitorAdapter extends SQLASTVisitorAdapter implements Ora return true; } - @Override - public boolean visit(OracleSelectForUpdate x) { - - return true; - } - @Override public boolean visit(OracleSelectJoin x) { @@ -1078,4 +1067,23 @@ public class OracleASTVisitorAdapter extends SQLASTVisitorAdapter implements Ora } + @Override + public boolean visit(OracleSupplementalIdKey x) { + return true; + } + + @Override + public void endVisit(OracleSupplementalIdKey x) { + + } + + @Override + public boolean visit(OracleSupplementalLogGrp x) { + return true; + } + + @Override + public void endVisit(OracleSupplementalLogGrp x) { + + } } diff --git a/src/main/java/com/alibaba/druid/sql/dialect/oracle/visitor/OracleOutputVisitor.java b/src/main/java/com/alibaba/druid/sql/dialect/oracle/visitor/OracleOutputVisitor.java index feac56e072..562e706259 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/oracle/visitor/OracleOutputVisitor.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/oracle/visitor/OracleOutputVisitor.java @@ -300,11 +300,6 @@ public class OracleOutputVisitor extends SQLASTOutputVisitor implements OracleAS x.getRestriction().accept(this); } - if (x.getForUpdate() != null) { - println(); - x.getForUpdate().accept(this); - } - SQLOrderBy orderBy = x.getOrderBy(); if (orderBy != null) { boolean hasFirst = false; @@ -322,25 +317,6 @@ public class OracleOutputVisitor extends SQLASTOutputVisitor implements OracleAS return false; } - public boolean visit(OracleSelectForUpdate x) { - print0(ucase ? "FOR UPDATE" : "for update"); - if (x.getOf().size() > 0) { - print('('); - printAndAccept(x.getOf(), ", "); - print(')'); - } - - if (x.isNotWait()) { - print0(ucase ? " NOWAIT" : " nowait"); - } else if (x.isSkipLocked()) { - print0(ucase ? " SKIP LOCKED" : " skip locked"); - } else if (x.getWait() != null) { - print0(ucase ? " WAIT " : " wait "); - x.getWait().accept(this); - } - return false; - } - public boolean visit(OracleSelectJoin x) { x.getLeft().accept(this); @@ -502,6 +478,25 @@ public class OracleOutputVisitor extends SQLASTOutputVisitor implements OracleAS printFetchFirst(x); + if (x.isForUpdate()) { + println(); + print0(ucase ? "FOR UPDATE" : "for update"); + if (x.getForUpdateOf().size() > 0) { + print('('); + printAndAccept(x.getForUpdateOf(), ", "); + print(')'); + } + + if (x.isNoWait()) { + print0(ucase ? " NOWAIT" : " nowait"); + } else if (x.isSkipLocked()) { + print0(ucase ? " SKIP LOCKED" : " skip locked"); + } else if (x.getWaitTime() != null) { + print0(ucase ? " WAIT " : " wait "); + x.getWaitTime().accept(this); + } + } + return false; } @@ -715,11 +710,6 @@ public class OracleOutputVisitor extends SQLASTOutputVisitor implements OracleAS } - @Override - public void endVisit(OracleSelectForUpdate x) { - - } - @Override public void endVisit(OracleSelectJoin x) { @@ -2058,6 +2048,7 @@ public class OracleOutputVisitor extends SQLASTOutputVisitor implements OracleAS } protected void printConstraintState(OracleConstraint x) { + incrementIndent(); if (x.getUsing() != null) { println(); x.getUsing().accept(this); @@ -2089,6 +2080,7 @@ public class OracleOutputVisitor extends SQLASTOutputVisitor implements OracleAS print0(ucase ? " NOT DEFERRABLE" : " not deferrable"); } } + decrementIndent(); } @Override @@ -2149,12 +2141,12 @@ public class OracleOutputVisitor extends SQLASTOutputVisitor implements OracleAS print0(ucase ? "NOCACHE" : "nocache"); } - if (x.getCompress() == Boolean.TRUE) { - println(); - print0(ucase ? "COMPRESS" : "compress"); - } else if (x.getCompress() == Boolean.FALSE) { + if (x.getCompress() == Boolean.FALSE) { println(); print0(ucase ? "NOCOMPRESS" : "nocompress"); + } else if (x.getCompress() == Boolean.TRUE) { + println(); + print0(ucase ? "COMPRESS" : "compress"); } if (x.getLogging() == Boolean.TRUE) { @@ -2806,34 +2798,41 @@ public class OracleOutputVisitor extends SQLASTOutputVisitor implements OracleAS x.getIndex().accept(this); } else { if (x.getPtcfree() != null) { - print0(ucase ? " PCTFREE " : " pctfree "); + println(); + print0(ucase ? "PCTFREE " : "pctfree "); x.getPtcfree().accept(this); } if (x.getInitrans() != null) { - print0(ucase ? " INITRANS " : " initrans "); + println(); + print0(ucase ? "INITRANS " : "initrans "); x.getInitrans().accept(this); } if (x.getMaxtrans() != null) { - print0(ucase ? " MAXTRANS " : " maxtrans "); + println(); + print0(ucase ? "MAXTRANS " : "maxtrans "); x.getMaxtrans().accept(this); } if (x.isComputeStatistics()) { - print0(ucase ? " COMPUTE STATISTICS" : " compute statistics"); + println(); + print0(ucase ? "COMPUTE STATISTICS" : "compute statistics"); } if (x.getTablespace() != null) { - print0(ucase ? " TABLESPACE " : " tablespace "); + println(); + print0(ucase ? "TABLESPACE " : "tablespace "); x.getTablespace().accept(this); } if (x.getEnable() != null) { if (x.getEnable().booleanValue()) { - print0(ucase ? " ENABLE" : " enable"); + println(); + print0(ucase ? "ENABLE" : "enable"); } else { - print0(ucase ? " DISABLE" : " disable"); + println(); + print0(ucase ? "DISABLE" : "disable"); } } @@ -2841,6 +2840,20 @@ public class OracleOutputVisitor extends SQLASTOutputVisitor implements OracleAS println(); x.getStorage().accept(this); } + + if (x.isNocompress()) { + println(); + print0(ucase ? "NOCOMPRESS" : "nocompress"); + } + + if (x.getLogging() != null) { + println(); + if (x.getLogging().booleanValue()) { + print0(ucase ? "LOGGING" : "logging"); + } else { + print0(ucase ? "NOLOGGING" : "nologging"); + } + } } return false; @@ -2885,6 +2898,15 @@ public class OracleOutputVisitor extends SQLASTOutputVisitor implements OracleAS } else { print0(ucase ? "DISABLE STORAGE IN ROW" : "disable storage in row"); } + first = false; + } + + if (x.getStorageClause() != null) { + if (!first) { + print(' '); + } + x.getStorageClause().accept(this); + first = false; } if (x.getChunk() != null) { @@ -2893,6 +2915,7 @@ public class OracleOutputVisitor extends SQLASTOutputVisitor implements OracleAS } print0(ucase ? "CHUNK " : "chunk "); x.getChunk().accept(this); + first = false; } if (x.getCache() != null) { @@ -2912,6 +2935,7 @@ public class OracleOutputVisitor extends SQLASTOutputVisitor implements OracleAS print0(ucase ? " NOLOGGING" : " nologging"); } } + first = false; } if (x.getCompress() != null) { @@ -2923,6 +2947,7 @@ public class OracleOutputVisitor extends SQLASTOutputVisitor implements OracleAS } else { print0(ucase ? "NOCOMPRESS" : "nocompress"); } + first = false; } if (x.getKeepDuplicate() != null) { @@ -2934,6 +2959,15 @@ public class OracleOutputVisitor extends SQLASTOutputVisitor implements OracleAS } else { print0(ucase ? "DEDUPLICATE" : "deduplicate"); } + first = false; + } + + if (x.isRetention()) { + if (!first) { + print(' '); + } + print0(ucase ? "RETENTION" : "retention"); + first = false; } print(')'); @@ -3023,4 +3057,74 @@ public class OracleOutputVisitor extends SQLASTOutputVisitor implements OracleAS return false; } + + @Override + public boolean visit(OracleSupplementalIdKey x) { + print0(ucase ? "SUPPLEMENTAL LOG DATA (" : "supplemental log data ("); + + int count = 0; + + if (x.isAll()) { + print0(ucase ? "ALL" : "all"); + count++; + } + + if (x.isPrimaryKey()) { + if (count != 0) { + print0(", "); + } + print0(ucase ? "PRIMARY KEY" : "primary key"); + count++; + } + + if (x.isUnique()) { + if (count != 0) { + print0(", "); + } + print0(ucase ? "UNIQUE" : "unique"); + count++; + } + + if (x.isUniqueIndex()) { + if (count != 0) { + print0(", "); + } + print0(ucase ? "UNIQUE INDEX" : "unique index"); + count++; + } + + if (x.isForeignKey()) { + if (count != 0) { + print0(", "); + } + print0(ucase ? "FOREIGHN KEY" : "foreighn key"); + count++; + } + + print0(ucase ? ") COLUMNS" : ") columns"); + return false; + } + + @Override + public void endVisit(OracleSupplementalIdKey x) { + + } + + @Override + public boolean visit(OracleSupplementalLogGrp x) { + print0(ucase ? "SUPPLEMENTAL LOG GROUP " : "supplemental log group "); + x.getGroup().accept(this); + print0(" ("); + printAndAccept(x.getColumns(), ", "); + print(')'); + if (x.isAlways()) { + print0(ucase ? " ALWAYS" : " always"); + } + return false; + } + + @Override + public void endVisit(OracleSupplementalLogGrp x) { + + } } diff --git a/src/main/java/com/alibaba/druid/sql/dialect/oracle/visitor/OracleSchemaStatVisitor.java b/src/main/java/com/alibaba/druid/sql/dialect/oracle/visitor/OracleSchemaStatVisitor.java index 13633630fa..6c236b6589 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/oracle/visitor/OracleSchemaStatVisitor.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/oracle/visitor/OracleSchemaStatVisitor.java @@ -326,11 +326,6 @@ public class OracleSchemaStatVisitor extends SchemaStatVisitor implements Oracle } - @Override - public void endVisit(OracleSelectForUpdate x) { - - } - @Override public void endVisit(OracleSelectJoin x) { @@ -402,12 +397,6 @@ public class OracleSchemaStatVisitor extends SchemaStatVisitor implements Oracle return true; } - @Override - public boolean visit(OracleSelectForUpdate x) { - - return true; - } - @Override public boolean visit(OracleSelectJoin x) { super.visit(x); @@ -1410,4 +1399,24 @@ public class OracleSchemaStatVisitor extends SchemaStatVisitor implements Oracle public void endVisit(OracleCheck x) { } + + @Override + public boolean visit(OracleSupplementalIdKey x) { + return true; + } + + @Override + public void endVisit(OracleSupplementalIdKey x) { + + } + + @Override + public boolean visit(OracleSupplementalLogGrp x) { + return true; + } + + @Override + public void endVisit(OracleSupplementalLogGrp x) { + + } } diff --git a/src/main/java/com/alibaba/druid/sql/dialect/sqlserver/ast/expr/SQLServerObjectReferenceExpr.java b/src/main/java/com/alibaba/druid/sql/dialect/sqlserver/ast/expr/SQLServerObjectReferenceExpr.java index 1029d0a14f..a94bb87e53 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/sqlserver/ast/expr/SQLServerObjectReferenceExpr.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/sqlserver/ast/expr/SQLServerObjectReferenceExpr.java @@ -111,4 +111,11 @@ public class SQLServerObjectReferenceExpr extends SQLServerObjectImpl implements this.schema = schema; } + public SQLServerObjectReferenceExpr clone() { + SQLServerObjectReferenceExpr x = new SQLServerObjectReferenceExpr(); + x.server = server; + x.database = database; + x.schema = schema; + return x; + } } diff --git a/src/main/java/com/alibaba/druid/sql/parser/Lexer.java b/src/main/java/com/alibaba/druid/sql/parser/Lexer.java index fbbb6d3512..2b477b3b7c 100644 --- a/src/main/java/com/alibaba/druid/sql/parser/Lexer.java +++ b/src/main/java/com/alibaba/druid/sql/parser/Lexer.java @@ -243,7 +243,7 @@ public class Lexer { } public String info() { - return this.token + " " + this.stringVal(); + return "pos " + this.pos + ", " + this.token + " " + this.stringVal(); } public final void nextTokenComma() { diff --git a/src/main/java/com/alibaba/druid/sql/parser/SQLCreateTableParser.java b/src/main/java/com/alibaba/druid/sql/parser/SQLCreateTableParser.java index cff4834d9b..ff5e521749 100644 --- a/src/main/java/com/alibaba/druid/sql/parser/SQLCreateTableParser.java +++ b/src/main/java/com/alibaba/druid/sql/parser/SQLCreateTableParser.java @@ -23,6 +23,7 @@ import com.alibaba.druid.sql.ast.statement.SQLConstraint; import com.alibaba.druid.sql.ast.statement.SQLCreateTableStatement; import com.alibaba.druid.sql.ast.statement.SQLExprTableSource; import com.alibaba.druid.sql.ast.statement.SQLTableElement; +import com.alibaba.druid.util.JdbcConstants; public class SQLCreateTableParser extends SQLDDLParser { @@ -82,19 +83,25 @@ public class SQLCreateTableParser extends SQLDDLParser { lexer.nextToken(); for (;;) { - if (lexer.token() == Token.IDENTIFIER // - || lexer.token() == Token.LITERAL_ALIAS) { + Token token = lexer.token(); + if (token == Token.IDENTIFIER + && lexer.stringVal().equalsIgnoreCase("SUPPLEMENTAL") + && JdbcConstants.ORACLE.equals(dbType)) { + this.parseCreateTableSupplementalLogingProps(createTable); + } else if (token == Token.IDENTIFIER // + || token == Token.LITERAL_ALIAS) { SQLColumnDefinition column = this.exprParser.parseColumn(); createTable.getTableElementList().add(column); - } else if (lexer.token == Token.PRIMARY // - || lexer.token == Token.UNIQUE // - || lexer.token == Token.CHECK // - || lexer.token == Token.CONSTRAINT) { + } else if (token == Token.PRIMARY // + || token == Token.UNIQUE // + || token == Token.CHECK // + || token == Token.CONSTRAINT + || token == Token.FOREIGN) { SQLConstraint constraint = this.exprParser.parseConstaint(); constraint.setParent(createTable); createTable.getTableElementList().add((SQLTableElement) constraint); - } else if (lexer.token() == Token.TABLESPACE) { - throw new ParserException("TODO " + lexer.token()); + } else if (token == Token.TABLESPACE) { + throw new ParserException("TODO " + token); } else { SQLColumnDefinition column = this.exprParser.parseColumn(); createTable.getTableElementList().add(column); @@ -135,6 +142,10 @@ public class SQLCreateTableParser extends SQLDDLParser { return createTable; } + protected void parseCreateTableSupplementalLogingProps(SQLCreateTableStatement stmt) { + throw new ParserException("TODO " + lexer.info()); + } + protected SQLCreateTableStatement newCreateStatement() { return new SQLCreateTableStatement(getDbType()); } diff --git a/src/main/java/com/alibaba/druid/sql/parser/SQLExprParser.java b/src/main/java/com/alibaba/druid/sql/parser/SQLExprParser.java index 96a712a6c4..8d4475b1fd 100644 --- a/src/main/java/com/alibaba/druid/sql/parser/SQLExprParser.java +++ b/src/main/java/com/alibaba/druid/sql/parser/SQLExprParser.java @@ -264,9 +264,17 @@ public class SQLExprParser extends SQLParser { sqlExpr = new SQLNCharExpr(lexer.stringVal()); lexer.nextToken(); break; - case VARIANT: - SQLVariantRefExpr varRefExpr = new SQLVariantRefExpr(lexer.stringVal()); + case VARIANT: { + String varName = lexer.stringVal(); lexer.nextToken(); + + if (varName.equals(":") && lexer.token() == Token.IDENTIFIER && JdbcConstants.ORACLE.equals(dbType)) { + String part2 = lexer.stringVal(); + lexer.nextToken(); + varName += part2; + } + + SQLVariantRefExpr varRefExpr = new SQLVariantRefExpr(varName); if (varRefExpr.getName().equals("@") && lexer.token() == Token.LITERAL_CHARS) { varRefExpr.setName("@'" + lexer.stringVal() + "'"); lexer.nextToken(); @@ -275,6 +283,7 @@ public class SQLExprParser extends SQLParser { lexer.nextToken(); } sqlExpr = varRefExpr; + } break; case DEFAULT: sqlExpr = new SQLDefaultExpr(); @@ -1018,6 +1027,7 @@ public class SQLExprParser extends SQLParser { case BINARY: case OVER: case ORDER: + case DO: identName = lexer.stringVal(); lexer.nextToken(); break; @@ -2023,6 +2033,18 @@ public class SQLExprParser extends SQLParser { } else if (lexer.token() == Token.ENABLE) { lexer.nextToken(); check.setEnable(true); + } else if (identifierEquals("VALIDATE")) { + lexer.nextToken(); + check.setValidate(Boolean.TRUE); + } else if (identifierEquals("NOVALIDATE")) { + lexer.nextToken(); + check.setValidate(Boolean.FALSE); + } else if (identifierEquals("RELY")) { + lexer.nextToken(); + check.setRely(Boolean.TRUE); + } else if (identifierEquals("NORELY")) { + lexer.nextToken(); + check.setRely(Boolean.FALSE); } return check; } @@ -2152,7 +2174,7 @@ public class SQLExprParser extends SQLParser { accept(Token.FOREIGN); accept(Token.KEY); - SQLForeignKeyConstraint fk = createForeignKey(); + SQLForeignKeyImpl fk = createForeignKey(); accept(Token.LPAREN); this.names(fk.getReferencingColumns()); @@ -2168,10 +2190,23 @@ public class SQLExprParser extends SQLParser { accept(Token.RPAREN); } + if (lexer.token() == Token.ON) { + lexer.nextToken(); + accept(Token.DELETE); + if (identifierEquals("CASCADE")) { + lexer.nextToken(); + fk.setOnDeleteCascade(true); + } else { + accept(Token.SET); + accept(Token.NULL); + fk.setOnDeleteSetNull(true); + } + } + return fk; } - protected SQLForeignKeyConstraint createForeignKey() { + protected SQLForeignKeyImpl createForeignKey() { return new SQLForeignKeyImpl(); } @@ -2205,6 +2240,8 @@ public class SQLExprParser extends SQLParser { expr = this.primaryRest(expr); expr = this.exprRest(expr); } + } else if (lexer.token() == Token.DO) { + expr = this.name(); } else { expr = expr(); } diff --git a/src/main/java/com/alibaba/druid/sql/parser/SQLSelectParser.java b/src/main/java/com/alibaba/druid/sql/parser/SQLSelectParser.java index 985cde3148..a8f7125798 100644 --- a/src/main/java/com/alibaba/druid/sql/parser/SQLSelectParser.java +++ b/src/main/java/com/alibaba/druid/sql/parser/SQLSelectParser.java @@ -444,6 +444,10 @@ public class SQLSelectParser extends SQLParser { tableReference.setExpr(expr()); } + protected SQLTableSource primaryTableSourceRest(SQLTableSource tableSource) { + return tableSource; + } + protected SQLTableSource parseTableSourceRest(SQLTableSource tableSource) { if ((tableSource.getAlias() == null) || (tableSource.getAlias().length() == 0)) { if (lexer.token() != Token.LEFT && lexer.token() != Token.RIGHT && lexer.token() != Token.FULL @@ -520,7 +524,37 @@ public class SQLSelectParser extends SQLParser { SQLJoinTableSource join = new SQLJoinTableSource(); join.setLeft(tableSource); join.setJoinType(joinType); - join.setRight(parseTableSource()); + + + SQLTableSource rightTableSource; + if (lexer.token() == Token.LPAREN) { + lexer.nextToken(); + SQLSelect select = this.select(); + rightTableSource = new SQLSubqueryTableSource(select); + accept(Token.RPAREN); + } else { + SQLExpr expr = this.expr(); + rightTableSource = new SQLExprTableSource(expr); + } + + if (identifierEquals("USING")) { + lexer.nextToken(); + if (lexer.token() == Token.LPAREN) { + lexer.nextToken(); + this.exprParser.exprList(join.getUsing(), join); + accept(Token.RPAREN); + } else { + join.getUsing().add(this.expr()); + } + + join.setAlias(this.tableAlias()); + } else { + rightTableSource.setAlias(this.tableAlias()); + + primaryTableSourceRest(rightTableSource); + } + + join.setRight(rightTableSource); if (!natural) { if (JdbcConstants.MYSQL.equals(dbType) && "NATURAL".equalsIgnoreCase(tableSource.getAlias())) { diff --git a/src/main/java/com/alibaba/druid/sql/parser/SQLStatementParser.java b/src/main/java/com/alibaba/druid/sql/parser/SQLStatementParser.java index 8a730245e1..b346991237 100644 --- a/src/main/java/com/alibaba/druid/sql/parser/SQLStatementParser.java +++ b/src/main/java/com/alibaba/druid/sql/parser/SQLStatementParser.java @@ -1682,6 +1682,10 @@ public class SQLStatementParser extends SQLParser { } else if (token == Token.OR) { lexer.nextToken(); accept(Token.REPLACE); + + if (identifierEquals("FORCE")) { + lexer.nextToken(); + } if (lexer.token() == Token.PROCEDURE) { lexer.reset(markBp, markChar, Token.CREATE); return parseCreateProcedure(); @@ -1980,6 +1984,11 @@ public class SQLStatementParser extends SQLParser { lexer.nextToken(); } + if (identifierEquals("FORCE")) { + lexer.nextToken(); + createView.setForce(true); + } + this.accept(Token.VIEW); if (lexer.token() == Token.IF || identifierEquals("IF")) { @@ -2025,7 +2034,8 @@ public class SQLStatementParser extends SQLParser { this.accept(Token.AS); - createView.setSubQuery(new SQLSelectParser(this.exprParser).select()); + SQLSelectParser selectParser = this.createSQLSelectParser(); + createView.setSubQuery(selectParser.select()); return createView; } diff --git a/src/main/java/com/alibaba/druid/sql/visitor/SQLASTOutputVisitor.java b/src/main/java/com/alibaba/druid/sql/visitor/SQLASTOutputVisitor.java index 9adb2b9852..ccf21b3f53 100644 --- a/src/main/java/com/alibaba/druid/sql/visitor/SQLASTOutputVisitor.java +++ b/src/main/java/com/alibaba/druid/sql/visitor/SQLASTOutputVisitor.java @@ -2710,7 +2710,9 @@ public class SQLASTOutputVisitor extends SQLASTVisitorAdapter implements Paramet printAndAccept(x.getReferencingColumns(), ", "); print(')'); - print0(ucase ? " REFERENCES " : " references "); + incrementIndent(); + println(); + print0(ucase ? "REFERENCES " : "references "); x.getReferencedTableName().accept(this); if (x.getReferencedColumns().size() > 0) { @@ -2718,6 +2720,14 @@ public class SQLASTOutputVisitor extends SQLASTVisitorAdapter implements Paramet printAndAccept(x.getReferencedColumns(), ", "); print(')'); } + + if (x.isOnDeleteCascade()) { + println(); + print0(ucase ? "ON DELETE CASCADE" : "on delete cascade"); + } else if (x.isOnDeleteSetNull()) { + print0(ucase ? "ON DELETE SET NULL" : "on delete set null"); + } + decrementIndent(); return false; } diff --git a/src/test/java/com/alibaba/druid/bvt/sql/mysql/create/MySqlCreateTableTest76.java b/src/test/java/com/alibaba/druid/bvt/sql/mysql/create/MySqlCreateTableTest76.java index b726767dd0..1c4e8d015a 100644 --- a/src/test/java/com/alibaba/druid/bvt/sql/mysql/create/MySqlCreateTableTest76.java +++ b/src/test/java/com/alibaba/druid/bvt/sql/mysql/create/MySqlCreateTableTest76.java @@ -28,23 +28,26 @@ public class MySqlCreateTableTest76 extends MysqlTest { @Test public void test_one() throws Exception { - String sql = "CREATE TABLE `log_info_20170516` (\n" + - " `toid` varchar(40) CHARACTER SET utf8 NOT NULL,\n" + - " `title` varchar(40) CHARACTER SET utf8 DEFAULT NULL,\n" + - " `traceid` varchar(50) CHARACTER SET utf8 DEFAULT NULL,\n" + - " `parentid` varchar(40) CHARACTER SET utf8 DEFAULT NULL,\n" + - " `key1` varchar(100) CHARACTER SET utf8 DEFAULT NULL,\n" + - " `key2` varchar(100) CHARACTER SET utf8 DEFAULT NULL,\n" + - " `key3` varchar(100) CHARACTER SET utf8 DEFAULT NULL,\n" + - " `type` varchar(50) CHARACTER SET utf8 DEFAULT NULL,\n" + - " `createdate` varchar(30) CHARACTER SET utf8 DEFAULT NULL,\n" + - " `classmethod` varchar(100) CHARACTER SET utf8 DEFAULT NULL,\n" + - " `linenum` int(11) DEFAULT NULL,\n" + - " `threadname` varchar(80) CHARACTER SET utf8 DEFAULT '',\n" + - " `content` text CHARACTER SET utf8,\n" + - " PRIMARY KEY (`toid`),\n" + - " KEY `traceid_index` (`traceid`)\n" + - ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;"; + String sql = "CREATE TABLE `sys_msg_entry_0320` (\n" + + " `provider_dsp_name` varchar(256) /*!50616 COLUMN_FORMAT COMPRESSED */ DEFAULT NULL COMMENT '消息提供者的显示名,提供给一些需要统一发送名称的系统消息',\n" + + " `template_data` varchar(2048) NOT NULL /*!50616 COLUMN_FORMAT COMPRESSED */ COMMENT '模板渲染时使用的数据,key-value对',\n" + + " `template_merge_data` varchar(512) /*!50616 COLUMN_FORMAT COMPRESSED */ DEFAULT NULL COMMENT '对于需要合并的消息,被合并的参数放在此处理',\n" + + " `expiration_date` datetime NOT NULL COMMENT '失效日期,精度到天,查询和任务处理时使用',\n" + + " `merge_key` varchar(128) DEFAULT NULL COMMENT '消息合并主键,合并成功的消息,不参与计数',\n" + + " `out_id` varchar(256) /*!50616 COLUMN_FORMAT COMPRESSED */ DEFAULT NULL COMMENT '外部关联源的id',\n" + + " `number_expiration_date` datetime DEFAULT NULL COMMENT '计数过期时间',\n" + + " `hidden` int(11) DEFAULT '0' COMMENT '标记是否在吊顶展示,1表示隐藏,0表示展示',\n" + + " `popup` tinyint(3) unsigned DEFAULT '0' COMMENT '表示消息提醒方式:0-数字提醒,1-layer,2-popup',\n" + + " `tag_id` bigint(20) unsigned DEFAULT NULL COMMENT 'TAG标志',\n" + + " `attribute` varchar(512) /*!50616 COLUMN_FORMAT COMPRESSED */ DEFAULT NULL COMMENT '消息的一些属性',\n" + + " `original_msg_ids` varchar(1024) /*!50616 COLUMN_FORMAT COMPRESSED */ DEFAULT NULL COMMENT '原始消息ID,多个id用半角逗号隔开',\n" + + " PRIMARY KEY (`id`),\n" + + " KEY `idx_expiration_date` (`expiration_date`),\n" + + " KEY `k_rid_hidd_cid` (`receiver_id`,`hidden`,`cat_id`,`expiration_date`),\n" + + " KEY `k_rid_aid_tid` (`receiver_id`,`app_id`,`type_id`,`expiration_date`),\n" + + " KEY `k_rid_stat_popup` (`receiver_id`,`status`,`popup`,`expiration_date`),\n" + + " KEY `k_tid` (`tag_id`)\n" + + ") ENGINE=InnoDB AUTO_INCREMENT=167279613030 DEFAULT CHARSET=gbk COMMENT='消息实体表'"; MySqlStatementParser parser = new MySqlStatementParser(sql); SQLStatement stmt = parser.parseCreateTable(); @@ -52,50 +55,56 @@ public class MySqlCreateTableTest76 extends MysqlTest { MySqlSchemaStatVisitor visitor = new MySqlSchemaStatVisitor(); stmt.accept(visitor); - Column column = visitor.getColumn("log_info_20170516", "toid"); + Column column = visitor.getColumn("sys_msg_entry_0320", "provider_dsp_name"); Assert.assertNotNull(column); Assert.assertEquals("varchar", column.getDataType()); { String output = SQLUtils.toMySqlString(stmt); - Assert.assertEquals("CREATE TABLE `log_info_20170516` (\n" + - "\t`toid` varchar(40) CHARACTER SET utf8 NOT NULL, \n" + - "\t`title` varchar(40) CHARACTER SET utf8 DEFAULT NULL, \n" + - "\t`traceid` varchar(50) CHARACTER SET utf8 DEFAULT NULL, \n" + - "\t`parentid` varchar(40) CHARACTER SET utf8 DEFAULT NULL, \n" + - "\t`key1` varchar(100) CHARACTER SET utf8 DEFAULT NULL, \n" + - "\t`key2` varchar(100) CHARACTER SET utf8 DEFAULT NULL, \n" + - "\t`key3` varchar(100) CHARACTER SET utf8 DEFAULT NULL, \n" + - "\t`type` varchar(50) CHARACTER SET utf8 DEFAULT NULL, \n" + - "\t`createdate` varchar(30) CHARACTER SET utf8 DEFAULT NULL, \n" + - "\t`classmethod` varchar(100) CHARACTER SET utf8 DEFAULT NULL, \n" + - "\t`linenum` int(11) DEFAULT NULL, \n" + - "\t`threadname` varchar(80) CHARACTER SET utf8 DEFAULT '', \n" + - "\t`content` text CHARACTER SET utf8, \n" + - "\tPRIMARY KEY (`toid`), \n" + - "\tKEY `traceid_index` (`traceid`)\n" + - ") ENGINE = InnoDB CHARSET = utf8mb4", output); + Assert.assertEquals("CREATE TABLE `sys_msg_entry_0320` (\n" + + "\t`provider_dsp_name` varchar(256) /*!50616 COLUMN_FORMAT COMPRESSED */ DEFAULT NULL COMMENT '消息提供者的显示名,提供给一些需要统一发送名称的系统消息', \n" + + "\t`template_data` varchar(2048) NOT NULL /*!50616 COLUMN_FORMAT COMPRESSED */ COMMENT '模板渲染时使用的数据,key-value对', \n" + + "\t`template_merge_data` varchar(512) /*!50616 COLUMN_FORMAT COMPRESSED */ DEFAULT NULL COMMENT '对于需要合并的消息,被合并的参数放在此处理', \n" + + "\t`expiration_date` datetime NOT NULL COMMENT '失效日期,精度到天,查询和任务处理时使用', \n" + + "\t`merge_key` varchar(128) DEFAULT NULL COMMENT '消息合并主键,合并成功的消息,不参与计数', \n" + + "\t`out_id` varchar(256) /*!50616 COLUMN_FORMAT COMPRESSED */ DEFAULT NULL COMMENT '外部关联源的id', \n" + + "\t`number_expiration_date` datetime DEFAULT NULL COMMENT '计数过期时间', \n" + + "\t`hidden` int(11) DEFAULT '0' COMMENT '标记是否在吊顶展示,1表示隐藏,0表示展示', \n" + + "\t`popup` tinyint(3) UNSIGNED DEFAULT '0' COMMENT '表示消息提醒方式:0-数字提醒,1-layer,2-popup', \n" + + "\t`tag_id` bigint(20) UNSIGNED DEFAULT NULL COMMENT 'TAG标志', \n" + + "\t`attribute` varchar(512) /*!50616 COLUMN_FORMAT COMPRESSED */ DEFAULT NULL COMMENT '消息的一些属性', \n" + + "\t`original_msg_ids` varchar(1024) /*!50616 COLUMN_FORMAT COMPRESSED */ DEFAULT NULL COMMENT '原始消息ID,多个id用半角逗号隔开', \n" + + "\tPRIMARY KEY (`id`), \n" + + "\tKEY `idx_expiration_date` (`expiration_date`), \n" + + "\tKEY `k_rid_hidd_cid` (`receiver_id`, `hidden`, `cat_id`, `expiration_date`), \n" + + "\tKEY `k_rid_aid_tid` (`receiver_id`, `app_id`, `type_id`, `expiration_date`), \n" + + "\tKEY `k_rid_stat_popup` (`receiver_id`, `status`, `popup`, `expiration_date`), \n" + + "\tKEY `k_tid` (`tag_id`)\n" + + ") ENGINE = InnoDB AUTO_INCREMENT = 167279613030 CHARSET = gbk COMMENT = '消息实体表'", output); } { String output = SQLUtils.toMySqlString(stmt, SQLUtils.DEFAULT_LCASE_FORMAT_OPTION); - Assert.assertEquals("create table `log_info_20170516` (\n" + - "\t`toid` varchar(40) character set utf8 not null, \n" + - "\t`title` varchar(40) character set utf8 default null, \n" + - "\t`traceid` varchar(50) character set utf8 default null, \n" + - "\t`parentid` varchar(40) character set utf8 default null, \n" + - "\t`key1` varchar(100) character set utf8 default null, \n" + - "\t`key2` varchar(100) character set utf8 default null, \n" + - "\t`key3` varchar(100) character set utf8 default null, \n" + - "\t`type` varchar(50) character set utf8 default null, \n" + - "\t`createdate` varchar(30) character set utf8 default null, \n" + - "\t`classmethod` varchar(100) character set utf8 default null, \n" + - "\t`linenum` int(11) default null, \n" + - "\t`threadname` varchar(80) character set utf8 default '', \n" + - "\t`content` text character set utf8, \n" + - "\tprimary key (`toid`), \n" + - "\tkey `traceid_index` (`traceid`)\n" + - ") engine = InnoDB charset = utf8mb4", output); + Assert.assertEquals("create table `sys_msg_entry_0320` (\n" + + "\t`provider_dsp_name` varchar(256) /*!50616 COLUMN_FORMAT COMPRESSED */ default null comment '消息提供者的显示名,提供给一些需要统一发送名称的系统消息', \n" + + "\t`template_data` varchar(2048) not null /*!50616 COLUMN_FORMAT COMPRESSED */ comment '模板渲染时使用的数据,key-value对', \n" + + "\t`template_merge_data` varchar(512) /*!50616 COLUMN_FORMAT COMPRESSED */ default null comment '对于需要合并的消息,被合并的参数放在此处理', \n" + + "\t`expiration_date` datetime not null comment '失效日期,精度到天,查询和任务处理时使用', \n" + + "\t`merge_key` varchar(128) default null comment '消息合并主键,合并成功的消息,不参与计数', \n" + + "\t`out_id` varchar(256) /*!50616 COLUMN_FORMAT COMPRESSED */ default null comment '外部关联源的id', \n" + + "\t`number_expiration_date` datetime default null comment '计数过期时间', \n" + + "\t`hidden` int(11) default '0' comment '标记是否在吊顶展示,1表示隐藏,0表示展示', \n" + + "\t`popup` tinyint(3) unsigned default '0' comment '表示消息提醒方式:0-数字提醒,1-layer,2-popup', \n" + + "\t`tag_id` bigint(20) unsigned default null comment 'TAG标志', \n" + + "\t`attribute` varchar(512) /*!50616 COLUMN_FORMAT COMPRESSED */ default null comment '消息的一些属性', \n" + + "\t`original_msg_ids` varchar(1024) /*!50616 COLUMN_FORMAT COMPRESSED */ default null comment '原始消息ID,多个id用半角逗号隔开', \n" + + "\tprimary key (`id`), \n" + + "\tkey `idx_expiration_date` (`expiration_date`), \n" + + "\tkey `k_rid_hidd_cid` (`receiver_id`, `hidden`, `cat_id`, `expiration_date`), \n" + + "\tkey `k_rid_aid_tid` (`receiver_id`, `app_id`, `type_id`, `expiration_date`), \n" + + "\tkey `k_rid_stat_popup` (`receiver_id`, `status`, `popup`, `expiration_date`), \n" + + "\tkey `k_tid` (`tag_id`)\n" + + ") engine = InnoDB auto_increment = 167279613030 charset = gbk comment = '消息实体表'", output); } } } diff --git a/src/test/java/com/alibaba/druid/bvt/sql/mysql/select/MySqlSelectTest_comma_src.java b/src/test/java/com/alibaba/druid/bvt/sql/mysql/select/MySqlSelectTest_comma_src.java new file mode 100644 index 0000000000..bced66631e --- /dev/null +++ b/src/test/java/com/alibaba/druid/bvt/sql/mysql/select/MySqlSelectTest_comma_src.java @@ -0,0 +1,66 @@ +/* + * 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.bvt.sql.mysql.select; + +import com.alibaba.druid.sql.MysqlTest; +import com.alibaba.druid.sql.SQLUtils; +import com.alibaba.druid.sql.ast.SQLStatement; +import com.alibaba.druid.sql.ast.statement.SQLSelect; +import com.alibaba.druid.sql.ast.statement.SQLSelectQueryBlock; +import com.alibaba.druid.sql.ast.statement.SQLSelectStatement; +import com.alibaba.druid.sql.ast.statement.SQLTableSource; +import com.alibaba.druid.sql.visitor.SchemaStatVisitor; +import com.alibaba.druid.util.JdbcConstants; +import org.junit.Assert; + +import java.util.List; + +public class MySqlSelectTest_comma_src extends MysqlTest { + + public void test_0() throws Exception { + String sql = "select * from t1, t2, t3"; + + + List statementList = SQLUtils.parseStatements(sql, JdbcConstants.MYSQL, true); + Assert.assertEquals(1, statementList.size()); + + SQLStatement stmt = statementList.get(0); + + SQLSelect select = ((SQLSelectStatement)stmt).getSelect(); + SQLTableSource tableSource = ((SQLSelectQueryBlock)select.getQuery()).getFrom(); + + SchemaStatVisitor visitor = SQLUtils.createSchemaStatVisitor(JdbcConstants.MYSQL); + stmt.accept(visitor); + +// System.out.println("Tables : " + visitor.getTables()); +// System.out.println("fields : " + visitor.getColumns()); +// System.out.println("coditions : " + visitor.getConditions()); +// System.out.println("orderBy : " + visitor.getOrderByColumns()); + +// Assert.assertEquals(1, visitor.getTables().size()); +// Assert.assertEquals(1, visitor.getColumns().size()); +// Assert.assertEquals(0, visitor.getConditions().size()); +// Assert.assertEquals(0, visitor.getOrderByColumns().size()); + + { + String output = SQLUtils.toMySqlString(stmt); + Assert.assertEquals("SELECT *\n" + + "FROM t1, t2, t3", // + output); + } + + } +} diff --git a/src/test/java/com/alibaba/druid/bvt/sql/oracle/alter/OracleAlterTableTest19.java b/src/test/java/com/alibaba/druid/bvt/sql/oracle/alter/OracleAlterTableTest19.java index 979e3d000f..7703a414d8 100644 --- a/src/test/java/com/alibaba/druid/bvt/sql/oracle/alter/OracleAlterTableTest19.java +++ b/src/test/java/com/alibaba/druid/bvt/sql/oracle/alter/OracleAlterTableTest19.java @@ -52,10 +52,11 @@ public class OracleAlterTableTest19 extends OracleTest { System.out.println("relationships : " + visitor.getRelationships()); System.out.println("orderBy : " + visitor.getOrderByColumns()); - Assert.assertEquals("ALTER TABLE warehouses" // - + "\n\tADD CONSTRAINT wh_unq UNIQUE (warehouse_id, warehouse_name)"// - + "\n\tUSING INDEX PCTFREE 5"// - + "\n\tEXCEPTIONS INTO wrong_id", SQLUtils.toSQLString(stmt, JdbcConstants.ORACLE)); + Assert.assertEquals("ALTER TABLE warehouses\n" + + "\tADD CONSTRAINT wh_unq UNIQUE (warehouse_id, warehouse_name)\n" + + "\t\tUSING INDEX\n" + + "\t\tPCTFREE 5\n" + + "\t\tEXCEPTIONS INTO wrong_id", SQLUtils.toSQLString(stmt, JdbcConstants.ORACLE)); Assert.assertEquals(1, visitor.getTables().size()); diff --git a/src/test/java/com/alibaba/druid/bvt/sql/oracle/block/OracleBlockTest10.java b/src/test/java/com/alibaba/druid/bvt/sql/oracle/block/OracleBlockTest10.java index d95bed9d49..2171380777 100644 --- a/src/test/java/com/alibaba/druid/bvt/sql/oracle/block/OracleBlockTest10.java +++ b/src/test/java/com/alibaba/druid/bvt/sql/oracle/block/OracleBlockTest10.java @@ -61,7 +61,7 @@ public class OracleBlockTest10 extends OracleTest { Assert.assertTrue(visitor.getTables().containsKey(new TableStat.Name("employees"))); Assert.assertTrue(visitor.getTables().containsKey(new TableStat.Name("departments"))); - Assert.assertEquals(5, visitor.getColumns().size()); + Assert.assertEquals(6, visitor.getColumns().size()); Assert.assertEquals(3, visitor.getConditions().size()); // Assert.assertTrue(visitor.getColumns().contains(new TableStat.Column("employees", "salary"))); diff --git a/src/test/java/com/alibaba/druid/bvt/sql/oracle/create/OracleCreateTableTest18.java b/src/test/java/com/alibaba/druid/bvt/sql/oracle/create/OracleCreateTableTest18.java index 740828d9e2..f719dc1b0d 100644 --- a/src/test/java/com/alibaba/druid/bvt/sql/oracle/create/OracleCreateTableTest18.java +++ b/src/test/java/com/alibaba/druid/bvt/sql/oracle/create/OracleCreateTableTest18.java @@ -52,21 +52,18 @@ public class OracleCreateTableTest18 extends OracleTest { Assert.assertEquals(1, statementList.size()); - Assert.assertEquals("CREATE TABLE \"SONAR\".\"AUTHORS\" (" // - + "\n\t\"ID\" NUMBER(38, 0) NOT NULL ENABLE," // - + "\n\t\"PERSON_ID\" NUMBER(38, 0) NOT NULL ENABLE," // - + "\n\t\"LOGIN\" VARCHAR2(100 BYTE)," // - + "\n\t\"CREATED_AT\" TIMESTAMP(6)," // - + "\n\t\"UPDATED_AT\" TIMESTAMP(6)," // - + "\n\tPRIMARY KEY (\"ID\")" - + "\n\tUSING INDEX PCTFREE 10 INITRANS 2 MAXTRANS 255 COMPUTE STATISTICS TABLESPACE \"USERS\" ENABLE" // - + "\n)" - + "\nPCTFREE 10" - + "\nINITRANS 1" - + "\nMAXTRANS 255" - + "\nNOCOMPRESS" - + "\nLOGGING" - + "\nTABLESPACE \"USERS\"", // + Assert.assertEquals("CREATE TABLE dept_20 (\n" + + "\temployee_id NUMBER(4),\n" + + "\tlast_name VARCHAR2(10),\n" + + "\tjob_id VARCHAR2(9),\n" + + "\tmanager_id NUMBER(4),\n" + + "\thire_date DATE,\n" + + "\tsalary NUMBER(7, 2),\n" + + "\tcommission_pct NUMBER(7, 2),\n" + + "\tdepartment_id,\n" + + "\tCONSTRAINT fk_deptno FOREIGN KEY (department_id)\n" + + "\t\tREFERENCES departments (department_id)\n" + + ")", // SQLUtils.toSQLString(statement, JdbcConstants.ORACLE)); OracleSchemaStatVisitor visitor = new OracleSchemaStatVisitor(); diff --git a/src/test/java/com/alibaba/druid/bvt/sql/oracle/visitor/OracleASTVisitorAdapterTest.java b/src/test/java/com/alibaba/druid/bvt/sql/oracle/visitor/OracleASTVisitorAdapterTest.java index 8d8d05c4df..7f3f21c5a1 100644 --- a/src/test/java/com/alibaba/druid/bvt/sql/oracle/visitor/OracleASTVisitorAdapterTest.java +++ b/src/test/java/com/alibaba/druid/bvt/sql/oracle/visitor/OracleASTVisitorAdapterTest.java @@ -77,7 +77,6 @@ import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleMultiInsertStatement. import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OraclePLSQLCommitStatement; import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OraclePrimaryKey; import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleSavePointStatement; -import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleSelectForUpdate; import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleSelectPivot; import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleSelectQueryBlock; import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleSelectRestriction.CheckOption; @@ -100,7 +99,6 @@ public class OracleASTVisitorAdapterTest extends TestCase { new OracleAnalyticWindowing().accept(adapter); new SQLDateExpr().accept(adapter); new OracleDbLinkExpr().accept(adapter); - new OracleSelectForUpdate().accept(adapter); new OracleSelectPivot.Item().accept(adapter); new OracleSelectPivot().accept(adapter); new CheckOption().accept(adapter); diff --git a/src/test/resources/bvt/parser/mysql-8.txt b/src/test/resources/bvt/parser/mysql-8.txt index 17bc4c3d22..99f1b41baa 100644 --- a/src/test/resources/bvt/parser/mysql-8.txt +++ b/src/test/resources/bvt/parser/mysql-8.txt @@ -7,14 +7,13 @@ WHERE a.examine_id = b.examine_id --------------------------- SELECT a.examine_id AS examineId, a.examine_name AS examineName, a.level_limit AS levelLimit -FROM c_examinepaper a, ( - SELECT examine_id +FROM c_examinepaper a, (SELECT examine_id FROM c_examinepaper WHERE level_limit = ? AND status = 1 MINUS SELECT DISTINCT examine_id FROM course - ) b + ) b WHERE a.examine_id = b.examine_id