Index: art/syntax/all-bnf.html ================================================================== --- art/syntax/all-bnf.html +++ art/syntax/all-bnf.html @@ -10,120 +10,5 @@

sql-stmt-list:

sql-stmt-list::=<sql-stmt> ] [ ; [ <sql-stmt> ] ]*

sql-stmt:

sql-stmt::=EXPLAIN [ QUERY PLAN ] ] [ <alter-table-stmt> | <analyze-stmt> | <attach-stmt> | <begin-stmt> | <commit-stmt> | <create-index-stmt> | <create-table-stmt> | <create-trigger-stmt> | <create-view-stmt> | <create-virtual-table-stmt> | <delete-stmt> | <delete-stmt-limited> | <detach-stmt> | <drop-index-stmt> | <drop-table-stmt> | <drop-trigger-stmt> | <drop-view-stmt> | <insert-stmt> | <pragma-stmt> | <reindex-stmt> | <release-stmt> | <rollback-stmt> | <savepoint-stmt> | <select-stmt> | <update-stmt> | <update-stmt-limited> | <vacuum-stmt> ]1

alter-table-stmt:

-
alter-table-stmt::=ALTER TABLE [ database-name . ] table-name
RENAME TO new-table-name | ADD [ COLUMN ] <column-def> ]1
-

analyze-stmt:

-
analyze-stmt::=ANALYZE [ database-name | table-or-index-name | database-name . table-or-index-name ]
-

attach-stmt:

-
attach-stmt::=ATTACH [ DATABASE ] <expr> AS database-name
-

begin-stmt:

-
begin-stmt::=BEGIN [ DEFERRED | IMMEDIATE | EXCLUSIVE ] [ TRANSACTION ]
-

commit-stmt:

-
commit-stmt::=COMMIT | END ]1 [ TRANSACTION ]
-

rollback-stmt:

-
rollback-stmt::=ROLLBACK [ TRANSACTION ] [ TO [ SAVEPOINT ] savepoint-name ]
-

savepoint-stmt:

-
savepoint-stmt::=SAVEPOINT savepoint-name
-

release-stmt:

-
release-stmt::=RELEASE [ SAVEPOINT ] savepoint-name
-

create-index-stmt:

-
create-index-stmt::=CREATE [ UNIQUE ] INDEX [ IF NOT EXISTS ]
database-name . ] index-name ON table-name ( <indexed-column>, <indexed-column> ]* )
WHERE <expr> ]
-

indexed-column:

-
indexed-column::=column-name [ COLLATE collation-name ] [ ASC | DESC ]
-

create-table-stmt:

-
create-table-stmt::=CREATE [ TEMP | TEMPORARY ] TABLE [ IF NOT EXISTS ]
database-name . ] table-name
( <column-def>, <column-def> ]* [ , <table-constraint> ]* ) [ WITHOUT ROWID ] | AS <select-stmt> ]1
-

column-def:

-
column-def::=column-name [ <type-name> ] [ <column-constraint> ]*
-

type-name:

-
type-name::=name [ ( <signed-number> ) | ( <signed-number> , <signed-number> ) ]
-

column-constraint:

-
column-constraint::=CONSTRAINT name ]
PRIMARY KEY [ ASC | DESC ] <conflict-clause> [ AUTOINCREMENT ] | NOT NULL <conflict-clause> | UNIQUE <conflict-clause> | CHECK ( <expr> ) | DEFAULT [ <signed-number> | <literal-value> | ( <expr> ) ]1 | COLLATE collation-name | <foreign-key-clause> ]1
-

signed-number:

-
signed-number::=+ | - ] numeric-literal
-

table-constraint:

-
table-constraint::=CONSTRAINT name ]
[ [ PRIMARY KEY | UNIQUE ]1 ( <indexed-column>, <indexed-column> ]* ) <conflict-clause> | CHECK ( <expr> ) | FOREIGN KEY ( column-name, column-name ]* ) <foreign-key-clause> ]1
-

foreign-key-clause:

