[parser] parser: support window function grammar (#21)

This commit is contained in:
Haibin Xie
2018-11-08 14:39:32 +08:00
committed by Ti Chi Robot
parent 9cf30372a0
commit e18cfdc62b
3 changed files with 6079 additions and 5184 deletions

File diff suppressed because it is too large Load Diff

View File

@ -811,6 +811,30 @@ import (
ElseOpt "Optional else clause"
Type "Types"
OptExistingWindowName "Optional existing WINDOW name"
OptFromFirstLast "Optional FROM FIRST/LAST"
OptLLDefault "Optional LEAD/LAG default"
OptLeadLagInfo "Optional LEAD/LAG info"
OptNullTreatment "Optional NULL treatment"
OptPartitionClause "Optional PARTITION clause"
OptWindowOrderByClause "Optional ORDER BY clause in WINDOW"
OptWindowFrameClause "Optional FRAME clause in WINDOW"
OptWindowingClause "Optional OVER clause"
WindowingClause "OVER clause"
WindowClauseOptional "Optional WINDOW clause"
WindowDefinitionList "WINDOW definition list"
WindowDefinition "WINDOW definition"
WindowFrameUnits "WINDOW frame units"
WindowFrameBetween "WINDOW frame between"
WindowFrameBound "WINDOW frame bound"
WindowFrameExtent "WINDOW frame extent"
WindowFrameStart "WINDOW frame start"
WindowFuncCall "WINDOW function call"
WindowName "WINDOW name"
WindowNameOrSpec "WINDOW name or spec"
WindowSpec "WINDOW spec"
WindowSpecDetails "WINDOW spec details"
BetweenOrNotOp "Between predicate"
IsOrNotOp "Is predicate"
InOrNotOp "In predicate"
@ -3334,6 +3358,11 @@ SimpleExpr:
// TODO: Create a builtin function hold expr and collation. When do evaluation, convert expr result using the collation.
$$ = $1
}
| WindowFuncCall
{
// TODO: Remove this fake ast placeholder.
$$ = ast.NewParamMarkerExpr(yyS[yypt].offset)
}
| Literal
| paramMarker
{
@ -3813,31 +3842,31 @@ TrimDirection:
}
SumExpr:
"AVG" '(' BuggyDefaultFalseDistinctOpt Expression ')'
"AVG" '(' BuggyDefaultFalseDistinctOpt Expression ')' OptWindowingClause
{
$$ = &ast.AggregateFuncExpr{F: $1, Args: []ast.ExprNode{$4}, Distinct: $3.(bool)}
}
| builtinBitAnd '(' Expression ')'
| builtinBitAnd '(' Expression ')' OptWindowingClause
{
$$ = &ast.AggregateFuncExpr{F: $1, Args: []ast.ExprNode{$3}}
}
| builtinBitAnd '(' "ALL" Expression ')'
| builtinBitAnd '(' "ALL" Expression ')' OptWindowingClause
{
$$ = &ast.AggregateFuncExpr{F: $1, Args: []ast.ExprNode{$4}}
}
| builtinBitOr '(' Expression ')'
| builtinBitOr '(' Expression ')' OptWindowingClause
{
$$ = &ast.AggregateFuncExpr{F: $1, Args: []ast.ExprNode{$3}}
}
| builtinBitOr '(' "ALL" Expression ')'
| builtinBitOr '(' "ALL" Expression ')' OptWindowingClause
{
$$ = &ast.AggregateFuncExpr{F: $1, Args: []ast.ExprNode{$4}}
}
| builtinBitXor '(' Expression ')'
| builtinBitXor '(' Expression ')' OptWindowingClause
{
$$ = &ast.AggregateFuncExpr{F: $1, Args: []ast.ExprNode{$3}}
}
| builtinBitXor '(' "ALL" Expression ')'
| builtinBitXor '(' "ALL" Expression ')' OptWindowingClause
{
$$ = &ast.AggregateFuncExpr{F: $1, Args: []ast.ExprNode{$4}}
}
@ -3845,15 +3874,15 @@ SumExpr:
{
$$ = &ast.AggregateFuncExpr{F: $1, Args: $4.([]ast.ExprNode), Distinct: true}
}
| builtinCount '(' "ALL" Expression ')'
| builtinCount '(' "ALL" Expression ')' OptWindowingClause
{
$$ = &ast.AggregateFuncExpr{F: $1, Args: []ast.ExprNode{$4}}
}
| builtinCount '(' Expression ')'
| builtinCount '(' Expression ')' OptWindowingClause
{
$$ = &ast.AggregateFuncExpr{F: $1, Args: []ast.ExprNode{$3}}
}
| builtinCount '(' '*' ')'
| builtinCount '(' '*' ')' OptWindowingClause
{
args := []ast.ExprNode{ast.NewValueExpr(1)}
$$ = &ast.AggregateFuncExpr{F: $1, Args: args}
@ -3864,23 +3893,23 @@ SumExpr:
args = append(args, $6.(ast.ExprNode))
$$ = &ast.AggregateFuncExpr{F: $1, Args: args, Distinct: $3.(bool)}
}
| builtinMax '(' BuggyDefaultFalseDistinctOpt Expression ')'
| builtinMax '(' BuggyDefaultFalseDistinctOpt Expression ')' OptWindowingClause
{
$$ = &ast.AggregateFuncExpr{F: $1, Args: []ast.ExprNode{$4}, Distinct: $3.(bool)}
}
| builtinMin '(' BuggyDefaultFalseDistinctOpt Expression ')'
| builtinMin '(' BuggyDefaultFalseDistinctOpt Expression ')' OptWindowingClause
{
$$ = &ast.AggregateFuncExpr{F: $1, Args: []ast.ExprNode{$4}, Distinct: $3.(bool)}
}
| builtinSum '(' BuggyDefaultFalseDistinctOpt Expression ')'
| builtinSum '(' BuggyDefaultFalseDistinctOpt Expression ')' OptWindowingClause
{
$$ = &ast.AggregateFuncExpr{F: $1, Args: []ast.ExprNode{$4}, Distinct: $3.(bool)}
}
| builtinStddevPop '(' BuggyDefaultFalseDistinctOpt Expression ')'
| builtinStddevPop '(' BuggyDefaultFalseDistinctOpt Expression ')' OptWindowingClause
{
$$ = &ast.AggregateFuncExpr{F: ast.AggFuncStddevPop, Args: []ast.ExprNode{$4}, Distinct: $3.(bool)}
}
| builtinStddevSamp '(' BuggyDefaultFalseDistinctOpt Expression ')'
| builtinStddevSamp '(' BuggyDefaultFalseDistinctOpt Expression ')' OptWindowingClause
{
$$ = &ast.AggregateFuncExpr{F: $1, Args: []ast.ExprNode{$4}, Distinct: $3.(bool)}
}
@ -4340,7 +4369,7 @@ SelectStmtFromDual:
SelectStmtFromTable:
SelectStmtBasic "FROM"
TableRefsClause WhereClauseOptional SelectStmtGroup HavingClause
TableRefsClause WhereClauseOptional SelectStmtGroup HavingClause WindowClauseOptional
{
st := $1.(*ast.SelectStmt)
st.From = $3.(*ast.TableRefsClause)
@ -4420,6 +4449,277 @@ SelectStmt:
FromDual:
"FROM" "DUAL"
WindowClauseOptional:
{
$$ = nil
}
| "WINDOW" WindowDefinitionList
{
$$ = nil
}
WindowDefinitionList:
WindowDefinition
{
$$ = nil
}
| WindowDefinitionList ',' WindowDefinition
{
$$ = nil
}
WindowDefinition:
WindowName "AS" WindowSpec
{
$$ = nil
}
WindowName:
Identifier
{
$$ = nil
}
WindowSpec:
'(' WindowSpecDetails ')'
{
$$ = nil
}
WindowSpecDetails:
OptExistingWindowName OptPartitionClause OptWindowOrderByClause OptWindowFrameClause
{
$$ = nil
}
OptExistingWindowName:
{
$$ = nil
}
| WindowName
{
$$ = nil
}
OptPartitionClause:
{
$$ = nil
}
| "PARTITION" "BY" ByList
{
$$ = nil
}
OptWindowOrderByClause:
{
$$ = nil
}
| "ORDER" "BY" ByList
{
$$ = nil
}
OptWindowFrameClause:
{
$$ = nil
}
| WindowFrameUnits WindowFrameExtent
{
$$ = nil
}
WindowFrameUnits:
"ROWS"
{
$$ = nil
}
| "RANGE"
{
$$ = nil
}
| "GROUPS"
{
$$ = nil
}
WindowFrameExtent:
WindowFrameStart
{
$$ = nil
}
| WindowFrameBetween
{
$$ = nil
}
WindowFrameStart:
"UNBOUNDED" "PRECEDING"
{
$$ = nil
}
| NumLiteral "PRECEDING"
{
$$ = nil
}
| paramMarker "PRECEDING"
{
$$ = nil
}
| "INTERVAL" Expression TimeUnit "PRECEDING"
{
$$ = nil
}
| "CURRENT" "ROW"
{
$$ = nil
}
WindowFrameBetween:
"BETWEEN" WindowFrameBound "AND" WindowFrameBound
{
$$ = nil
}
WindowFrameBound:
WindowFrameStart
{
$$ = nil
}
| "UNBOUNDED" "FOLLOWING"
{
$$ = nil
}
| NumLiteral "FOLLOWING"
{
$$ = nil
}
| paramMarker "FOLLOWING"
{
$$ = nil
}
| "INTERVAL" Expression TimeUnit "FOLLOWING"
{
$$ = nil
}
OptWindowingClause:
{
$$ = nil
}
| WindowingClause
{
$$ = nil
}
WindowingClause:
"OVER" WindowNameOrSpec
{
$$ = nil
}
WindowNameOrSpec:
WindowName
{
$$ = nil
}
| WindowSpec
{
$$ = nil
}
WindowFuncCall:
"ROW_NUMBER" '(' ')' WindowingClause
{
$$ = nil
}
| "RANK" '(' ')' WindowingClause
{
$$ = nil
}
| "DENSE_RANK" '(' ')' WindowingClause
{
$$ = nil
}
| "CUME_DIST" '(' ')' WindowingClause
{
$$ = nil
}
| "PERCENT_RANK" '(' ')' WindowingClause
{
$$ = nil
}
| "NTILE" '(' SimpleExpr ')' WindowingClause
{
$$ = nil
}
| "LEAD" '(' Expression OptLeadLagInfo ')' OptNullTreatment WindowingClause
{
$$ = nil
}
| "LAG" '(' Expression OptLeadLagInfo ')' OptNullTreatment WindowingClause
{
$$ = nil
}
| "FIRST_VALUE" '(' Expression ')' OptNullTreatment WindowingClause
{
$$ = nil
}
| "LAST_VALUE" '(' Expression ')' OptNullTreatment WindowingClause
{
$$ = nil
}
| "NTH_VALUE" '(' Expression ',' SimpleExpr ')' OptFromFirstLast OptNullTreatment WindowingClause
{
$$ = nil
}
OptLeadLagInfo:
{
$$ = nil
}
| ',' NumLiteral OptLLDefault
{
$$ = nil
}
| ',' paramMarker OptLLDefault
{
$$ = nil
}
OptLLDefault:
{
$$ = nil
}
| ',' Expression
{
$$ = nil
}
OptNullTreatment:
{
$$ = nil
}
| "RESPECT" "NULLS"
{
$$ = nil
}
| "IGNORE" "NULLS"
{
$$ = nil
}
OptFromFirstLast:
{
$$ = nil
}
| "FROM" "FIRST"
{
$$ = nil
}
| "FROM" "LAST"
{
$$ = nil
}
TableRefsClause:
TableRefs

View File

@ -2459,3 +2459,67 @@ func (s *testParserSuite) TestWindowFunctionIdentifier(c *C) {
}
s.RunTest(c, table)
}
func (s *testParserSuite) TestWindowFunctions(c *C) {
table := []testCase{
// For window function descriptions.
// See https://dev.mysql.com/doc/refman/8.0/en/window-function-descriptions.html
{`SELECT CUME_DIST() OVER w FROM t;`, true},
{`SELECT DENSE_RANK() OVER w FROM t;`, true},
{`SELECT FIRST_VALUE(val) OVER w FROM t;`, true},
{`SELECT FIRST_VALUE(val) RESPECT NULLS OVER w FROM t;`, true},
{`SELECT FIRST_VALUE(val) IGNORE NULLS OVER w FROM t;`, true},
{`SELECT LAG(val) OVER w FROM t;`, true},
{`SELECT LAG(val, 1) OVER w FROM t;`, true},
{`SELECT LAG(val, 1, def) OVER w FROM t;`, true},
{`SELECT LAST_VALUE(val) OVER w FROM t;`, true},
{`SELECT LEAD(val) OVER w FROM t;`, true},
{`SELECT LEAD(val, 1) OVER w FROM t;`, true},
{`SELECT LEAD(val, 1, def) OVER w FROM t;`, true},
{`SELECT NTH_VALUE(val, 233) OVER w FROM t;`, true},
{`SELECT NTH_VALUE(val, 233) FROM FIRST OVER w FROM t;`, true},
{`SELECT NTH_VALUE(val, 233) FROM LAST OVER w FROM t;`, true},
{`SELECT NTH_VALUE(val, 233) FROM LAST IGNORE NULLS OVER w FROM t;`, true},
{`SELECT NTH_VALUE(val) OVER w FROM t;`, false},
{`SELECT NTILE(233) OVER w FROM t;`, true},
{`SELECT PERCENT_RANK() OVER w FROM t;`, true},
{`SELECT RANK() OVER w FROM t;`, true},
{`SELECT ROW_NUMBER() OVER w FROM t;`, true},
{`SELECT n, LAG(n, 1, 0) OVER w, LEAD(n, 1, 0) OVER w, n + LAG(n, 1, 0) OVER w FROM fib;`, true},
// For window function concepts and syntax.
// See https://dev.mysql.com/doc/refman/8.0/en/window-functions-usage.html
{`SELECT SUM(profit) OVER(PARTITION BY country) AS country_profit FROM sales;`, true},
{`SELECT SUM(profit) OVER() AS country_profit FROM sales;`, true},
{`SELECT AVG(profit) OVER() AS country_profit FROM sales;`, true},
{`SELECT BIT_XOR(profit) OVER() AS country_profit FROM sales;`, true},
{`SELECT COUNT(profit) OVER() AS country_profit FROM sales;`, true},
{`SELECT COUNT(ALL profit) OVER() AS country_profit FROM sales;`, true},
{`SELECT COUNT(*) OVER() AS country_profit FROM sales;`, true},
{`SELECT MAX(profit) OVER() AS country_profit FROM sales;`, true},
{`SELECT MIN(profit) OVER() AS country_profit FROM sales;`, true},
{`SELECT SUM(profit) OVER() AS country_profit FROM sales;`, true},
{`SELECT ROW_NUMBER() OVER(PARTITION BY country) AS row_num1 FROM sales;`, true},
{`SELECT ROW_NUMBER() OVER(PARTITION BY country, d ORDER BY year, product) AS row_num2 FROM sales;`, true},
// For window function frame specification.
// See https://dev.mysql.com/doc/refman/8.0/en/window-functions-frames.html
{`SELECT SUM(val) OVER (PARTITION BY subject ORDER BY time ROWS UNBOUNDED PRECEDING) FROM t;`, true},
{`SELECT AVG(val) OVER (PARTITION BY subject ORDER BY time ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING) FROM t;`, true},
{`SELECT AVG(val) OVER (ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING) FROM t;`, true},
{`SELECT AVG(val) OVER (ROWS BETWEEN 1 PRECEDING AND UNBOUNDED FOLLOWING) FROM t;`, true},
{`SELECT AVG(val) OVER (RANGE BETWEEN INTERVAL 5 DAY PRECEDING AND INTERVAL '2:30' MINUTE_SECOND FOLLOWING) FROM t;`, true},
{`SELECT AVG(val) OVER (RANGE BETWEEN CURRENT ROW AND CURRENT ROW) FROM t;`, true},
{`SELECT AVG(val) OVER (RANGE CURRENT ROW) FROM t;`, true},
// For named windows.
// See https://dev.mysql.com/doc/refman/8.0/en/window-functions-named-windows.html
{`SELECT RANK() OVER w FROM t WINDOW w AS (ORDER BY val);`, true},
{`SELECT RANK() OVER w FROM t WINDOW w AS ();`, true},
{`SELECT FIRST_VALUE(year) OVER (w ORDER BY year ASC) AS first FROM sales WINDOW w AS (PARTITION BY country);`, true},
{`SELECT RANK() OVER w1 FROM t WINDOW w1 AS (w2), w2 AS (), w3 AS (w1);`, true},
{`SELECT RANK() OVER w1 FROM t WINDOW w1 AS (w2), w2 AS (w3), w3 AS (w1);`, true},
}
s.enableWindowFunc = true
s.RunTest(c, table)
}