Oracle支持GroupingSets Clause

git-svn-id: http://code.alibabatech.com/svn/druid/trunk@155 b9813039-fb51-4c41-a8b9-e21c2acb5095
This commit is contained in:
wenshao
2011-05-20 02:46:38 +00:00
parent 3d060c191e
commit ce32206d97
13 changed files with 210 additions and 32 deletions
@@ -0,0 +1,27 @@
package com.alibaba.druid.sql.ast.expr;
import java.util.ArrayList;
import java.util.List;
import com.alibaba.druid.sql.ast.SQLExpr;
import com.alibaba.druid.sql.ast.SQLExprImpl;
import com.alibaba.druid.sql.visitor.SQLASTVisitor;
public class SQLListExpr extends SQLExprImpl {
private static final long serialVersionUID = 1L;
private final List<SQLExpr> items = new ArrayList<SQLExpr>();
public List<SQLExpr> getItems() {
return items;
}
@Override
protected void accept0(SQLASTVisitor visitor) {
if (visitor.visit(this)) {
acceptChild(visitor, items);
}
visitor.endVisit(this);
}
}
@@ -0,0 +1,32 @@
package com.alibaba.druid.sql.dialect.oracle.ast.clause;
import java.util.ArrayList;
import java.util.List;
import com.alibaba.druid.sql.ast.SQLExpr;
import com.alibaba.druid.sql.ast.SQLExprImpl;
import com.alibaba.druid.sql.dialect.oracle.ast.visitor.OracleASTVisitor;
import com.alibaba.druid.sql.visitor.SQLASTVisitor;
public class GroupingSetExpr extends SQLExprImpl {
private static final long serialVersionUID = 1L;
private final List<SQLExpr> parameters = new ArrayList<SQLExpr>();
public List<SQLExpr> getParameters() {
return parameters;
}
@Override
protected void accept0(SQLASTVisitor visitor) {
accept0((OracleASTVisitor) visitor);
}
protected void accept0(OracleASTVisitor visitor) {
if (visitor.visit(this)) {
acceptChild(visitor, parameters);
}
visitor.endVisit(this);
}
}
@@ -21,6 +21,7 @@ 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.FlashbackQueryClause.AsOfFlashbackQueryClause;
import com.alibaba.druid.sql.dialect.oracle.ast.clause.FlashbackQueryClause.VersionsFlashbackQueryClause;
import com.alibaba.druid.sql.dialect.oracle.ast.clause.GroupingSetExpr;
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.expr.OracleAggregateExpr;
@@ -328,4 +329,8 @@ public interface OracleASTVisitor extends SQLASTVisitor {
boolean visit(AsOfFlashbackQueryClause x);
void endVisit(AsOfFlashbackQueryClause x);
boolean visit(GroupingSetExpr x);
void endVisit(GroupingSetExpr x);
}
@@ -33,6 +33,7 @@ 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.FlashbackQueryClause.AsOfFlashbackQueryClause;
import com.alibaba.druid.sql.dialect.oracle.ast.clause.FlashbackQueryClause.VersionsFlashbackQueryClause;
import com.alibaba.druid.sql.dialect.oracle.ast.clause.GroupingSetExpr;
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.expr.OracleAggregateExpr;
@@ -1560,5 +1561,19 @@ public class OracleOutputVisitor extends SQLASTOutputVisitor implements OracleAS
public void endVisit(AsOfFlashbackQueryClause x) {
}
@Override
public boolean visit(GroupingSetExpr x) {
print("GROUPING SETS");
print(" (");
printAndAccept(x.getParameters(), ", ");
print(")");
return false;
}
@Override
public void endVisit(GroupingSetExpr x) {
}
}
@@ -26,6 +26,7 @@ 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.FlashbackQueryClause.AsOfFlashbackQueryClause;
import com.alibaba.druid.sql.dialect.oracle.ast.clause.FlashbackQueryClause.VersionsFlashbackQueryClause;
import com.alibaba.druid.sql.dialect.oracle.ast.clause.GroupingSetExpr;
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.expr.OracleAggregateExpr;
@@ -56,7 +57,7 @@ public class OracleSelectParser extends SQLSelectParser {
public OracleSelectParser(Lexer lexer){
super(lexer);
}
protected SQLExprParser createExprParser() {
return new OracleExprParser(lexer);
}
@@ -199,9 +200,23 @@ public class OracleSelectParser extends SQLSelectParser {
accept(Token.BY);
SQLSelectGroupByClause groupBy = new SQLSelectGroupByClause();
while (true) {
groupBy.getItems().add(this.createExprParser().expr());
if (!(lexer.token() == (Token.COMMA))) break;
for (;;) {
if (identifierEquals("GROUPING")) {
GroupingSetExpr groupingSet = new GroupingSetExpr();
lexer.nextToken();
acceptIdentifier("SETS");
accept(Token.LPAREN);
createExprParser().exprList(groupingSet.getParameters());
accept(Token.RPAREN);
groupBy.getItems().add(groupingSet);
} else {
groupBy.getItems().add(this.createExprParser().expr());
}
if (!(lexer.token() == (Token.COMMA))) {
break;
}
lexer.nextToken();
}
@@ -246,8 +261,7 @@ public class OracleSelectParser extends SQLSelectParser {
lexer.nextToken();
accept(Token.BY);
if (lexer.token() == Token.PRIOR) {
lexer.nextToken();
hierachical.setPrior(true);
@@ -301,40 +315,40 @@ 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();
PartitionExtensionClause partition = new PartitionExtensionClause();
if (lexer.token() == Token.LPAREN) {
lexer.nextToken();
partition.setPartition(createExprParser().name());
@@ -345,15 +359,15 @@ public class OracleSelectParser extends SQLSelectParser {
createExprParser().names(partition.getFor());
accept(Token.RPAREN);
}
tableReference.setPartition(partition);
}
if (identifierEquals("SUBPARTITION")) {
lexer.nextToken();
PartitionExtensionClause partition = new PartitionExtensionClause();
partition.setSubPartition(true);
if (lexer.token() == Token.LPAREN) {
lexer.nextToken();
partition.setPartition(createExprParser().name());
@@ -364,16 +378,16 @@ public class OracleSelectParser extends SQLSelectParser {
createExprParser().names(partition.getFor());
accept(Token.RPAREN);
}
tableReference.setPartition(partition);
}
if (lexer.token() == Token.AS) {
lexer.nextToken();
if (identifierEquals("OF")) {
lexer.nextToken();
AsOfFlashbackQueryClause clause = new AsOfFlashbackQueryClause();
if (identifierEquals("SCN")) {
clause.setType(AsOfFlashbackQueryClause.Type.SCN);
@@ -383,17 +397,17 @@ public class OracleSelectParser extends SQLSelectParser {
clause.setType(AsOfFlashbackQueryClause.Type.TIMESTAMP);
}
clause.setExpr(createExprParser().primary());
tableReference.setFlashback(clause);
} else {
throw new SQLParseException("TODO");
}
} else if (identifierEquals("VERSIONS")) {
lexer.nextToken();
if (lexer.token() == Token.BETWEEN) {
lexer.nextToken();
VersionsFlashbackQueryClause clause = new VersionsFlashbackQueryClause();
if (identifierEquals("SCN")) {
clause.setType(AsOfFlashbackQueryClause.Type.SCN);
@@ -402,21 +416,21 @@ public class OracleSelectParser extends SQLSelectParser {
acceptIdentifier("TIMESTAMP");
clause.setType(AsOfFlashbackQueryClause.Type.TIMESTAMP);
}
SQLBinaryOpExpr binaryExpr = (SQLBinaryOpExpr) createExprParser().expr();
if (binaryExpr.getOperator() != SQLBinaryOperator.BooleanAnd) {
throw new SQLParseException("syntax error : " + binaryExpr.getOperator());
}
clause.setBegin(binaryExpr.getLeft());
clause.setEnd(binaryExpr.getRight());
tableReference.setFlashback(clause);
} else {
throw new SQLParseException("TODO");
}
}
}
protected SQLTableSource parseTableSourceRest(SQLTableSource tableSource) {
@@ -465,7 +479,7 @@ public class OracleSelectParser extends SQLSelectParser {
lexer.nextToken();
joinType = OracleSelectJoin.JoinType.JOIN;
}
if (lexer.token() == (Token.COMMA)) {
lexer.nextToken();
joinType = OracleSelectJoin.JoinType.COMMA;
@@ -489,7 +503,7 @@ public class OracleSelectParser extends SQLSelectParser {
return join;
}
return tableSource;
}
@@ -40,6 +40,7 @@ import com.alibaba.druid.sql.ast.expr.SQLIdentifierExpr;
import com.alibaba.druid.sql.ast.expr.SQLInListExpr;
import com.alibaba.druid.sql.ast.expr.SQLInSubQueryExpr;
import com.alibaba.druid.sql.ast.expr.SQLIntegerExpr;
import com.alibaba.druid.sql.ast.expr.SQLListExpr;
import com.alibaba.druid.sql.ast.expr.SQLMethodInvokeExpr;
import com.alibaba.druid.sql.ast.expr.SQLNCharExpr;
import com.alibaba.druid.sql.ast.expr.SQLNotExpr;
@@ -148,6 +149,16 @@ public class SQLExprParser extends SQLParser {
case LPAREN:
lexer.nextToken();
sqlExpr = expr();
if (lexer.token() == Token.COMMA) {
SQLListExpr listExpr = new SQLListExpr();
listExpr.getItems().add(sqlExpr);
do {
lexer.nextToken();
listExpr.getItems().add(expr());
} while (lexer.token() == Token.COMMA);
sqlExpr = listExpr;
}
accept(Token.RPAREN);
break;
case INSERT:
@@ -40,6 +40,7 @@ import com.alibaba.druid.sql.ast.expr.SQLIdentifierExpr;
import com.alibaba.druid.sql.ast.expr.SQLInListExpr;
import com.alibaba.druid.sql.ast.expr.SQLInSubQueryExpr;
import com.alibaba.druid.sql.ast.expr.SQLIntegerExpr;
import com.alibaba.druid.sql.ast.expr.SQLListExpr;
import com.alibaba.druid.sql.ast.expr.SQLMethodInvokeExpr;
import com.alibaba.druid.sql.ast.expr.SQLNCharExpr;
import com.alibaba.druid.sql.ast.expr.SQLNotExpr;
@@ -808,4 +809,13 @@ public class SQLASTOutputVisitor extends SQLASTVisitorAdapter {
return false;
}
@Override
public boolean visit(SQLListExpr x) {
print("(");
printAndAccept(x.getItems(), ", ");
print(")");
return false;
}
}
@@ -38,6 +38,7 @@ import com.alibaba.druid.sql.ast.expr.SQLInListExpr;
import com.alibaba.druid.sql.ast.expr.SQLInSubQueryExpr;
import com.alibaba.druid.sql.ast.expr.SQLIntegerExpr;
import com.alibaba.druid.sql.ast.expr.SQLIntervalLiteralExpr;
import com.alibaba.druid.sql.ast.expr.SQLListExpr;
import com.alibaba.druid.sql.ast.expr.SQLMethodInvokeExpr;
import com.alibaba.druid.sql.ast.expr.SQLNCharExpr;
import com.alibaba.druid.sql.ast.expr.SQLNotExpr;
@@ -319,4 +320,8 @@ public interface SQLASTVisitor {
void endVisit(SQLInSubQueryExpr x);
boolean visit(SQLInSubQueryExpr x);
void endVisit(SQLListExpr x);
boolean visit(SQLListExpr x);
}
@@ -38,6 +38,7 @@ import com.alibaba.druid.sql.ast.expr.SQLInListExpr;
import com.alibaba.druid.sql.ast.expr.SQLInSubQueryExpr;
import com.alibaba.druid.sql.ast.expr.SQLIntegerExpr;
import com.alibaba.druid.sql.ast.expr.SQLIntervalLiteralExpr;
import com.alibaba.druid.sql.ast.expr.SQLListExpr;
import com.alibaba.druid.sql.ast.expr.SQLMethodInvokeExpr;
import com.alibaba.druid.sql.ast.expr.SQLNCharExpr;
import com.alibaba.druid.sql.ast.expr.SQLNotExpr;
@@ -541,4 +542,14 @@ public class SQLASTVisitorAdapter implements SQLASTVisitor {
public boolean visit(SQLInSubQueryExpr x) {
return true;
}
@Override
public void endVisit(SQLListExpr x) {
}
@Override
public boolean visit(SQLListExpr x) {
return false;
}
}
@@ -27,7 +27,10 @@ public class OracleSQLParserResourceTest extends TestCase {
// exec_test("bvt/parser/oracle-8.txt");
// exec_test("bvt/parser/oracle-9.txt");
// exec_test("bvt/parser/oracle-10.txt");
exec_test("bvt/parser/oracle-11.txt");
// exec_test("bvt/parser/oracle-11.txt");
// exec_test("bvt/parser/oracle-12.txt");
// exec_test("bvt/parser/oracle-13.txt");
exec_test("bvt/parser/oracle-14.txt");
}
@@ -0,0 +1,9 @@
SELECT department_id, MIN(salary), MAX (salary)
FROM employees
GROUP BY department_id
ORDER BY department_id;
---------------------------
SELECT department_id, MIN(salary), MAX(salary)
FROM employees
GROUP BY department_id
ORDER BY department_id;
@@ -0,0 +1,15 @@
SELECT DECODE(GROUPING(department_name), 1, 'All Departments',
department_name) AS department_name,
DECODE(GROUPING(job_id), 1, 'All Jobs', job_id) AS job_id,
COUNT(*) "Total Empl", AVG(salary) * 12 "Average Sal"
FROM employees e, departments d
WHERE d.department_id = e.department_id
GROUP BY CUBE (department_name, job_id)
ORDER BY department_name, job_id;
---------------------------
SELECT DECODE(GROUPING(department_name), 1, 'All Departments', department_name) AS department_name, DECODE(GROUPING(job_id), 1, 'All Jobs', job_id) AS job_id, COUNT(*) AS COUNT, AVG(salary) * 12 AS salary
FROM employees e , departments d
WHERE d.department_id = e.department_id
GROUP BY CUBE(department_name, job_id)
ORDER BY department_name, job_id;
@@ -0,0 +1,21 @@
SELECT channel_desc, calendar_month_desc, co.country_id,
TO_CHAR(sum(amount_sold) , '9,999,999,999') SALES$
FROM sales, customers, times, channels, countries co
WHERE sales.time_id=times.time_id
AND sales.cust_id=customers.cust_id
AND sales.channel_id= channels.channel_id
AND customers.country_id = co.country_id
AND channels.channel_desc IN ('Direct Sales', 'Internet')
AND times.calendar_month_desc IN ('2000-09', '2000-10')
AND co.country_iso_code IN ('UK', 'US')
GROUP BY GROUPING SETS(
(channel_desc, calendar_month_desc, co.country_id),
(channel_desc, co.country_id),
(calendar_month_desc, co.country_id) );
---------------------------
SELECT channel_desc, calendar_month_desc, co.country_id, TO_CHAR(sum(amount_sold), '9,999,999,999') AS SALES$
FROM sales , customers , times , channels , countries co
WHERE sales.time_id = times.time_id AND (sales.cust_id = customers.cust_id AND (sales.channel_id = channels.channel_id AND (customers.country_id = co.country_id AND (channels.channel_desc IN ('Direct Sales', 'Internet') AND (times.calendar_month_desc IN ('2000-09', '2000-10') AND co.country_iso_code IN ('UK', 'US'))))))
GROUP BY GROUPING SETS ((channel_desc, calendar_month_desc, co.country_id), (channel_desc, co.country_id), (calendar_month_desc, co.country_id));