-
foreign-key-clause::=REFERENCES foreign-table [ ( column-name, column-name ]* ) ]
[ [ ON [ DELETE | UPDATE ]1 [ SET NULL | SET DEFAULT | CASCADE | RESTRICT | NO ACTION ]1 | MATCH name ]1 ]
[ [ NOT ] DEFERRABLE [ INITIALLY DEFERRED | INITIALLY IMMEDIATE ] ]
-

conflict-clause:

-
conflict-clause::=ON CONFLICT [ ROLLBACK | ABORT | FAIL | IGNORE | REPLACE ]1 ]
-

create-trigger-stmt:

-
create-trigger-stmt::=CREATE [ TEMP | TEMPORARY ] TRIGGER [ IF NOT EXISTS ]
database-name . ] trigger-name [ BEFORE | AFTER | INSTEAD OF ]
DELETE | INSERT | UPDATE [ OF column-name, column-name ]* ] ]1 ON table-name
FOR EACH ROW ] [ WHEN <expr> ]
BEGIN [ <update-stmt> | <insert-stmt> | <delete-stmt> | <select-stmt> ]1 ; END
-

create-view-stmt:

-
create-view-stmt::=CREATE [ TEMP | TEMPORARY ] VIEW [ IF NOT EXISTS ]
database-name . ] view-name AS <select-stmt>
-

create-virtual-table-stmt:

-
create-virtual-table-stmt::=CREATE VIRTUAL TABLE [ IF NOT EXISTS ]
database-name . ] table-name
USING module-name [ ( module-argument, module-argument ]* ) ]
-

with-clause:

-
with-clause::=WITH [ RECURSIVE ] <cte-table-name> AS ( <select-stmt> ), <cte-table-name> AS ( <select-stmt> ) ]*
-

cte-table-name:

-
cte-table-name::=table-name [ ( column-name, column-name ]* ) ]
-

recursive-cte:

-
recursive-cte::=<cte-table-name> AS ( initial-select [ UNION | UNION ALL ]1 recursive-select )
-

common-table-expression:

-
common-table-expression::=table-name [ ( column-name, column-name ]* ) ] AS ( <select-stmt> )
-

delete-stmt:

-
delete-stmt::=<with-clause> ] DELETE FROM <qualified-table-name>
WHERE <expr> ]
-

delete-stmt-limited:

-
delete-stmt-limited::=<with-clause> ] DELETE FROM <qualified-table-name>
WHERE <expr> ]
[ [ ORDER BY <ordering-term>, <ordering-term> ]* ]
LIMIT <expr> [ [ OFFSET | , ]1 <expr> ] ]
-

detach-stmt:

-
detach-stmt::=DETACH [ DATABASE ] database-name
-

drop-index-stmt:

-
drop-index-stmt::=DROP INDEX [ IF EXISTS ] [ database-name . ] index-name
-

drop-table-stmt:

-
drop-table-stmt::=DROP TABLE [ IF EXISTS ] [ database-name . ] table-name
-

drop-trigger-stmt:

-
drop-trigger-stmt::=DROP TRIGGER [ IF EXISTS ] [ database-name . ] trigger-name
-

drop-view-stmt:

-
drop-view-stmt::=DROP VIEW [ IF EXISTS ] [ database-name . ] view-name
-

expr:

-
expr::=<literal-value>
expr::=<bind-parameter>
expr::=[ [ database-name . ] table-name . ] column-name
expr::=unary-operator <expr>
expr::=<expr> binary-operator <expr>
expr::=function-name ( [ [ DISTINCT ] <expr>, <expr> ]* | * ] )
expr::=( <expr> )
expr::=CAST ( <expr> AS <type-name> )
expr::=<expr> COLLATE collation-name
expr::=<expr> [ NOT ] [ LIKE | GLOB | REGEXP | MATCH ]1 <expr> [ ESCAPE <expr> ]
expr::=<expr> [ ISNULL | NOTNULL | NOT NULL ]1
expr::=<expr> IS [ NOT ] <expr>
expr::=<expr> [ NOT ] BETWEEN <expr> AND <expr>
expr::=<expr> [ NOT ] IN [ ( [ <select-stmt> | <expr>, <expr> ]* ] ) | [ database-name . ] table-name ]1
expr::=[ [ NOT ] EXISTS ] ( <select-stmt> )
expr::=CASE [ <expr> ] WHEN <expr> THEN <expr> [ ELSE <expr> ] END
expr::=<raise-function>
-

