diff --git a/doc/src/sgml/ref/select.sgml b/doc/src/sgml/ref/select.sgml index f73ca6ed64..91a756b3ae 100644 --- a/doc/src/sgml/ref/select.sgml +++ b/doc/src/sgml/ref/select.sgml @@ -1,5 +1,5 @@ @@ -30,7 +30,8 @@ SELECT [ ALL | DISTINCT [ ON ( expressionselect ] [ ORDER BY expression [ ASC | DESC | USING operator ] [ NULLS { FIRST | LAST } ] [, ...] ] [ LIMIT { count | ALL } ] - [ OFFSET start ] + [ OFFSET start [ ROW | ROWS ] ] + [ FETCH { FIRST | NEXT } [ count ] { ROW | ROWS } ONLY ] [ FOR { UPDATE | SHARE } [ OF table_name [, ...] ] [ NOWAIT ] [...] ] where from_item can be one of: @@ -150,7 +151,7 @@ and with_query is: - If the LIMIT or OFFSET + If the LIMIT (or FETCH FIRST) or OFFSET clause is specified, the SELECT statement only returns a subset of the result rows. (See below.) @@ -891,6 +892,24 @@ OFFSET start class="parameter">count rows to be returned. + + SQL:2008 introduced a different syntax to achieve the same thing, + which PostgreSQL also supports. It is: + +OFFSET start { ROW | ROWS } +FETCH { FIRST | NEXT } [ count ] { ROW | ROWS } ONLY + + Both clauses are optional, but if present + the OFFSET clause must come before + the FETCH clause. ROW + and ROWS as well as FIRST + and NEXT are noise words that don't influence + the effects of these clauses. When using expressions other than + constants for the offset or fetch count, parentheses will be + necessary in most cases. If the fetch count is omitted, it + defaults to 1. + + When using LIMIT, it is a good idea to use an ORDER BY clause that constrains the result rows into a @@ -1337,13 +1356,30 @@ SELECT distributors.* WHERE distributors.name = 'Westward'; + + <literal>LIMIT</literal> and <literal>OFFSET</literal> + + + The clauses LIMIT and OFFSET + are PostgreSQL-specific syntax, also + used by MySQL. The SQL:2008 standard + has introduced the clauses OFFSET ... FETCH {FIRST|NEXT} + ... for the same functionality, as shown above + in , and this + syntax is also used by IBM DB2. + (Applications written for Oracle + frequently use a workaround involving the automatically + generated rownum column, not available in + PostgreSQL, to implement the effects of these clauses.) + + + Nonstandard Clauses - The clauses DISTINCT ON, - LIMIT, and OFFSET are not - defined in the SQL standard. + The clause DISTINCT ON is not defined in the + SQL standard. diff --git a/doc/src/sgml/ref/select_into.sgml b/doc/src/sgml/ref/select_into.sgml index de9a86a878..cbe46cf763 100644 --- a/doc/src/sgml/ref/select_into.sgml +++ b/doc/src/sgml/ref/select_into.sgml @@ -1,5 +1,5 @@ @@ -31,7 +31,8 @@ SELECT [ ALL | DISTINCT [ ON ( expressionselect ] [ ORDER BY expression [ ASC | DESC | USING operator ] [ NULLS { FIRST | LAST } ] [, ...] ] [ LIMIT { count | ALL } ] - [ OFFSET start ] + [ OFFSET start [ ROW | ROWS ] ] + [ FETCH { FIRST | NEXT } [ count ] { ROW | ROWS } ONLY ] [ FOR { UPDATE | SHARE } [ OF table_name [, ...] ] [ NOWAIT ] [...] ] diff --git a/doc/src/sgml/ref/values.sgml b/doc/src/sgml/ref/values.sgml index 23b84d7820..ef605f818f 100644 --- a/doc/src/sgml/ref/values.sgml +++ b/doc/src/sgml/ref/values.sgml @@ -1,5 +1,5 @@ @@ -23,7 +23,8 @@ PostgreSQL documentation VALUES ( expression [, ...] ) [, ...] [ ORDER BY sort_expression [ ASC | DESC | USING operator ] [, ...] ] [ LIMIT { count | ALL } ] - [ OFFSET start ] + [ OFFSET start [ ROW | ROWS ] ] + [ FETCH { FIRST | NEXT } [ count ] { ROW | ROWS } ONLY ] @@ -48,8 +49,10 @@ VALUES ( expression [, ...] ) [, .. Within larger commands, VALUES is syntactically allowed anywhere that SELECT is. Because it is treated like a - SELECT by the grammar, it is possible to use the ORDER - BY, LIMIT, and OFFSET clauses with a + SELECT by the grammar, it is possible to use + the ORDER BY, LIMIT (or + equivalently FETCH FIRST), + and OFFSET clauses with a VALUES command. @@ -227,9 +230,10 @@ WHERE ip_address IN (VALUES('192.168.0.1'::inet), ('192.168.0.10'), ('192.168.1. Compatibility - VALUES conforms to the SQL standard, except that + VALUES conforms to the SQL standard. LIMIT and OFFSET are - PostgreSQL extensions. + PostgreSQL extensions; see also + under . diff --git a/src/backend/catalog/sql_features.txt b/src/backend/catalog/sql_features.txt index 4c50a22a0a..b90856fafb 100644 --- a/src/backend/catalog/sql_features.txt +++ b/src/backend/catalog/sql_features.txt @@ -319,15 +319,15 @@ F851 in subqueries YES F852 Top-level in views YES F855 Nested in YES F856 Nested in YES -F857 Top-level in NO same as LIMIT -F858 in subqueries NO same as LIMIT -F859 Top-level in views NO same as LIMIT -F860 in NO same as LIMIT -F861 Top-level in NO same as OFFSET -F862 in subqueries NO same as OFFSET -F863 Nested in NO same as OFFSET -F864 Top-level in views NO same as OFFSET -F865 in NO same as OFFSET +F857 Top-level in YES +F858 in subqueries YES +F859 Top-level in views YES +F860 in YES +F861 Top-level in YES +F862 in subqueries YES +F863 Nested in YES +F864 Top-level in views YES +F865 in YES S011 Distinct data types NO S011 Distinct data types 01 USER_DEFINED_TYPES view NO S023 Basic structured types NO diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index aa5f33b411..3ab62b23cb 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -11,7 +11,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.627 2008/10/21 08:38:15 petere Exp $ + * $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.628 2008/10/22 11:00:34 petere Exp $ * * HISTORY * AUTHOR DATE MAJOR EVENT @@ -308,6 +308,8 @@ static TypeName *TableFuncTypeName(List *columns); %type reindex_type drop_type comment_type %type fetch_direction select_limit_value select_offset_value + select_offset_value2 opt_select_fetch_first_value +%type row_or_rows first_or_next %type OptSeqOptList SeqOptList %type SeqOptElem @@ -6579,6 +6581,13 @@ select_limit: errhint("Use separate LIMIT and OFFSET clauses."), scanner_errposition(@1))); } + /* SQL:2008 syntax variants */ + | OFFSET select_offset_value2 row_or_rows + { $$ = list_make2($2, NULL); } + | FETCH first_or_next opt_select_fetch_first_value row_or_rows ONLY + { $$ = list_make2(NULL, $3); } + | OFFSET select_offset_value2 row_or_rows FETCH first_or_next opt_select_fetch_first_value row_or_rows ONLY + { $$ = list_make2($2, $6); } ; opt_select_limit: @@ -6596,10 +6605,40 @@ select_limit_value: } ; +/* + * Allowing full expressions without parentheses causes various parsing + * problems with the trailing ROW/ROWS key words. SQL only calls for + * constants, so we allow the rest only with parentheses. + */ +opt_select_fetch_first_value: + SignedIconst { $$ = makeIntConst($1, @1); } + | '(' a_expr ')' { $$ = $2; } + | /*EMPTY*/ { $$ = makeIntConst(1, -1); } + ; + select_offset_value: a_expr { $$ = $1; } ; +/* + * Again, the trailing ROW/ROWS in this case prevent the full expression + * syntax. c_expr is the best we can do. + */ +select_offset_value2: + c_expr { $$ = $1; } + ; + +/* noise words */ +row_or_rows: + ROW { $$ = 0; } + | ROWS { $$ = 0; } + +/* noise words */ +first_or_next: + FIRST_P { $$ = 0; } + | NEXT { $$ = 0; } + + group_clause: GROUP_P BY expr_list { $$ = $3; } | /*EMPTY*/ { $$ = NIL; } @@ -9218,6 +9257,7 @@ Sconst: SCONST { $$ = $1; }; RoleId: ColId { $$ = $1; }; SignedIconst: ICONST { $$ = $1; } + | '+' ICONST { $$ = + $2; } | '-' ICONST { $$ = - $2; } ; @@ -9351,7 +9391,6 @@ unreserved_keyword: | EXPLAIN | EXTERNAL | FAMILY - | FETCH | FIRST_P | FORCE | FORWARD @@ -9641,6 +9680,7 @@ reserved_keyword: | END_P | EXCEPT | FALSE_P + | FETCH | FOR | FOREIGN | FROM diff --git a/src/backend/parser/keywords.c b/src/backend/parser/keywords.c index 7c6aa71572..608e80e0f5 100644 --- a/src/backend/parser/keywords.c +++ b/src/backend/parser/keywords.c @@ -11,7 +11,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/parser/keywords.c,v 1.203 2008/10/21 08:38:15 petere Exp $ + * $PostgreSQL: pgsql/src/backend/parser/keywords.c,v 1.204 2008/10/22 11:00:34 petere Exp $ * *------------------------------------------------------------------------- */ @@ -166,7 +166,7 @@ const ScanKeyword ScanKeywords[] = { {"extract", EXTRACT, COL_NAME_KEYWORD}, {"false", FALSE_P, RESERVED_KEYWORD}, {"family", FAMILY, UNRESERVED_KEYWORD}, - {"fetch", FETCH, UNRESERVED_KEYWORD}, + {"fetch", FETCH, RESERVED_KEYWORD}, {"first", FIRST_P, UNRESERVED_KEYWORD}, {"float", FLOAT_P, COL_NAME_KEYWORD}, {"for", FOR, RESERVED_KEYWORD},