[parser] parser: support window function grammar (#21)
This commit is contained in:
10867
parser/parser.go
10867
parser/parser.go
File diff suppressed because it is too large
Load Diff
332
parser/parser.y
332
parser/parser.y
@ -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
|
||||
|
||||
@ -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)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user