raise-function:

-
raise-function::=RAISE ( [ IGNORE | [ ROLLBACK | ABORT | FAIL ]1 , error-message ]1 )
-

literal-value:

-
literal-value::=numeric-literal
literal-value::=string-literal
literal-value::=blob-literal
literal-value::=NULL
literal-value::=CURRENT_TIME
literal-value::=CURRENT_DATE
literal-value::=CURRENT_TIMESTAMP
-

numeric-literal:

-
numeric-literal::=digit [ decimal-point [ digit ]* ] | decimal-point digit ]1 [ E [ + | - ] digit ]
-

insert-stmt:

-
insert-stmt::=<with-clause> ] [ INSERT | REPLACE | INSERT OR REPLACE | INSERT OR ROLLBACK | INSERT OR ABORT | INSERT OR FAIL | INSERT OR IGNORE ]1 INTO
database-name . ] table-name [ ( column-name, column-name ]* ) ]
VALUES ( <expr>, <expr> ]* ), ( <expr>, <expr> ]* ) ]* | <select-stmt> | DEFAULT VALUES ]1
-

pragma-stmt:

-
pragma-stmt::=PRAGMA [ database-name . ] pragma-name [ = <pragma-value> | ( <pragma-value> ) ]
-

pragma-value:

-
pragma-value::=<signed-number>
pragma-value::=name
pragma-value::=string-literal
-

reindex-stmt:

-
reindex-stmt::=REINDEX [ collation-name | [ database-name . ] [ table-name | index-name ]1 ]
-

select-stmt:

-
select-stmt::=WITH [ RECURSIVE ] <common-table-expression>, <common-table-expression> ]* ]
SELECT [ DISTINCT | ALL ] <result-column>, <result-column> ]*
FROM [ <table-or-subquery>, <table-or-subquery> ]* | <join-clause> ]1 ]
WHERE <expr> ]
GROUP BY <expr>, <expr> ]* [ HAVING <expr> ] ] | VALUES ( <expr>, <expr> ]* ), ( <expr>, <expr> ]* ) ]* ]1<compound-operator> [ SELECT [ DISTINCT | ALL ] <result-column>, <result-column> ]*
FROM [ <table-or-subquery>, <table-or-subquery> ]* | <join-clause> ]1 ]
WHERE <expr> ]
GROUP BY <expr>, <expr> ]* [ HAVING <expr> ] ] | VALUES ( <expr>, <expr> ]* ), ( <expr>, <expr> ]* ) ]* ]1 ]*
ORDER BY <ordering-term>, <ordering-term> ]* ]
LIMIT <expr> [ [ OFFSET | , ]1 <expr> ] ]
-

join-clause:

-
join-clause::=<table-or-subquery> [ <join-operator> <table-or-subquery> <join-constraint> ]
-

select-core:

-
select-core::=SELECT [ DISTINCT | ALL ] <result-column>, <result-column> ]*
FROM [ <table-or-subquery>, <table-or-subquery> ]* | <join-clause> ]1 ]
WHERE <expr> ]
GROUP BY <expr>, <expr> ]* [ HAVING <expr> ] ]
select-core::=VALUES ( <expr>, <expr> ]* ), ( <expr>, <expr> ]* ) ]*
-

factored-select-stmt:

-
factored-select-stmt::=WITH [ RECURSIVE ] <common-table-expression>, <common-table-expression> ]* ]
<select-core><compound-operator> <select-core> ]*
ORDER BY <ordering-term>, <ordering-term> ]* ]
LIMIT <expr> [ [ OFFSET | , ]1 <expr> ] ]
-

simple-select-stmt:

-
simple-select-stmt::=WITH [ RECURSIVE ] <common-table-expression>, <common-table-expression> ]* ]
<select-core> [ ORDER BY <ordering-term>, <ordering-term> ]* ]
LIMIT <expr> [ [ OFFSET | , ]1 <expr> ] ]
-

compound-select-stmt:

