diff --git a/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/clause/SampleClause.java b/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/clause/SampleClause.java new file mode 100644 index 0000000000..6e6e988e6f --- /dev/null +++ b/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/clause/SampleClause.java @@ -0,0 +1,49 @@ +package com.alibaba.druid.sql.dialect.oracle.ast.clause; + +import com.alibaba.druid.sql.ast.SQLExpr; +import com.alibaba.druid.sql.dialect.oracle.ast.OracleSQLObject; +import com.alibaba.druid.sql.dialect.oracle.ast.visitor.OracleASTVisitor; + +public class SampleClause extends OracleSQLObject { + + private static final long serialVersionUID = 1L; + + private boolean block = false; + + private int percent; + + private SQLExpr seedValue; + + public boolean isBlock() { + return block; + } + + public void setBlock(boolean block) { + this.block = block; + } + + public int getPercent() { + return percent; + } + + public void setPercent(int percent) { + this.percent = percent; + } + + public SQLExpr getSeedValue() { + return seedValue; + } + + public void setSeedValue(SQLExpr seedValue) { + this.seedValue = seedValue; + } + + @Override + protected void accept0(OracleASTVisitor visitor) { + if (visitor.visit(this)) { + acceptChild(visitor, seedValue); + } + visitor.endVisit(this); + } + +} diff --git a/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/stmt/OracleSelectTableReference.java b/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/stmt/OracleSelectTableReference.java index 4febb95cad..b32bd00eeb 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/stmt/OracleSelectTableReference.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/stmt/OracleSelectTableReference.java @@ -16,6 +16,7 @@ package com.alibaba.druid.sql.dialect.oracle.ast.stmt; import com.alibaba.druid.sql.ast.statement.SQLExprTableSource; +import com.alibaba.druid.sql.dialect.oracle.ast.clause.SampleClause; import com.alibaba.druid.sql.dialect.oracle.ast.visitor.OracleASTVisitor; import com.alibaba.druid.sql.visitor.SQLASTVisitor; @@ -26,6 +27,8 @@ public class OracleSelectTableReference extends SQLExprTableSource implements Or private boolean only = false; protected OracleSelectPivotBase pivot; + protected SampleClause sampleClause; + public OracleSelectTableReference(){ } @@ -38,6 +41,14 @@ public class OracleSelectTableReference extends SQLExprTableSource implements Or this.only = only; } + public SampleClause getSampleClause() { + return sampleClause; + } + + public void setSampleClause(SampleClause sampleClause) { + this.sampleClause = sampleClause; + } + public OracleSelectPivotBase getPivot() { return pivot; } @@ -54,6 +65,7 @@ public class OracleSelectTableReference extends SQLExprTableSource implements Or protected void accept0(OracleASTVisitor visitor) { if (visitor.visit(this)) { acceptChild(visitor, this.expr); + acceptChild(visitor, this.sampleClause); acceptChild(visitor, this.pivot); } visitor.endVisit(this); @@ -73,6 +85,8 @@ public class OracleSelectTableReference extends SQLExprTableSource implements Or this.pivot.output(buf); } - if ((this.alias != null) && (this.alias.length() != 0)) buf.append(this.alias); + if ((this.alias != null) && (this.alias.length() != 0)) { + buf.append(this.alias); + } } } diff --git a/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/visitor/OracleASTVisitor.java b/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/visitor/OracleASTVisitor.java index ffda4114c7..59d7eada7c 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/visitor/OracleASTVisitor.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/visitor/OracleASTVisitor.java @@ -19,6 +19,7 @@ import com.alibaba.druid.sql.ast.expr.SQLObjectCreateExpr; import com.alibaba.druid.sql.dialect.oracle.ast.OracleDataTypeInterval; import com.alibaba.druid.sql.dialect.oracle.ast.OracleHint; import com.alibaba.druid.sql.dialect.oracle.ast.OracleOrderBy; +import com.alibaba.druid.sql.dialect.oracle.ast.clause.SampleClause; import com.alibaba.druid.sql.dialect.oracle.ast.expr.OracleAggregateExpr; import com.alibaba.druid.sql.dialect.oracle.ast.expr.OracleAnalytic; import com.alibaba.druid.sql.dialect.oracle.ast.expr.OracleAnalyticWindowing; @@ -51,6 +52,7 @@ import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleSelectOrderByItem; import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleSelectPivot; import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleSelectRestriction; import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleSelectSubqueryTableSource; +import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleSelectTableReference; import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleSelectUnPivot; import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleTableColumn; import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleTableExpr; @@ -304,4 +306,11 @@ public interface OracleASTVisitor extends SQLASTVisitor { boolean visit(OracleUpdateStatement x); + boolean visit(SampleClause x); + + void endVisit(SampleClause x); + + boolean visit(OracleSelectTableReference x); + + void endVisit(OracleSelectTableReference x); } diff --git a/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/visitor/OracleOutputVisitor.java b/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/visitor/OracleOutputVisitor.java index 0e4267616d..f8909794b1 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/visitor/OracleOutputVisitor.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/oracle/ast/visitor/OracleOutputVisitor.java @@ -31,6 +31,7 @@ import com.alibaba.druid.sql.ast.statement.SQLSelectQueryBlock; import com.alibaba.druid.sql.dialect.oracle.ast.OracleDataTypeInterval; import com.alibaba.druid.sql.dialect.oracle.ast.OracleHint; import com.alibaba.druid.sql.dialect.oracle.ast.OracleOrderBy; +import com.alibaba.druid.sql.dialect.oracle.ast.clause.SampleClause; import com.alibaba.druid.sql.dialect.oracle.ast.expr.OracleAggregateExpr; import com.alibaba.druid.sql.dialect.oracle.ast.expr.OracleAnalytic; import com.alibaba.druid.sql.dialect.oracle.ast.expr.OracleAnalyticWindowing; @@ -715,6 +716,11 @@ public class OracleOutputVisitor extends SQLASTOutputVisitor implements OracleAS } else { x.getExpr().accept(this); } + + if (x.getSampleClause() != null) { + print(" "); + x.getSampleClause().accept(this); + } if (x.getPivot() != null) { incrementIndent(); @@ -1449,4 +1455,34 @@ public class OracleOutputVisitor extends SQLASTOutputVisitor implements OracleAS } + @Override + public boolean visit(SampleClause x) { + print("SAMPLE "); + + if (x.isBlock()) { + print("BLOCK "); + } + + print("("); + print(x.getPercent()); + print(")"); + + if (x.getSeedValue() != null) { + print(" SEED "); + x.getSeedValue().accept(this); + } + + return false; + } + + @Override + public void endVisit(SampleClause x) { + + } + + @Override + public void endVisit(OracleSelectTableReference x) { + + } + } 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 77975b0a97..5cb1b4e20f 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 @@ -22,6 +22,7 @@ import com.alibaba.druid.sql.ast.statement.SQLSelectQuery; import com.alibaba.druid.sql.ast.statement.SQLTableSource; import com.alibaba.druid.sql.ast.statement.SQLUnionQuery; import com.alibaba.druid.sql.dialect.oracle.ast.OracleHint; +import com.alibaba.druid.sql.dialect.oracle.ast.clause.SampleClause; import com.alibaba.druid.sql.dialect.oracle.ast.expr.OracleAggregateExpr; import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleSelect; import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleSelectForUpdate; @@ -294,6 +295,35 @@ public class OracleSelectParser extends SQLSelectParser { private void parseTableSourceQueryTableExpr(OracleSelectTableReference tableReference) { tableReference.setExpr(this.createExprParser().expr()); + + if (identifierEquals("SAMPLE")) { + lexer.nextToken(); + + SampleClause sample = new SampleClause(); + + if (identifierEquals("BLOCK")) { + sample.setBlock(true); + } + + accept(Token.LPAREN); + + if (lexer.token() != Token.LITERAL_INT) { + throw new ParserException("syntax error : " + lexer.token()); + } + sample.setPercent(lexer.integerValue().intValue()); + lexer.nextToken(); + + accept(Token.RPAREN); + + if (identifierEquals("SEED")) { + lexer.nextToken(); + accept(Token.LPAREN); + sample.setSeedValue(expr()); + accept(Token.RPAREN); + } + + tableReference.setSampleClause(sample); + } if (identifierEquals("PARTITION")) { lexer.nextToken(); diff --git a/src/test/java/com/alibaba/druid/sql/oracle/bvt/OracleSQLParserResourceTest.java b/src/test/java/com/alibaba/druid/sql/oracle/bvt/OracleSQLParserResourceTest.java index 5101141dd1..6654daea3c 100644 --- a/src/test/java/com/alibaba/druid/sql/oracle/bvt/OracleSQLParserResourceTest.java +++ b/src/test/java/com/alibaba/druid/sql/oracle/bvt/OracleSQLParserResourceTest.java @@ -22,7 +22,8 @@ public class OracleSQLParserResourceTest extends TestCase { exec_test("bvt/parser/oracle-3.txt"); exec_test("bvt/parser/oracle-4.txt"); exec_test("bvt/parser/oracle-5.txt"); - // exec_test("bvt/parser/oracle-6.txt"); PARTITION + //exec_test("bvt/parser/oracle-6.txt"); PARTITION + exec_test("bvt/parser/oracle-7.txt"); } diff --git a/src/test/resources/bvt/parser/oracle-7.txt b/src/test/resources/bvt/parser/oracle-7.txt new file mode 100644 index 0000000000..21e2486837 --- /dev/null +++ b/src/test/resources/bvt/parser/oracle-7.txt @@ -0,0 +1,4 @@ +SELECT COUNT(*) * 10 FROM orders SAMPLE (10); +--------------------------- +SELECT COUNT(*) * 10 +FROM orders SAMPLE (10); \ No newline at end of file