diff --git a/doc/src/sgml/ref/select.sgml b/doc/src/sgml/ref/select.sgml index 9675adcbf8..d67d2b4ff7 100644 --- a/doc/src/sgml/ref/select.sgml +++ b/doc/src/sgml/ref/select.sgml @@ -1,5 +1,5 @@ @@ -1005,14 +1005,15 @@ OFFSET start SQL:2008 introduced a different syntax to achieve the same thing, - which PostgreSQL also supports. It is: + 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 + According to the standard, the OFFSET clause must come + before the FETCH clause if both are present; but + PostgreSQL is laxer and allows either order. + ROW and ROWS as well as FIRST and NEXT 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 6e52d3bcbd..ebf5b5d645 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 opt_column event cursor_options opt_hold opt_set_data %type reindex_type drop_type comment_type -%type fetch_direction select_limit_value select_offset_value +%type fetch_direction limit_clause select_limit_value + offset_clause select_offset_value select_offset_value2 opt_select_fetch_first_value %type 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; }