-
compound-select-stmt::=WITH [ RECURSIVE ] <common-table-expression>, <common-table-expression> ]* ]
<select-core> [ UNION | UNION ALL | INTERSECT | EXCEPT ]1 <select-core>
ORDER BY <ordering-term>, <ordering-term> ]* ]
LIMIT <expr> [ [ OFFSET | , ]1 <expr> ] ]
-

table-or-subquery:

-
table-or-subquery::=database-name . ] table-name [ [ AS ] table-alias ]
INDEXED BY index-name | NOT INDEXED ]
table-or-subquery::=( [ <table-or-subquery>, <table-or-subquery> ]* | <join-clause> ]1 )
table-or-subquery::=( <select-stmt> ) [ [ AS ] table-alias ]
-

result-column:

-
result-column::=*
result-column::=table-name . *
result-column::=<expr> [ [ AS ] column-alias ]
-

join-operator:

-
join-operator::=,
join-operator::=NATURAL ] [ LEFT [ OUTER ] | INNER | CROSS ] JOIN
-

join-constraint:

-
join-constraint::=ON <expr> | USING ( column-name, column-name ]* ) ]
-

ordering-term:

-
ordering-term::=<expr> [ COLLATE collation-name ] [ ASC | DESC ]
-

compound-operator:

-
compound-operator::=UNION
compound-operator::=UNION ALL
compound-operator::=INTERSECT
compound-operator::=EXCEPT
-

update-stmt:

-
update-stmt::=<with-clause> ] UPDATE [ OR ROLLBACK | OR ABORT | OR REPLACE | OR FAIL | OR IGNORE ] <qualified-table-name>
SET column-name = <expr>, column-name = <expr> ]* [ WHERE <expr> ]
-

update-stmt-limited:

-
update-stmt-limited::=<with-clause> ] UPDATE [ OR ROLLBACK | OR ABORT | OR REPLACE | OR FAIL | OR IGNORE ] <qualified-table-name>
SET column-name = <expr>, column-name = <expr> ]* [ WHERE <expr> ]
[ [ ORDER BY <ordering-term>, <ordering-term> ]* ]
LIMIT <expr> [ [ OFFSET | , ]1 <expr> ] ]
-

qualified-table-name:

-
qualified-table-name::=database-name . ] table-name [ INDEXED BY index-name | NOT INDEXED ]
-

vacuum-stmt:

-
vacuum-stmt::=VACUUM
-

comment-syntax:

