SQL:2008 alternative syntax for LIMIT/OFFSET:
authorPeter Eisentraut
Wed, 22 Oct 2008 11:00:34 +0000 (11:00 +0000)
committerPeter Eisentraut
Wed, 22 Oct 2008 11:00:34 +0000 (11:00 +0000)
OFFSET num {ROW|ROWS} FETCH {FIRST|NEXT} [num] {ROW|ROWS} ONLY

doc/src/sgml/ref/select.sgml
doc/src/sgml/ref/select_into.sgml
doc/src/sgml/ref/values.sgml
src/backend/catalog/sql_features.txt
src/backend/parser/gram.y
src/backend/parser/keywords.c

index f73ca6ed64aa87eb5dd6ae8a30be56abc839843a..91a756b3ae25531d1155b8fe123c3933c5e135bc 100644 (file)
@@ -1,5 +1,5 @@
 
 
@@ -30,7 +30,8 @@ SELECT [ ALL | DISTINCT [ ON ( expression
     [ { UNION | INTERSECT | EXCEPT } [ ALL ] select ]
     [ 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 FIRSTor OFFSET
       clause is specified, the SELECT statement
       only returns a subset of the result rows. (See 
       linkend="sql-limit" endterm="sql-limit-title"> 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.
    
   
  
index de9a86a878c87f66dc3aeabd132b8ad04d291e39..cbe46cf763e872dd91217f67cfb93c56c5bb3f72 100644 (file)
@@ -1,5 +1,5 @@
 
 
@@ -31,7 +31,8 @@ SELECT [ ALL | DISTINCT [ ON ( expression
     [ { UNION | INTERSECT | EXCEPT } [ ALL ] select ]
     [ 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 ] [...] ]
 
  
index 23b84d782035dd4fdf3457b58393fc693c3f8b55..ef605f818f020efe9c59973b6670532873c5452f 100644 (file)
@@ -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 .
   
  
 
index 4c50a22a0afa9ba94fcc98f8a683a045e601b692..b90856fafbaff6d7636d3a5c9da8827b3f54ff43 100644 (file)
@@ -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  
index aa5f33b41147f22026a5b93cbd3c62a6854432f3..3ab62b23cb27a40070a4fc1bed1782a3cbc0b03b 100644 (file)
@@ -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
index 7c6aa71572a99292c54b3e67ef7b5954425b3d84..608e80e0f5dc7ac7d46cfef959d8026070d4591c 100644 (file)
@@ -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},