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';
+
+ LIMIT and OFFSET
+
+
+ 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},