-
comment-syntax::=-- [ anything-except-newline ]* [ newline | end-of-input ]1
comment-syntax::=/* [ anything-except-*/ ]* [ */ | end-of-input ]1
- - Index: art/syntax/all-text.html ================================================================== --- art/syntax/all-text.html +++ art/syntax/all-text.html cannot compute difference between binary files Index: art/syntax/bubble-generator-data.tcl ================================================================== --- art/syntax/bubble-generator-data.tcl +++ art/syntax/bubble-generator-data.tcl @@ -523,32 +523,41 @@ } filter { line FILTER ( WHERE expr ) } window-defn { - stack {line ( {opt PARTITION BY {loop expr ,}}} + stack {line ( {opt existing-window-name} {opt PARTITION BY {loop expr ,}}} {opt ORDER BY {loop ordering-term ,}} {line {optx frame-spec} )} } frame-spec { - line {or RANGE ROWS} {or - {line BETWEEN {or {line UNBOUNDED PRECEDING} - {line expr PRECEDING} - {line CURRENT ROW} - {line expr FOLLOWING} - } - AND {or {line expr PRECEDING} - {line CURRENT ROW} - {line expr FOLLOWING} - {line UNBOUNDED FOLLOWING} - } - } - {or {line UNBOUNDED PRECEDING} - {line expr PRECEDING} - {line CURRENT ROW} - {line expr FOLLOWING} - } + stack { + line {or RANGE ROWS GROUPS} {or + {line BETWEEN {or {line UNBOUNDED PRECEDING} + {line expr PRECEDING} + {line CURRENT ROW} + {line expr FOLLOWING} + } + AND {or {line expr PRECEDING} + {line CURRENT ROW} + {line expr FOLLOWING} + {line UNBOUNDED FOLLOWING} + } + } + {or {line UNBOUNDED PRECEDING} + {line expr PRECEDING} + {line CURRENT ROW} + {line expr FOLLOWING} + } + } + } { + line {opt {or + {line EXCLUDE NO OTHERS} + {line EXCLUDE CURRENT ROW} + {line EXCLUDE GROUP} + {line EXCLUDE TIES} + } } } } function-invocation { line /function-name ( {or {line {optx DISTINCT} {toploop expr ,}} {} *} ) } Index: art/syntax/frame-spec.gif ================================================================== --- art/syntax/frame-spec.gif +++ art/syntax/frame-spec.gif cannot compute difference between binary files Index: art/syntax/window-defn.gif ================================================================== --- art/syntax/window-defn.gif +++ art/syntax/window-defn.gif cannot compute difference between binary files Index: pages/windowfunctions.in ================================================================== --- pages/windowfunctions.in +++ pages/windowfunctions.in @@ -13,13 +13,13 @@ RecursiveBubbleDiagram window-function-invocation window-defn frame-spec filter

Window functions are distinguished from ordinary SQL functions by the -presence of an OVER clause. If a function invocation has an OVER clause -then it is a window function, and if lacks an OVER clause it is an ordinary -function. Window functions might also have a FILTER +presence of an OVER clause. If a function invocation has an OVER clause, +then it is a window function. If it lacks an OVER clause, then it is an +ordinary aggregate or scalar function. Window functions may have a FILTER clause in between the function and the OVER clause.

Unlike ordinary functions, window functions cannot use the DISTINCT keyword. Also, Window functions may only appear in the result set and in the @@ -161,51 +161,79 @@

The frame-spec determines which output rows are read by an aggregate window function. The frame-spec consists of three parts:

The ending frame boundary can be omitted, in which case it defaults to CURRENT ROW.

The default frame-spec is: - RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW + RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW EXCLUDE NO OTHERS

The default means that aggregate window functions read all rows from the beginning of the partition up to and including the current row and its peers. -

If the frame type is RANGE, then rows with the same values for all ORDER BY -expressions are considered "peers". Or, if there are no ORDER BY terms, -all rows are peers. Rows that are peers always have the same window frames. +

If the frame type is RANGE or GROUPS, then rows with the same values for +all ORDER BY expressions are considered "peers". Or, if there are no ORDER BY +terms, all rows are peers. Rows that are peers always have the same window +frames.

There are five options for frame boundaries:
Frame Boundary Description
UNBOUNDED PRECEDING The start of the frame is the first row in the set.
<expr> PRECEDING <expr> is a constant expression - that evaluates to a non-negative integer value. The start - or end of the frame is <expr> rows before the current row. "0 - PRECEDING" is the same as "CURRENT ROW". This frame boundary type may - only be used with ROWS frames. -
CURRENT ROW The current row. For RANGE frame types, all peers of - the current row are also included in the window frame, regardless of - whether CURRENT ROW is used as the starting or ending frame boundary. + that evaluates to a non-negative numeric value. If the frame type + is ROWS or GROUPS, the expression must evaluate to an integer + value.

+ For a ROWS frame, the start or end of the frame is <expr> rows + before the current row.

+ For a GROUPS frame, the frame begins or ends with the peers + <expr> groups of peers before or after the current row's group. + For both ROWS and GROUPS frames, "0 PRECEDING" is the same as "CURRENT + ROW". +

A RANGE frame that uses "<expr> PRECEDING" must be + paired with an ORDER BY clause that contains a single expression. If + the ORDER BY expression for the current row is not a numeric value, + then "<expr> PRECEDING" is equivalent to "CURRENT ROW". + Otherwise, if the ORDER BY value is numeric for the current row, then + the value of "<expr>" is used as a logical range for determining + which groups are part of the current frame. +

+ For example, if the ORDER BY clause is ASC (not DESC) and the + "<expr> PRECEDING" is used as the start of the frame, then + the first group in the frame is that with the smallest value for + the ORDER BY expression that is greater than (current-row - + <expr>). If the ORDER BY clause is DESC, then the first group + is that with an ORDER BY expresion greater than (current-row + + <expr>). +

+ Or, if it is used as the end of the frame and the ORDER BY is ASC, then + the last group in the grame is that with the largest value for the + ORDER BY expression that is less than (current-row - <expr>). + For DESC the last group is that with the smallest ORDER BY value + that is greater than (current-row + <expr>). + +
CURRENT ROW The current row. For RANGE and GROUPS frame types, all + peers of the current row are also included in the window frame, + regardless of whether CURRENT ROW is used as the starting or ending + frame boundary.
<expr> FOLLOWING <expr> is a constant expression - that must evaluate to a non-negative integer value. The start - or end of the frame is <expr> rows after the current row. "0 - FOLLOWING" is the same as "CURRENT ROW". This frame boundary type may - only be used with ROWS frames. + that must evaluate to a non-negative numeric value. It is handled + in similarly to "<expr> PRECEDING".
UNBOUNDED FOLLOWING The end of the frame is the last row in the set.

The ending frame boundary must not take a form that appears higher in @@ -231,10 +259,14 @@ SELECT c, a, b, group_concat(b, '.') OVER ( ORDER BY c, a ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING ) AS group_concat FROM t1 ORDER BY c, a; + +

Even though they are technically part of the frame-specification, +[exclude clause|the EXCLUDE clause] and [window chaining] are described +separately below.

The PARTITION BY Clause

A window-defn may include a PARTITION BY clause. If so, the rows returned by the SELECT statement are divided into groups - @@ -291,10 +323,103 @@ SELECT c, a, b, group_concat(b, '.') FILTER (WHERE c!='two') OVER ( ORDER BY a ) AS group_concat FROM t1 ORDER BY a; + +

The EXCLUDE Clause

+ +

The optional EXCLUDE clause may take any of the following four forms: + +

+ + + -- The following SELECT statement returns: + -- + -- c | a | b | no_others | current_row | grp | ties + -- one | 1 | A | A.D.G | D.G | | A + -- one | 4 | D | A.D.G | A.G | | D + -- one | 7 | G | A.D.G | A.D | | G + -- three | 3 | C | A.D.G.C.F | A.D.G.F | A.D.G | A.D.G.C + -- three | 6 | F | A.D.G.C.F | A.D.G.C | A.D.G | A.D.G.F + -- two | 2 | B | A.D.G.C.F.B.E | A.D.G.C.F.E | A.D.G.C.F | A.D.G.C.F.B + -- two | 5 | E | A.D.G.C.F.B.E | A.D.G.C.F.B | A.D.G.C.F | A.D.G.C.F.E + -- + SELECT c, a, b, + group_concat(b, '.') OVER ( + ORDER BY c GROUPS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW EXCLUDE NO OTHERS + ) AS no_others, + group_concat(b, '.') OVER ( + ORDER BY c GROUPS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW EXCLUDE CURRENT ROW + ) AS current_row, + group_concat(b, '.') OVER ( + ORDER BY c GROUPS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW EXCLUDE GROUP + ) AS grp, + group_concat(b, '.') OVER ( + ORDER BY c GROUPS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW EXCLUDE TIES + ) AS ties + FROM t1 ORDER BY c, a; + + +

Window Chaining

+ +

+Window chaining is a shorthand that allows one window to be defined in terms +of another. Specifically, the shorthand allows the new window to implicitly +copy the PARTITION BY and optionally ORDER BY clauses of the base window. For +example, in the following: + + + SELECT group_concat(b, '.') OVER ( + win ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW + ) + FROM t1 + WINDOW win AS (PARTITION BY a ORDER BY c) + + +

+the window used by the group_concat() is equivalent to "PARTITION BY a ORDER +BY c ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW". In order to use window +chaining, all of the following must be true: + +

+ +

The two fragments of SQL below are similar, but not entirely equivalent, as +the latter will fail if the definition of window "win" contains a frame +specification. + + + SELECT group_concat(b, '.') OVER win ... + SELECT group_concat(b, '.') OVER (win) ... +

Built-in Window Functions

As well as aggregate window functions, SQLite features a set of built-in window functions based on