diff --git a/doc/src/sgml/ref/select.sgml b/doc/src/sgml/ref/select.sgml
index 9675adcbf86252632224a4a40a0bcd31aaa17e46..d67d2b4ff764036e3ee8179de7aba6bff0c10cee 100644
--- a/doc/src/sgml/ref/select.sgml
+++ b/doc/src/sgml/ref/select.sgml
@@ -1,5 +1,5 @@
 <!--
-$PostgreSQL: pgsql/doc/src/sgml/ref/select.sgml,v 1.122 2009/05/03 20:45:43 tgl Exp $
+$PostgreSQL: pgsql/doc/src/sgml/ref/select.sgml,v 1.123 2009/08/18 23:40:20 tgl Exp $
 PostgreSQL documentation
 -->
 
@@ -1005,14 +1005,15 @@ OFFSET <replaceable class="parameter">start</replaceable>
 
    <para>
     SQL:2008 introduced a different syntax to achieve the same thing,
-    which PostgreSQL also supports.  It is:
+    which <productname>PostgreSQL</> also supports.  It is:
 <synopsis>
 OFFSET <replaceable class="parameter">start</replaceable> { ROW | ROWS }
 FETCH { FIRST | NEXT } [ <replaceable class="parameter">count</replaceable> ] { ROW | ROWS } ONLY
 </synopsis>
-    Both clauses are optional, but if present
-    the <literal>OFFSET</literal> clause must come before
-    the <literal>FETCH</literal> clause.  <literal>ROW</literal>
+    According to the standard, the <literal>OFFSET</literal> clause must come
+    before the <literal>FETCH</literal> clause if both are present; but
+    <productname>PostgreSQL</> is laxer and allows either order.
+    <literal>ROW</literal>
     and <literal>ROWS</literal> as well as <literal>FIRST</literal>
     and <literal>NEXT</literal> are noise words that don't influence
     the effects of these clauses.  In this syntax, when using expressions
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 6e52d3bcbd37ac42ef2ace2b04bd11bc71aabe00..ebf5b5d6455c2f16d5698039704fdf583d5db969 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.676 2009/08/02 22:14:52 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.677 2009/08/18 23:40:20 tgl Exp $
  *
  * HISTORY
  *	  AUTHOR			DATE			MAJOR EVENT
@@ -331,7 +331,8 @@ static TypeName *TableFuncTypeName(List *columns);
 %type <ival>	opt_column event cursor_options opt_hold opt_set_data
 %type <objtype>	reindex_type drop_type comment_type
 
-%type <node>	fetch_direction select_limit_value select_offset_value
+%type <node>	fetch_direction limit_clause select_limit_value
+				offset_clause select_offset_value
 				select_offset_value2 opt_select_fetch_first_value
 %type <ival>	row_or_rows first_or_next
 
@@ -7228,14 +7229,20 @@ sortby:		a_expr USING qual_all_Op opt_nulls_order
 
 
 select_limit:
-			LIMIT select_limit_value OFFSET select_offset_value
-				{ $$ = list_make2($4, $2); }
-			| OFFSET select_offset_value LIMIT select_limit_value
-				{ $$ = list_make2($2, $4); }
-			| LIMIT select_limit_value
-				{ $$ = list_make2(NULL, $2); }
-			| OFFSET select_offset_value
-				{ $$ = list_make2($2, NULL); }
+			limit_clause offset_clause			{ $$ = list_make2($2, $1); }
+			| offset_clause limit_clause		{ $$ = list_make2($1, $2); }
+			| limit_clause						{ $$ = list_make2(NULL, $1); }
+			| offset_clause						{ $$ = list_make2($1, NULL); }
+		;
+
+opt_select_limit:
+			select_limit						{ $$ = $1; }
+			| /* EMPTY */						{ $$ = list_make2(NULL,NULL); }
+		;
+
+limit_clause:
+			LIMIT select_limit_value
+				{ $$ = $2; }
 			| LIMIT select_limit_value ',' select_offset_value
 				{
 					/* Disabled because it was too confusing, bjm 2002-02-18 */
@@ -7245,19 +7252,17 @@ select_limit:
 							 errhint("Use separate LIMIT and OFFSET clauses."),
 							 parser_errposition(@1)));
 				}
-			/* SQL:2008 syntax variants */
-			| OFFSET select_offset_value2 row_or_rows
-				{ $$ = list_make2($2, NULL); }
+			/* SQL:2008 syntax */
 			| 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); }
+				{ $$ = $3; }
 		;
 
-opt_select_limit:
-			select_limit							{ $$ = $1; }
-			| /* EMPTY */
-					{ $$ = list_make2(NULL,NULL); }
+offset_clause:
+			OFFSET select_offset_value
+				{ $$ = $2; }
+			/* SQL:2008 syntax */
+			| OFFSET select_offset_value2 row_or_rows
+				{ $$ = $2; }
 		;
 
 select_limit_value:
@@ -7269,19 +7274,20 @@ select_limit_value:
 				}
 		;
 
+select_offset_value:
+			a_expr									{ $$ = $1; }
+		;
+
 /*
  * 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.
+ * constants, so we allow the rest only with parentheses.  If omitted,
+ * default to 1.
  */
 opt_select_fetch_first_value:
-			SignedIconst		{ $$ = makeIntConst($1, @1); }
-			| '(' a_expr ')'	{ $$ = $2; }
-			| /*EMPTY*/		{ $$ = makeIntConst(1, -1); }
-		;
-
-select_offset_value:
-			a_expr									{ $$ = $1; }
+			SignedIconst						{ $$ = makeIntConst($1, @1); }
+			| '(' a_expr ')'					{ $$ = $2; }
+			| /*EMPTY*/							{ $$ = makeIntConst(1, -1); }
 		;
 
 /*
@@ -7293,16 +7299,14 @@ select_offset_value2:
 		;
 
 /* noise words */
-row_or_rows:
-			ROW		{ $$ = 0; }
-			| ROWS		{ $$ = 0; }
-			;
+row_or_rows: ROW									{ $$ = 0; }
+			| ROWS									{ $$ = 0; }
+		;
+
+first_or_next: FIRST_P								{ $$ = 0; }
+			| NEXT									{ $$ = 0; }
+		;
 
-/* noise words */
-first_or_next:
-			FIRST_P		{ $$ = 0; }
-			| NEXT		{ $$ = 0; }
-			;
 
 group_clause:
 			GROUP_P BY expr_list					{ $$ = $3; }