
/*****************************************************************************
 *
 *		QUERY:
 *				SELECT STATEMENTS
 *
 *****************************************************************************/

/* A complete SELECT statement looks like this.
 *
 * The rule returns either a single PGSelectStmt node or a tree of them,
 * representing a set-operation tree.
 *
 * There is an ambiguity when a sub-SELECT is within an a_expr and there
 * are excess parentheses: do the parentheses belong to the sub-SELECT or
 * to the surrounding a_expr?  We don't really care, but bison wants to know.
 * To resolve the ambiguity, we are careful to define the grammar so that
 * the decision is staved off as long as possible: as long as we can keep
 * absorbing parentheses into the sub-SELECT, we will do so, and only when
 * it's no longer possible to do that will we decide that parens belong to
 * the expression.	For example, in "SELECT (((SELECT 2)) + 3)" the extra
 * parentheses are treated as part of the sub-select.  The necessity of doing
 * it that way is shown by "SELECT (((SELECT 2)) UNION SELECT 2)".	Had we
 * parsed "((SELECT 2))" as an a_expr, it'd be too late to go back to the
 * SELECT viewpoint when we see the UNION.
 *
 * This approach is implemented by defining a nonterminal select_with_parens,
 * which represents a SELECT with at least one outer layer of parentheses,
 * and being careful to use select_with_parens, never '(' PGSelectStmt ')',
 * in the expression grammar.  We will then have shift-reduce conflicts
 * which we can resolve in favor of always treating '(' <select> ')' as
 * a select_with_parens.  To resolve the conflicts, the productions that
 * conflict with the select_with_parens productions are manually given
 * precedences lower than the precedence of ')', thereby ensuring that we
 * shift ')' (and then reduce to select_with_parens) rather than trying to
 * reduce the inner <select> nonterminal to something else.  We use UMINUS
 * precedence for this, which is a fairly arbitrary choice.
 *
 * To be able to define select_with_parens itself without ambiguity, we need
 * a nonterminal select_no_parens that represents a SELECT structure with no
 * outermost parentheses.  This is a little bit tedious, but it works.
 *
 * In non-expression contexts, we use PGSelectStmt which can represent a SELECT
 * with or without outer parentheses.
 */

SelectStmt: select_no_parens			%prec UMINUS
			| select_with_parens		%prec UMINUS
		;

select_with_parens:
			'(' select_no_parens ')'				{ $$ = $2; }
			| '(' select_with_parens ')'			{ $$ = $2; }
			| '(' VariableShowStmt ')'
		    {
		    	$$ = $2;
			}
		;

/*
 * This rule parses the equivalent of the standard's <query expression>.
 * The duplicative productions are annoying, but hard to get rid of without
 * creating shift/reduce conflicts.
 *
 *	The locking clause (FOR UPDATE etc) may be before or after LIMIT/OFFSET.
 *	In <=7.2.X, LIMIT/OFFSET had to be after FOR UPDATE
 *	We now support both orderings, but prefer LIMIT/OFFSET before the locking
 * clause.
 *	2002-08-28 bjm
 */
select_no_parens:
			simple_select						{ $$ = $1; }
			| select_clause sort_clause
				{
					insertSelectOptions((PGSelectStmt *) $1, $2, NIL,
										NULL, NULL, NULL,
										yyscanner);
					$$ = $1;
				}
			| select_clause opt_sort_clause for_locking_clause opt_select_limit
				{
					insertSelectOptions((PGSelectStmt *) $1, $2, $3,
										(PGNode*) list_nth($4, 0), (PGNode*) list_nth($4, 1),
										NULL,
										yyscanner);
					$$ = $1;
				}
			| select_clause opt_sort_clause select_limit opt_for_locking_clause
				{
					insertSelectOptions((PGSelectStmt *) $1, $2, $4,
										(PGNode*) list_nth($3, 0), (PGNode*) list_nth($3, 1),
										NULL,
										yyscanner);
					$$ = $1;
				}
			| with_clause select_clause
				{
					insertSelectOptions((PGSelectStmt *) $2, NULL, NIL,
										NULL, NULL,
										$1,
										yyscanner);
					$$ = $2;
				}
			| with_clause select_clause sort_clause
				{
					insertSelectOptions((PGSelectStmt *) $2, $3, NIL,
										NULL, NULL,
										$1,
										yyscanner);
					$$ = $2;
				}
			| with_clause select_clause opt_sort_clause for_locking_clause opt_select_limit
				{
					insertSelectOptions((PGSelectStmt *) $2, $3, $4,
										(PGNode*) list_nth($5, 0), (PGNode*) list_nth($5, 1),
										$1,
										yyscanner);
					$$ = $2;
				}
			| with_clause select_clause opt_sort_clause select_limit opt_for_locking_clause
				{
					insertSelectOptions((PGSelectStmt *) $2, $3, $5,
										(PGNode*) list_nth($4, 0), (PGNode*) list_nth($4, 1),
										$1,
										yyscanner);
					$$ = $2;
				}
		;

select_clause:
			simple_select							{ $$ = $1; }
			| select_with_parens					{ $$ = $1; }
		;

/*
 * This rule parses SELECT statements that can appear within set operations,
 * including UNION, INTERSECT and EXCEPT.  '(' and ')' can be used to specify
 * the ordering of the set operations.	Without '(' and ')' we want the
 * operations to be ordered per the precedence specs at the head of this file.
 *
 * As with select_no_parens, simple_select cannot have outer parentheses,
 * but can have parenthesized subclauses.
 *
 * Note that sort clauses cannot be included at this level --- SQL requires
 *		SELECT foo UNION SELECT bar ORDER BY baz
 * to be parsed as
 *		(SELECT foo UNION SELECT bar) ORDER BY baz
 * not
 *		SELECT foo UNION (SELECT bar ORDER BY baz)
 * Likewise for WITH, FOR UPDATE and LIMIT.  Therefore, those clauses are
 * described as part of the select_no_parens production, not simple_select.
 * This does not limit functionality, because you can reintroduce these
 * clauses inside parentheses.
 *
 * NOTE: only the leftmost component PGSelectStmt should have INTO.
 * However, this is not checked by the grammar; parse analysis must check it.
 */
opt_select:
		SELECT opt_all_clause opt_target_list_opt_comma
			{
				$$ = $3;
			}
		| /* empty */
			{
				PGAStar *star = makeNode(PGAStar);
				$$ = list_make1(star);
			}
	;


simple_select:
			SELECT opt_all_clause opt_target_list_opt_comma
			into_clause from_clause where_clause
			group_clause having_clause window_clause qualify_clause sample_clause
				{
					PGSelectStmt *n = makeNode(PGSelectStmt);
					n->targetList = $3;
					n->intoClause = $4;
					n->fromClause = $5;
					n->whereClause = $6;
					n->groupClause = $7;
					n->havingClause = $8;
					n->windowClause = $9;
					n->qualifyClause = $10;
					n->sampleOptions = $11;
					$$ = (PGNode *)n;
				}
			| SELECT distinct_clause target_list_opt_comma
			into_clause from_clause where_clause
			group_clause having_clause window_clause qualify_clause sample_clause
				{
					PGSelectStmt *n = makeNode(PGSelectStmt);
					n->distinctClause = $2;
					n->targetList = $3;
					n->intoClause = $4;
					n->fromClause = $5;
					n->whereClause = $6;
					n->groupClause = $7;
					n->havingClause = $8;
					n->windowClause = $9;
					n->qualifyClause = $10;
					n->sampleOptions = $11;
					$$ = (PGNode *)n;
				}
			|  FROM from_list opt_select
			into_clause where_clause
			group_clause having_clause window_clause qualify_clause sample_clause
				{
					PGSelectStmt *n = makeNode(PGSelectStmt);
					n->targetList = $3;
					n->fromClause = $2;
					n->intoClause = $4;
					n->whereClause = $5;
					n->groupClause = $6;
					n->havingClause = $7;
					n->windowClause = $8;
					n->qualifyClause = $9;
					n->sampleOptions = $10;
					n->from_first = true;
					$$ = (PGNode *)n;
				}
			|
			FROM from_list SELECT distinct_clause target_list_opt_comma
			into_clause where_clause
			group_clause having_clause window_clause qualify_clause sample_clause
				{
					PGSelectStmt *n = makeNode(PGSelectStmt);
					n->targetList = $5;
					n->distinctClause = $4;
					n->fromClause = $2;
					n->intoClause = $6;
					n->whereClause = $7;
					n->groupClause = $8;
					n->havingClause = $9;
					n->windowClause = $10;
					n->qualifyClause = $11;
					n->sampleOptions = $12;
					n->from_first = true;
					$$ = (PGNode *)n;
				}
			| values_clause_opt_comma							{ $$ = $1; }
			| TABLE relation_expr
				{
					/* same as SELECT * FROM relation_expr */
					PGColumnRef *cr = makeNode(PGColumnRef);
					PGResTarget *rt = makeNode(PGResTarget);
					PGSelectStmt *n = makeNode(PGSelectStmt);

					cr->fields = list_make1(makeNode(PGAStar));
					cr->location = -1;

					rt->name = NULL;
					rt->indirection = NIL;
					rt->val = (PGNode *)cr;
					rt->location = -1;

					n->targetList = list_make1(rt);
					n->fromClause = list_make1($2);
					$$ = (PGNode *)n;
				}
            | select_clause UNION all_or_distinct by_name select_clause
				{
					$$ = makeSetOp(PG_SETOP_UNION_BY_NAME, $3, $1, $5);
				}
			| select_clause UNION all_or_distinct select_clause
				{
					$$ = makeSetOp(PG_SETOP_UNION, $3, $1, $4);
				}
			| select_clause INTERSECT all_or_distinct select_clause
				{
					$$ = makeSetOp(PG_SETOP_INTERSECT, $3, $1, $4);
				}
			| select_clause EXCEPT all_or_distinct select_clause
				{
					$$ = makeSetOp(PG_SETOP_EXCEPT, $3, $1, $4);
				}
			| pivot_keyword table_ref USING target_list_opt_comma
				{
					PGSelectStmt *res = makeNode(PGSelectStmt);
					PGPivotStmt *n = makeNode(PGPivotStmt);
					n->source = $2;
					n->aggrs = $4;
					n->location = @1;
					res->pivot = n;
					$$ = (PGNode *)res;
				}
			| pivot_keyword table_ref USING target_list_opt_comma GROUP_P BY name_list_opt_comma_opt_bracket
				{
					PGSelectStmt *res = makeNode(PGSelectStmt);
					PGPivotStmt *n = makeNode(PGPivotStmt);
					n->source = $2;
					n->aggrs = $4;
					n->groups = $7;
					n->location = @1;
					res->pivot = n;
					$$ = (PGNode *)res;
				}
			| pivot_keyword table_ref GROUP_P BY name_list_opt_comma_opt_bracket
				{
					PGSelectStmt *res = makeNode(PGSelectStmt);
					PGPivotStmt *n = makeNode(PGPivotStmt);
					n->source = $2;
					n->groups = $5;
					n->location = @1;
					res->pivot = n;
					$$ = (PGNode *)res;
				}
			| pivot_keyword table_ref ON pivot_column_list
				{
					PGSelectStmt *res = makeNode(PGSelectStmt);
					PGPivotStmt *n = makeNode(PGPivotStmt);
					n->source = $2;
					n->columns = $4;
					res->pivot = n;
					$$ = (PGNode *)res;
				}
			| pivot_keyword table_ref ON pivot_column_list GROUP_P BY name_list_opt_comma_opt_bracket
				{
					PGSelectStmt *res = makeNode(PGSelectStmt);
					PGPivotStmt *n = makeNode(PGPivotStmt);
					n->source = $2;
					n->columns = $4;
					n->groups = $7;
					n->location = @1;
					res->pivot = n;
					$$ = (PGNode *)res;
				}
			| pivot_keyword table_ref ON pivot_column_list USING target_list_opt_comma
				{
					PGSelectStmt *res = makeNode(PGSelectStmt);
					PGPivotStmt *n = makeNode(PGPivotStmt);
					n->source = $2;
					n->columns = $4;
					n->aggrs = $6;
					n->location = @1;
					res->pivot = n;
					$$ = (PGNode *)res;
				}
			| pivot_keyword table_ref ON pivot_column_list USING target_list_opt_comma GROUP_P BY name_list_opt_comma_opt_bracket
				{
					PGSelectStmt *res = makeNode(PGSelectStmt);
					PGPivotStmt *n = makeNode(PGPivotStmt);
					n->source = $2;
					n->columns = $4;
					n->aggrs = $6;
					n->groups = $9;
					n->location = @1;
					res->pivot = n;
					$$ = (PGNode *)res;
				}
			| unpivot_keyword table_ref ON target_list_opt_comma INTO NAME_P name value_or_values name_list_opt_comma_opt_bracket
				{
					PGSelectStmt *res = makeNode(PGSelectStmt);
					PGPivotStmt *n = makeNode(PGPivotStmt);
					n->source = $2;
					n->unpivots = $9;
					n->location = @1;
					PGPivot *piv = makeNode(PGPivot);
					piv->unpivot_columns = list_make1(makeString($7));
					piv->pivot_value = $4;
					n->columns = list_make1(piv);

					res->pivot = n;
					$$ = (PGNode *)res;
				}
			| unpivot_keyword table_ref ON target_list_opt_comma
				{
					PGSelectStmt *res = makeNode(PGSelectStmt);
					PGPivotStmt *n = makeNode(PGPivotStmt);
					n->source = $2;
					n->unpivots = list_make1(makeString("value"));
					n->location = @1;
					PGPivot *piv = makeNode(PGPivot);
					piv->unpivot_columns = list_make1(makeString("name"));
					piv->pivot_value = $4;
					n->columns = list_make1(piv);

					res->pivot = n;
					$$ = (PGNode *)res;
				}
		;

value_or_values:
		VALUE_P | VALUES
	;

pivot_keyword:
		PIVOT | PIVOT_WIDER
	;

unpivot_keyword:
		UNPIVOT | PIVOT_LONGER
	;

pivot_column_entry:
			b_expr
			{
				PGPivot *n = makeNode(PGPivot);
				n->pivot_columns = list_make1($1);
				$$ = (PGNode *) n;
			}
			| b_expr IN_P '(' select_no_parens ')'
			{
				PGPivot *n = makeNode(PGPivot);
				n->pivot_columns = list_make1($1);
				n->subquery = $4;
				$$ = (PGNode *) n;
			}
			| single_pivot_value													{ $$ = $1; }
		;

pivot_column_list_internal:
			pivot_column_entry												{ $$ = list_make1($1); }
			| pivot_column_list_internal ',' pivot_column_entry 			{ $$ = lappend($1, $3); }
		;

pivot_column_list:
			pivot_column_list_internal										{ $$ = $1; }
			| pivot_column_list_internal ','								{ $$ = $1; }
		;

/*
 * SQL standard WITH clause looks like:
 *
 * WITH [ RECURSIVE ] <query name> [ (<column>,...) ]
 *		AS (query) [ SEARCH or CYCLE clause ]
 *
 * We don't currently support the SEARCH or CYCLE clause.
 *
 * Recognizing WITH_LA here allows a CTE to be named TIME or ORDINALITY.
 */
with_clause:
		WITH cte_list
			{
				$$ = makeNode(PGWithClause);
				$$->ctes = $2;
				$$->recursive = false;
				$$->location = @1;
			}
		| WITH_LA cte_list
			{
				$$ = makeNode(PGWithClause);
				$$->ctes = $2;
				$$->recursive = false;
				$$->location = @1;
			}
		| WITH RECURSIVE cte_list
			{
				$$ = makeNode(PGWithClause);
				$$->ctes = $3;
				$$->recursive = true;
				$$->location = @1;
			}
		;

cte_list:
		common_table_expr						{ $$ = list_make1($1); }
		| cte_list ',' common_table_expr		{ $$ = lappend($1, $3); }
		;

common_table_expr:  name opt_name_list AS opt_materialized '(' PreparableStmt ')'
			{
				PGCommonTableExpr *n = makeNode(PGCommonTableExpr);
				n->ctename = $1;
				n->aliascolnames = $2;
				n->ctematerialized = $4;
				n->ctequery = $6;
				n->location = @1;
				$$ = (PGNode *) n;
			}
		;

opt_materialized:
		MATERIALIZED							{ $$ = PGCTEMaterializeAlways; }
		| NOT MATERIALIZED						{ $$ = PGCTEMaterializeNever; }
		| /*EMPTY*/								{ $$ = PGCTEMaterializeDefault; }
		;

into_clause:
			INTO OptTempTableName
				{
					$$ = makeNode(PGIntoClause);
					$$->rel = $2;
					$$->colNames = NIL;
					$$->options = NIL;
					$$->onCommit = PG_ONCOMMIT_NOOP;
					$$->viewQuery = NULL;
					$$->skipData = false;
				}
			| /*EMPTY*/
				{ $$ = NULL; }
		;

/*
 * Redundancy here is needed to avoid shift/reduce conflicts,
 * since TEMP is not a reserved word.  See also OptTemp.
 */
OptTempTableName:
			TEMPORARY opt_table qualified_name
				{
					$$ = $3;
					$$->relpersistence = PG_RELPERSISTENCE_TEMP;
				}
			| TEMP opt_table qualified_name
				{
					$$ = $3;
					$$->relpersistence = PG_RELPERSISTENCE_TEMP;
				}
			| LOCAL TEMPORARY opt_table qualified_name
				{
					$$ = $4;
					$$->relpersistence = PG_RELPERSISTENCE_TEMP;
				}
			| LOCAL TEMP opt_table qualified_name
				{
					$$ = $4;
					$$->relpersistence = PG_RELPERSISTENCE_TEMP;
				}
			| GLOBAL TEMPORARY opt_table qualified_name
				{
					ereport(PGWARNING,
							(errmsg("GLOBAL is deprecated in temporary table creation"),
							 parser_errposition(@1)));
					$$ = $4;
					$$->relpersistence = PG_RELPERSISTENCE_TEMP;
				}
			| GLOBAL TEMP opt_table qualified_name
				{
					ereport(PGWARNING,
							(errmsg("GLOBAL is deprecated in temporary table creation"),
							 parser_errposition(@1)));
					$$ = $4;
					$$->relpersistence = PG_RELPERSISTENCE_TEMP;
				}
			| UNLOGGED opt_table qualified_name
				{
					$$ = $3;
					$$->relpersistence = PG_RELPERSISTENCE_UNLOGGED;
				}
			| TABLE qualified_name
				{
					$$ = $2;
					$$->relpersistence = RELPERSISTENCE_PERMANENT;
				}
			| qualified_name
				{
					$$ = $1;
					$$->relpersistence = RELPERSISTENCE_PERMANENT;
				}
		;

opt_table:	TABLE									{}
			| /*EMPTY*/								{}
		;

all_or_distinct:
			ALL										{ $$ = true; }
			| DISTINCT								{ $$ = false; }
			| /*EMPTY*/								{ $$ = false; }
		;

by_name:
            BY NAME_P                                     { }
        ;

/* We use (NIL) as a placeholder to indicate that all target expressions
 * should be placed in the DISTINCT list during parsetree analysis.
 */
distinct_clause:
			DISTINCT								{ $$ = list_make1(NIL); }
			| DISTINCT ON '(' expr_list_opt_comma ')'			{ $$ = $4; }
		;

opt_all_clause:
			ALL										{ $$ = NIL;}
			| /*EMPTY*/								{ $$ = NIL; }
		;

opt_ignore_nulls:
			IGNORE_P NULLS_P						{ $$ = PG_IGNORE_NULLS;}
			| RESPECT_P NULLS_P						{ $$ = PG_RESPECT_NULLS;}
			| /*EMPTY*/								{ $$ = PG_DEFAULT_NULLS; }
		;

opt_sort_clause:
			sort_clause								{ $$ = $1;}
			| /*EMPTY*/								{ $$ = NIL; }
		;

sort_clause:
			ORDER BY sortby_list					{ $$ = $3; }
			| ORDER BY ALL opt_asc_desc opt_nulls_order
				{
					PGSortBy *sort = makeNode(PGSortBy);
					PGAStar *star = makeNode(PGAStar);
					star->columns = true;
					star->location = @3;
					sort->node = (PGNode *) star;
					sort->sortby_dir = $4;
					sort->sortby_nulls = $5;
					sort->useOp = NIL;
					sort->location = -1;		/* no operator */
					$$ = list_make1(sort);
				}
		;

sortby_list:
			sortby									{ $$ = list_make1($1); }
			| sortby_list ',' sortby				{ $$ = lappend($1, $3); }
		;

sortby:		a_expr USING qual_all_Op opt_nulls_order
				{
					$$ = makeNode(PGSortBy);
					$$->node = $1;
					$$->sortby_dir = SORTBY_USING;
					$$->sortby_nulls = $4;
					$$->useOp = $3;
					$$->location = @3;
				}
			| a_expr opt_asc_desc opt_nulls_order
				{
					$$ = makeNode(PGSortBy);
					$$->node = $1;
					$$->sortby_dir = $2;
					$$->sortby_nulls = $3;
					$$->useOp = NIL;
					$$->location = -1;		/* no operator */
				}
		;

opt_asc_desc: ASC_P							{ $$ = PG_SORTBY_ASC; }
			| DESC_P						{ $$ = PG_SORTBY_DESC; }
			| /*EMPTY*/						{ $$ = PG_SORTBY_DEFAULT; }
		;

opt_nulls_order: NULLS_LA FIRST_P			{ $$ = PG_SORTBY_NULLS_FIRST; }
			| NULLS_LA LAST_P				{ $$ = PG_SORTBY_NULLS_LAST; }
			| /*EMPTY*/						{ $$ = PG_SORTBY_NULLS_DEFAULT; }
		;

select_limit:
			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 */
					ereport(ERROR,
							(errcode(PG_ERRCODE_SYNTAX_ERROR),
							 errmsg("LIMIT #,# syntax is not supported"),
							 errhint("Use separate LIMIT and OFFSET clauses."),
							 parser_errposition(@1)));
				}
			/* SQL:2008 syntax */
			/* to avoid shift/reduce conflicts, handle the optional value with
			 * a separate production rather than an opt_ expression.  The fact
			 * that ONLY is fully reserved means that this way, we defer any
			 * decision about what rule reduces ROW or ROWS to the point where
			 * we can see the ONLY token in the lookahead slot.
			 */
			| FETCH first_or_next select_fetch_first_value row_or_rows ONLY
				{ $$ = $3; }
			| FETCH first_or_next row_or_rows ONLY
				{ $$ = makeIntConst(1, -1); }
		;

offset_clause:
			OFFSET select_offset_value
				{ $$ = $2; }
			/* SQL:2008 syntax */
			| OFFSET select_fetch_first_value row_or_rows
				{ $$ = $2; }
		;

sample_value:
    FCONST
        {
            $$ = makeFloatConst($1, @1);
        }
    | ICONST
        {
            $$ = makeIntConst($1, @1);
        }
    | param_expr
    ;

/*
 * SAMPLE clause
 */
sample_count:
	sample_value '%'
		{
			$$ = makeSampleSize($1, true);
		}
	| sample_value PERCENT
		{
			$$ = makeSampleSize($1, true);
		}
	| sample_value
		{
			$$ = makeSampleSize($1, false);
		}
	| sample_value ROWS
		{
			$$ = makeSampleSize($1, false);
		}
	;

sample_clause:
			USING SAMPLE tablesample_entry
				{
					$$ = $3;
				}
			| /* EMPTY */
				{ $$ = NULL; }
		;

/*
 * TABLESAMPLE decoration in a FROM item
 */
opt_sample_func:
			ColId					{ $$ = $1; }
			| /*EMPTY*/				{ $$ = NULL; }
		;

tablesample_entry:
	opt_sample_func '(' sample_count ')' opt_repeatable_clause
				{
					int seed = $5;
					$$ = makeSampleOptions($3, $1, &seed, @1);
				}
	| sample_count
		{
			$$ = makeSampleOptions($1, NULL, NULL, @1);
		}
	| sample_count '(' ColId ')'
		{
			$$ = makeSampleOptions($1, $3, NULL, @1);
		}
	| sample_count '(' ColId ',' ICONST ')'
		{
			int seed = $5;
			$$ = makeSampleOptions($1, $3, &seed, @1);
		}
	;

tablesample_clause:
			TABLESAMPLE tablesample_entry
				{
					$$ = $2;
				}
		;

opt_tablesample_clause:
			tablesample_clause			{ $$ = $1; }
			| /*EMPTY*/					{ $$ = NULL; }
		;


opt_repeatable_clause:
			REPEATABLE '(' ICONST ')'	{ $$ = $3; }
			| /*EMPTY*/					{ $$ = -1; }
		;

select_limit_value:
			a_expr									{ $$ = $1; }
			| ALL
				{
					/* LIMIT ALL is represented as a NULL constant */
					$$ = makeNullAConst(@1);
				}
			| a_expr '%'
				{ $$ = makeLimitPercent($1); }
			| FCONST PERCENT
				{ $$ = makeLimitPercent(makeFloatConst($1,@1)); }
			| ICONST PERCENT
				{ $$ = makeLimitPercent(makeIntConst($1,@1)); }
		;

select_offset_value:
			a_expr									{ $$ = $1; }
		;

/*
 * Allowing full expressions without parentheses causes various parsing
 * problems with the trailing ROW/ROWS key words.  SQL spec only calls for
 * <simple value specification>, which is either a literal or a parameter (but
 * an <SQL parameter reference> could be an identifier, bringing up conflicts
 * with ROW/ROWS). We solve this by leveraging the presence of ONLY (see above)
 * to determine whether the expression is missing rather than trying to make it
 * optional in this rule.
 *
 * c_expr covers almost all the spec-required cases (and more), but it doesn't
 * cover signed numeric literals, which are allowed by the spec. So we include
 * those here explicitly. We need FCONST as well as ICONST because values that
 * don't fit in the platform's "long", but do fit in bigint, should still be
 * accepted here. (This is possible in 64-bit Windows as well as all 32-bit
 * builds.)
 */
select_fetch_first_value:
			c_expr									{ $$ = $1; }
			| '+' I_or_F_const
				{ $$ = (PGNode *) makeSimpleAExpr(PG_AEXPR_OP, "+", NULL, $2, @1); }
			| '-' I_or_F_const
				{ $$ = doNegate($2, @1); }
		;

I_or_F_const:
			Iconst									{ $$ = makeIntConst($1,@1); }
			| FCONST								{ $$ = makeFloatConst($1,@1); }
		;

/* noise words */
row_or_rows: ROW									{ $$ = 0; }
			| ROWS									{ $$ = 0; }
		;

first_or_next: FIRST_P								{ $$ = 0; }
			| NEXT									{ $$ = 0; }
		;


/*
 * This syntax for group_clause tries to follow the spec quite closely.
 * However, the spec allows only column references, not expressions,
 * which introduces an ambiguity between implicit row constructors
 * (a,b) and lists of column references.
 *
 * We handle this by using the a_expr production for what the spec calls
 * <ordinary grouping set>, which in the spec represents either one column
 * reference or a parenthesized list of column references. Then, we check the
 * top node of the a_expr to see if it's an implicit PGRowExpr, and if so, just
 * grab and use the list, discarding the node. (this is done in parse analysis,
 * not here)
 *
 * (we abuse the row_format field of PGRowExpr to distinguish implicit and
 * explicit row constructors; it's debatable if anyone sanely wants to use them
 * in a group clause, but if they have a reason to, we make it possible.)
 *
 * Each item in the group_clause list is either an expression tree or a
 * PGGroupingSet node of some type.
 */
group_clause:
			GROUP_P BY group_by_list_opt_comma				{ $$ = $3; }
			| GROUP_P BY ALL
				{
					PGNode *node = (PGNode *) makeGroupingSet(GROUPING_SET_ALL, NIL, @3);
					$$ = list_make1(node);
				}
			| /*EMPTY*/								{ $$ = NIL; }
		;

group_by_list:
			group_by_item							{ $$ = list_make1($1); }
			| group_by_list ',' group_by_item		{ $$ = lappend($1,$3); }
		;

group_by_list_opt_comma:
			group_by_list								{ $$ = $1; }
			| group_by_list ','							{ $$ = $1; }
		;

group_by_item:
			a_expr									{ $$ = $1; }
			| empty_grouping_set					{ $$ = $1; }
			| cube_clause							{ $$ = $1; }
			| rollup_clause							{ $$ = $1; }
			| grouping_sets_clause					{ $$ = $1; }
		;

empty_grouping_set:
			'(' ')'
				{
					$$ = (PGNode *) makeGroupingSet(GROUPING_SET_EMPTY, NIL, @1);
				}
		;

/*
 * These hacks rely on setting precedence of CUBE and ROLLUP below that of '(',
 * so that they shift in these rules rather than reducing the conflicting
 * unreserved_keyword rule.
 */

rollup_clause:
			ROLLUP '(' expr_list_opt_comma ')'
				{
					$$ = (PGNode *) makeGroupingSet(GROUPING_SET_ROLLUP, $3, @1);
				}
		;

cube_clause:
			CUBE '(' expr_list_opt_comma ')'
				{
					$$ = (PGNode *) makeGroupingSet(GROUPING_SET_CUBE, $3, @1);
				}
		;

grouping_sets_clause:
			GROUPING SETS '(' group_by_list_opt_comma ')'
				{
					$$ = (PGNode *) makeGroupingSet(GROUPING_SET_SETS, $4, @1);
				}
		;

grouping_or_grouping_id:
		GROUPING								{ $$ = NULL; }
		| GROUPING_ID							{ $$ = NULL; }
		;

having_clause:
			HAVING a_expr							{ $$ = $2; }
			| /*EMPTY*/								{ $$ = NULL; }
		;

qualify_clause:
			QUALIFY a_expr							{ $$ = $2; }
			| /*EMPTY*/								{ $$ = NULL; }
		;

for_locking_clause:
			for_locking_items						{ $$ = $1; }
			| FOR READ_P ONLY							{ $$ = NIL; }
		;

opt_for_locking_clause:
			for_locking_clause						{ $$ = $1; }
			| /* EMPTY */							{ $$ = NIL; }
		;

for_locking_items:
			for_locking_item						{ $$ = list_make1($1); }
			| for_locking_items for_locking_item	{ $$ = lappend($1, $2); }
		;

for_locking_item:
			for_locking_strength locked_rels_list opt_nowait_or_skip
				{
					PGLockingClause *n = makeNode(PGLockingClause);
					n->lockedRels = $2;
					n->strength = $1;
					n->waitPolicy = $3;
					$$ = (PGNode *) n;
				}
		;

for_locking_strength:
			FOR UPDATE 							{ $$ = LCS_FORUPDATE; }
			| FOR NO KEY UPDATE 				{ $$ = PG_LCS_FORNOKEYUPDATE; }
			| FOR SHARE 						{ $$ = PG_LCS_FORSHARE; }
			| FOR KEY SHARE 					{ $$ = PG_LCS_FORKEYSHARE; }
		;

locked_rels_list:
			OF qualified_name_list					{ $$ = $2; }
			| /* EMPTY */							{ $$ = NIL; }
		;


opt_nowait_or_skip:
			NOWAIT							{ $$ = LockWaitError; }
			| SKIP LOCKED					{ $$ = PGLockWaitSkip; }
			| /*EMPTY*/						{ $$ = PGLockWaitBlock; }
		;

/*
 * We should allow ROW '(' expr_list ')' too, but that seems to require
 * making VALUES a fully reserved word, which will probably break more apps
 * than allowing the noise-word is worth.
 */
values_clause:
			VALUES '(' expr_list_opt_comma ')'
				{
					PGSelectStmt *n = makeNode(PGSelectStmt);
					n->valuesLists = list_make1($3);
					$$ = (PGNode *) n;
				}
			| values_clause ',' '(' expr_list_opt_comma ')'
				{
					PGSelectStmt *n = (PGSelectStmt *) $1;
					n->valuesLists = lappend(n->valuesLists, $4);
					$$ = (PGNode *) n;
				}
		;

values_clause_opt_comma:
			values_clause				{ $$ = $1; }
			| values_clause ','			{ $$ = $1; }
		;


/*****************************************************************************
 *
 *	clauses common to all Optimizable Stmts:
 *		from_clause		- allow list of both JOIN expressions and table names
 *		where_clause	- qualifications for joins or restrictions
 *
 *****************************************************************************/

from_clause:
			FROM from_list_opt_comma							{ $$ = $2; }
			| /*EMPTY*/								{ $$ = NIL; }
		;

from_list:
			table_ref								{ $$ = list_make1($1); }
			| from_list ',' table_ref				{ $$ = lappend($1, $3); }
		;

from_list_opt_comma:
			from_list								{ $$ = $1; }
			| from_list ','							{ $$ = $1; }
		;

alias_prefix_colon_clause:
            ColIdOrString ':'
            {
                $$ = makeNode(PGAlias);
                $$->aliasname = $1;
            }
    ;


/*
 * table_ref is where an alias clause can be attached.
 */
table_ref:	relation_expr opt_alias_clause opt_tablesample_clause
				{
					$1->alias = $2;
					$1->sample = $3;
					$$ = (PGNode *) $1;
				}
			| alias_prefix_colon_clause relation_expr opt_tablesample_clause
                {
                    $2->alias = $1;
                    $2->sample = $3;
                    $$ = (PGNode *) $2;
                }
            | func_table func_alias_clause opt_tablesample_clause
				{
					PGRangeFunction *n = (PGRangeFunction *) $1;
					n->alias = (PGAlias*) linitial($2);
					n->coldeflist = (PGList*) lsecond($2);
					n->sample = $3;
					$$ = (PGNode *) n;
				}
		     | alias_prefix_colon_clause func_table opt_tablesample_clause
                    {
                        PGRangeFunction *n = (PGRangeFunction *) $2;
                        n->alias = $1;
                        n->sample = $3;
                        $$ = (PGNode *) n;
                    }
		    |
            values_clause_opt_comma alias_clause opt_tablesample_clause
                {
                    PGRangeSubselect *n = makeNode(PGRangeSubselect);
                    n->lateral = false;
                    n->subquery = $1;
                    n->alias = $2;
                    n->sample = $3;
                    $$ = (PGNode *) n;
                }

			| LATERAL_P func_table func_alias_clause
				{
					PGRangeFunction *n = (PGRangeFunction *) $2;
					n->lateral = true;
					n->alias = (PGAlias*) linitial($3);
					n->coldeflist = (PGList*) lsecond($3);
					$$ = (PGNode *) n;
				}
			| select_with_parens opt_alias_clause opt_tablesample_clause
				{
					PGRangeSubselect *n = makeNode(PGRangeSubselect);
					n->lateral = false;
					n->subquery = $1;
					n->alias = $2;
					n->sample = $3;
					$$ = (PGNode *) n;
				}
			| alias_prefix_colon_clause select_with_parens opt_tablesample_clause
                {
                    PGRangeSubselect *n = makeNode(PGRangeSubselect);
                    n->lateral = false;
                    n->subquery = $2;
                    n->alias = $1;
                    n->sample = $3;
                    $$ = (PGNode *) n;
                }
			| LATERAL_P select_with_parens opt_alias_clause
				{
					PGRangeSubselect *n = makeNode(PGRangeSubselect);
					n->lateral = true;
					n->subquery = $2;
					n->alias = $3;
					n->sample = NULL;
					$$ = (PGNode *) n;
				}
			| joined_table
				{
					$$ = (PGNode *) $1;
				}
			| '(' joined_table ')' alias_clause
				{
					$2->alias = $4;
					$$ = (PGNode *) $2;
				}
            | alias_prefix_colon_clause '(' joined_table ')'
                {
                    $3->alias = $1;
                    $$ = (PGNode *) $3;
                }
			| table_ref PIVOT '(' target_list_opt_comma FOR pivot_value_list opt_pivot_group_by ')' opt_alias_clause
				{
					PGPivotExpr *n = makeNode(PGPivotExpr);
					n->source = $1;
					n->aggrs = $4;
					n->pivots = $6;
					n->groups = $7;
					n->alias = $9;
					n->location = @2;
					$$ = (PGNode *) n;
				}
			| table_ref UNPIVOT opt_include_nulls '(' unpivot_header FOR unpivot_value_list ')' opt_alias_clause
				{
					PGPivotExpr *n = makeNode(PGPivotExpr);
					n->source = $1;
					n->include_nulls = $3;
					n->unpivots = $5;
					n->pivots = $7;
					n->alias = $9;
					n->location = @2;
					$$ = (PGNode *) n;
				}
		;

opt_pivot_group_by:
	GROUP_P BY name_list_opt_comma		{ $$ = $3; }
	| /* empty */						{ $$ = NULL; }

opt_include_nulls:
	INCLUDE_P NULLS_P					{ $$ = true; }
	| EXCLUDE NULLS_P					{ $$ = false; }
	| /* empty */						{ $$ = false; }

single_pivot_value:
	b_expr IN_P '(' target_list_opt_comma ')'
		{
			PGPivot *n = makeNode(PGPivot);
			n->pivot_columns = list_make1($1);
			n->pivot_value = $4;
			$$ = (PGNode *) n;
		}
	|
	b_expr IN_P ColIdOrString
		{
			PGPivot *n = makeNode(PGPivot);
			n->pivot_columns = list_make1($1);
			n->pivot_enum = $3;
			$$ = (PGNode *) n;
		}
	;

pivot_header:
	d_expr	                 			{ $$ = list_make1($1); }
	| indirection_expr						{ $$ = list_make1($1); }
	| '(' c_expr_list_opt_comma ')' 		{ $$ = $2; }

pivot_value:
	pivot_header IN_P '(' target_list_opt_comma ')'
		{
			PGPivot *n = makeNode(PGPivot);
			n->pivot_columns = $1;
			n->pivot_value = $4;
			$$ = (PGNode *) n;
		}
	|
	pivot_header IN_P ColIdOrString
		{
			PGPivot *n = makeNode(PGPivot);
			n->pivot_columns = $1;
			n->pivot_enum = $3;
			$$ = (PGNode *) n;
		}
	;

pivot_value_list:	pivot_value
				{
					$$ = list_make1($1);
				}
			| pivot_value_list pivot_value
				{
					$$ = lappend($1, $2);
				}
		;

unpivot_header:
		ColIdOrString 				  { $$ = list_make1(makeString($1)); }
		| '(' name_list_opt_comma ')' { $$ = $2; }
	;

unpivot_value:
	unpivot_header IN_P '(' target_list_opt_comma ')'
		{
			PGPivot *n = makeNode(PGPivot);
			n->unpivot_columns = $1;
			n->pivot_value = $4;
			$$ = (PGNode *) n;
		}
	;

unpivot_value_list:	unpivot_value
				{
					$$ = list_make1($1);
				}
			| unpivot_value_list unpivot_value
				{
					$$ = lappend($1, $2);
				}
		;

/*
 * It may seem silly to separate joined_table from table_ref, but there is
 * method in SQL's madness: if you don't do it this way you get reduce-
 * reduce conflicts, because it's not clear to the parser generator whether
 * to expect alias_clause after ')' or not.  For the same reason we must
 * treat 'JOIN' and 'join_type JOIN' separately, rather than allowing
 * join_type to expand to empty; if we try it, the parser generator can't
 * figure out when to reduce an empty join_type right after table_ref.
 *
 * Note that a CROSS JOIN is the same as an unqualified
 * INNER JOIN, and an INNER JOIN/ON has the same shape
 * but a qualification expression to limit membership.
 * A NATURAL JOIN implicitly matches column names between
 * tables and the shape is determined by which columns are
 * in common. We'll collect columns during the later transformations.
 * A POSITIONAL JOIN implicitly matches row numbers and is more like a table.
 */

joined_table:
			'(' joined_table ')'
				{
					$$ = $2;
				}
			| table_ref CROSS JOIN table_ref
				{
					/* CROSS JOIN is same as unqualified inner join */
					PGJoinExpr *n = makeNode(PGJoinExpr);
					n->jointype = PG_JOIN_INNER;
					n->joinreftype = PG_JOIN_REGULAR;
					n->larg = $1;
					n->rarg = $4;
					n->usingClause = NIL;
					n->quals = NULL;
					n->location = @2;
					$$ = n;
				}
			| table_ref join_type JOIN table_ref join_qual
				{
					PGJoinExpr *n = makeNode(PGJoinExpr);
					n->jointype = $2;
					n->joinreftype = PG_JOIN_REGULAR;
					n->larg = $1;
					n->rarg = $4;
					if ($5 != NULL && IsA($5, PGList))
						n->usingClause = (PGList *) $5; /* USING clause */
					else
						n->quals = $5; /* ON clause */
					n->location = @2;
					$$ = n;
				}
			| table_ref JOIN table_ref join_qual
				{
					/* letting join_type reduce to empty doesn't work */
					PGJoinExpr *n = makeNode(PGJoinExpr);
					n->jointype = PG_JOIN_INNER;
					n->joinreftype = PG_JOIN_REGULAR;
					n->larg = $1;
					n->rarg = $3;
					if ($4 != NULL && IsA($4, PGList))
						n->usingClause = (PGList *) $4; /* USING clause */
					else
						n->quals = $4; /* ON clause */
					n->location = @2;
					$$ = n;
				}
			| table_ref NATURAL join_type JOIN table_ref
				{
					PGJoinExpr *n = makeNode(PGJoinExpr);
					n->jointype = $3;
					n->joinreftype = PG_JOIN_NATURAL;
					n->larg = $1;
					n->rarg = $5;
					n->usingClause = NIL; /* figure out which columns later... */
					n->quals = NULL; /* fill later */
					n->location = @2;
					$$ = n;
				}
			| table_ref NATURAL JOIN table_ref
				{
					/* letting join_type reduce to empty doesn't work */
					PGJoinExpr *n = makeNode(PGJoinExpr);
					n->jointype = PG_JOIN_INNER;
					n->joinreftype = PG_JOIN_NATURAL;
					n->larg = $1;
					n->rarg = $4;
					n->usingClause = NIL; /* figure out which columns later... */
					n->quals = NULL; /* fill later */
					n->location = @2;
					$$ = n;
				}
			| table_ref ASOF join_type JOIN table_ref join_qual
				{
					PGJoinExpr *n = makeNode(PGJoinExpr);
					n->jointype = $3;
					n->joinreftype = PG_JOIN_ASOF;
					n->larg = $1;
					n->rarg = $5;
					if ($6 != NULL && IsA($6, PGList))
						n->usingClause = (PGList *) $6; /* USING clause */
					else
						n->quals = $6; /* ON clause */
					n->location = @2;
					$$ = n;
				}
			| table_ref ASOF JOIN table_ref join_qual
				{
					PGJoinExpr *n = makeNode(PGJoinExpr);
					n->jointype = PG_JOIN_INNER;
					n->joinreftype = PG_JOIN_ASOF;
					n->larg = $1;
					n->rarg = $4;
					if ($5 != NULL && IsA($5, PGList))
						n->usingClause = (PGList *) $5; /* USING clause */
					else
						n->quals = $5; /* ON clause */
					n->location = @2;
					$$ = n;
				}
			| table_ref POSITIONAL JOIN table_ref
				{
					/* POSITIONAL JOIN is a coordinated scan */
					PGJoinExpr *n = makeNode(PGJoinExpr);
					n->jointype = PG_JOIN_POSITION;
					n->joinreftype = PG_JOIN_REGULAR;
					n->larg = $1;
					n->rarg = $4;
					n->usingClause = NIL;
					n->quals = NULL;
					n->location = @2;
					$$ = n;
				}
            | table_ref ANTI JOIN table_ref join_qual
                {
                    /* ANTI JOIN is a filter */
                    PGJoinExpr *n = makeNode(PGJoinExpr);
                    n->jointype = PG_JOIN_ANTI;
                    n->joinreftype = PG_JOIN_REGULAR;
                    n->larg = $1;
                    n->rarg = $4;
                    if ($5 != NULL && IsA($5, PGList))
                        n->usingClause = (PGList *) $5; /* USING clause */
                    else
                        n->quals = $5; /* ON clause */
                    n->location = @2;
                    $$ = n;
                }
           | table_ref SEMI JOIN table_ref join_qual
               {
                   /* SEMI JOIN is also a filter */
                   PGJoinExpr *n = makeNode(PGJoinExpr);
                   n->jointype = PG_JOIN_SEMI;
                   n->joinreftype = PG_JOIN_REGULAR;
                   n->larg = $1;
                   n->rarg = $4;
                   if ($5 != NULL && IsA($5, PGList))
                       n->usingClause = (PGList *) $5; /* USING clause */
                   else
                       n->quals = $5; /* ON clause */
                   n->location = @2;
                   n->location = @2;
                   $$ = n;
               }
		;

alias_clause:
			AS ColIdOrString '(' name_list_opt_comma ')'
				{
					$$ = makeNode(PGAlias);
					$$->aliasname = $2;
					$$->colnames = $4;
				}
			| AS ColIdOrString
				{
					$$ = makeNode(PGAlias);
					$$->aliasname = $2;
				}
			| ColId '(' name_list_opt_comma ')'
				{
					$$ = makeNode(PGAlias);
					$$->aliasname = $1;
					$$->colnames = $3;
				}
			| ColId
				{
					$$ = makeNode(PGAlias);
					$$->aliasname = $1;
				}
		;

opt_alias_clause: alias_clause						{ $$ = $1; }
			| /*EMPTY*/								{ $$ = NULL; }
		;

/*
 * func_alias_clause can include both an PGAlias and a coldeflist, so we make it
 * return a 2-element list that gets disassembled by calling production.
 */
func_alias_clause:
			alias_clause
				{
					$$ = list_make2($1, NIL);
				}
			| AS '(' TableFuncElementList ')'
				{
					$$ = list_make2(NULL, $3);
				}
			| AS ColIdOrString '(' TableFuncElementList ')'
				{
					PGAlias *a = makeNode(PGAlias);
					a->aliasname = $2;
					$$ = list_make2(a, $4);
				}
			| ColId '(' TableFuncElementList ')'
				{
					PGAlias *a = makeNode(PGAlias);
					a->aliasname = $1;
					$$ = list_make2(a, $3);
				}
			| /*EMPTY*/
				{
					$$ = list_make2(NULL, NIL);
				}
		;

join_type:	FULL join_outer							{ $$ = PG_JOIN_FULL; }
			| LEFT join_outer						{ $$ = PG_JOIN_LEFT; }
			| RIGHT join_outer						{ $$ = PG_JOIN_RIGHT; }
			| SEMI          						{ $$ = PG_JOIN_SEMI; }
			| ANTI          						{ $$ = PG_JOIN_ANTI; }
			| INNER_P								{ $$ = PG_JOIN_INNER; }
		;

/* OUTER is just noise... */
join_outer: OUTER_P									{ $$ = NULL; }
			| /*EMPTY*/								{ $$ = NULL; }
		;

/* JOIN qualification clauses
 * Possibilities are:
 *	USING ( column list ) allows only unqualified column names,
 *						  which must match between tables.
 *	ON expr allows more general qualifications.
 *
 * We return USING as a PGList node, while an ON-expr will not be a List.
 */

join_qual:	USING '(' name_list_opt_comma ')'					{ $$ = (PGNode *) $3; }
			| ON a_expr								{ $$ = $2; }
		;


relation_expr:
			qualified_name
				{
					/* inheritance query, implicitly */
					$$ = $1;
					$$->inh = true;
					$$->alias = NULL;
				}
			| qualified_name '*'
				{
					/* inheritance query, explicitly */
					$$ = $1;
					$$->inh = true;
					$$->alias = NULL;
				}
			| ONLY qualified_name
				{
					/* no inheritance */
					$$ = $2;
					$$->inh = false;
					$$->alias = NULL;
				}
			| ONLY '(' qualified_name ')'
				{
					/* no inheritance, SQL99-style syntax */
					$$ = $3;
					$$->inh = false;
					$$->alias = NULL;
				}
		;


/*
 * Given "UPDATE foo set set ...", we have to decide without looking any
 * further ahead whether the first "set" is an alias or the UPDATE's SET
 * keyword.  Since "set" is allowed as a column name both interpretations
 * are feasible.  We resolve the shift/reduce conflict by giving the first
 * production a higher precedence than the SET token
 * has, causing the parser to prefer to reduce, in effect assuming that the
 * SET is not an alias.
 */

/*
 * func_table represents a function invocation in a FROM list. It can be
 * a plain function call, like "foo(...)", or a ROWS FROM expression with
 * one or more function calls, "ROWS FROM (foo(...), bar(...))",
 * optionally with WITH ORDINALITY attached.
 * In the ROWS FROM syntax, a column list can be given for each
 * function, for example:
 *     ROWS FROM (foo() AS (foo_res_a text, foo_res_b text),
 *                bar() AS (bar_res_a text, bar_res_b text))
 * It's also possible to attach a column list to the PGRangeFunction
 * as a whole, but that's handled by the table_ref production.
 */
func_table: func_expr_windowless opt_ordinality
				{
					PGRangeFunction *n = makeNode(PGRangeFunction);
					n->lateral = false;
					n->ordinality = $2;
					n->is_rowsfrom = false;
					n->functions = list_make1(list_make2($1, NIL));
					n->sample = NULL;
					/* alias and coldeflist are set by table_ref production */
					$$ = (PGNode *) n;
				}
			| ROWS FROM '(' rowsfrom_list ')' opt_ordinality
				{
					PGRangeFunction *n = makeNode(PGRangeFunction);
					n->lateral = false;
					n->ordinality = $6;
					n->is_rowsfrom = true;
					n->functions = $4;
					n->sample = NULL;
					/* alias and coldeflist are set by table_ref production */
					$$ = (PGNode *) n;
				}
		;

rowsfrom_item: func_expr_windowless opt_col_def_list
				{ $$ = list_make2($1, $2); }
		;

rowsfrom_list:
			rowsfrom_item						{ $$ = list_make1($1); }
			| rowsfrom_list ',' rowsfrom_item	{ $$ = lappend($1, $3); }
		;

opt_col_def_list: AS '(' TableFuncElementList ')'	{ $$ = $3; }
			| /*EMPTY*/								{ $$ = NIL; }
		;

opt_ordinality: WITH_LA ORDINALITY					{ $$ = true; }
			| /*EMPTY*/								{ $$ = false; }
		;


where_clause:
			WHERE a_expr							{ $$ = $2; }
			| /*EMPTY*/								{ $$ = NULL; }
		;

/* variant for UPDATE and DELETE */
TableFuncElementList:
			TableFuncElement
				{
					$$ = list_make1($1);
				}
			| TableFuncElementList ',' TableFuncElement
				{
					$$ = lappend($1, $3);
				}
		;

TableFuncElement:	ColIdOrString Typename opt_collate_clause
				{
					PGColumnDef *n = makeNode(PGColumnDef);
					n->colname = $1;
					n->typeName = $2;
					n->inhcount = 0;
					n->is_local = true;
					n->is_not_null = false;
					n->is_from_type = false;
					n->storage = 0;
					n->raw_default = NULL;
					n->cooked_default = NULL;
					n->collClause = (PGCollateClause *) $3;
					n->collOid = InvalidOid;
					n->constraints = NIL;
					n->location = @1;
					$$ = (PGNode *)n;
				}
		;

opt_collate_clause:
			COLLATE any_name
				{
					PGCollateClause *n = makeNode(PGCollateClause);
					n->arg = NULL;
					n->collname = $2;
					n->location = @1;
					$$ = (PGNode *) n;
				}
			| /* EMPTY */				{ $$ = NULL; }
		;

/*****************************************************************************
 *
 *	Type syntax
 *		SQL introduces a large amount of type-specific syntax.
 *		Define individual clauses to handle these cases, and use
 *		 the generic case to handle regular type-extensible Postgres syntax.
 *		- thomas 1997-10-10
 *
 *****************************************************************************/

colid_type_list:
            ColId Typename   {
             $$ = list_make1(list_make2(makeString($1), $2));
            }
            | colid_type_list ',' ColId Typename {
             $$ = lappend($1, list_make2(makeString($3), $4));
            }

RowOrStruct: ROW | STRUCT

opt_Typename:
			Typename						{ $$ = $1; }
			| /*EMPTY*/						{ $$ = NULL; }

Typename:	SimpleTypename opt_array_bounds
				{
					$$ = $1;
					$$->arrayBounds = $2;
				}
			| SETOF SimpleTypename opt_array_bounds
				{
					$$ = $2;
					$$->arrayBounds = $3;
					$$->setof = true;
				}
			/* SQL standard syntax, currently only one-dimensional */
			| SimpleTypename ARRAY '[' Iconst ']'
				{
					$$ = $1;
					$$->arrayBounds = list_make1(makeInteger($4));
				}
			| SETOF SimpleTypename ARRAY '[' Iconst ']'
				{
					$$ = $2;
					$$->arrayBounds = list_make1(makeInteger($5));
					$$->setof = true;
				}
			| SimpleTypename ARRAY
				{
					$$ = $1;
					$$->arrayBounds = list_make1(makeInteger(-1));
				}
			| SETOF SimpleTypename ARRAY
				{
					$$ = $2;
					$$->arrayBounds = list_make1(makeInteger(-1));
					$$->setof = true;
				}
			| qualified_typename
				{
					$$ = makeTypeNameFromNameList($1);
				}
			| RowOrStruct '(' colid_type_list ')' opt_array_bounds
				{
				   $$ = SystemTypeName("struct");
				   $$->arrayBounds = $5;
				   $$->typmods = $3;
				   $$->location = @1;
               }
            | MAP '(' type_list ')' opt_array_bounds
            	{
				   $$ = SystemTypeName("map");
				   $$->arrayBounds = $5;
				   $$->typmods = $3;
				   $$->location = @1;
				}
			| UNION '(' colid_type_list ')' opt_array_bounds
				{
				   $$ = SystemTypeName("union");
				   $$->arrayBounds = $5;
				   $$->typmods = $3;
				   $$->location = @1;
				}
		;

qualified_typename:
			IDENT '.' IDENT					{ $$ = list_make2(makeString($1), makeString($3)); }
			| qualified_typename '.' IDENT	{ $$ = lappend($1, makeString($3)); }
	;

opt_array_bounds:
			opt_array_bounds '[' ']'
					{  $$ = lappend($1, makeInteger(-1)); }
			| opt_array_bounds '[' Iconst ']'
					{  $$ = lappend($1, makeInteger($3)); }
			| /*EMPTY*/
					{  $$ = NIL; }
		;

SimpleTypename:
			GenericType								{ $$ = $1; }
			| Numeric								{ $$ = $1; }
			| Bit									{ $$ = $1; }
			| Character								{ $$ = $1; }
			| ConstDatetime							{ $$ = $1; }
			| ConstInterval opt_interval
				{
					$$ = $1;
					$$->typmods = $2;
				}
			| ConstInterval '(' Iconst ')'
				{
					$$ = $1;
					$$->typmods = list_make2(makeIntConst(INTERVAL_FULL_RANGE, -1),
											 makeIntConst($3, @3));
				}
		;

/* We have a separate ConstTypename to allow defaulting fixed-length
 * types such as CHAR() and BIT() to an unspecified length.
 * SQL9x requires that these default to a length of one, but this
 * makes no sense for constructs like CHAR 'hi' and BIT '0101',
 * where there is an obvious better choice to make.
 * Note that ConstInterval is not included here since it must
 * be pushed up higher in the rules to accommodate the postfix
 * options (e.g. INTERVAL '1' YEAR). Likewise, we have to handle
 * the generic-type-name case in AExprConst to avoid premature
 * reduce/reduce conflicts against function names.
 */
ConstTypename:
			Numeric									{ $$ = $1; }
			| ConstBit								{ $$ = $1; }
			| ConstCharacter						{ $$ = $1; }
			| ConstDatetime							{ $$ = $1; }
		;

/*
 * GenericType covers all type names that don't have special syntax mandated
 * by the standard, including qualified names.  We also allow type modifiers.
 * To avoid parsing conflicts against function invocations, the modifiers
 * have to be shown as expr_list here, but parse analysis will only accept
 * constants for them.
 */
GenericType:
			type_name_token opt_type_modifiers
				{
					$$ = makeTypeName($1);
					$$->typmods = $2;
					$$->location = @1;
				}
			// | type_name_token attrs opt_type_modifiers
			// 	{
			// 		$$ = makeTypeNameFromNameList(lcons(makeString($1), $2));
			// 		$$->typmods = $3;
			// 		$$->location = @1;
			// 	}
		;

opt_type_modifiers: '(' opt_expr_list_opt_comma	 ')'				{ $$ = $2; }
					| /* EMPTY */					{ $$ = NIL; }
		;

/*
 * SQL numeric data types
 */
Numeric:	INT_P
				{
					$$ = SystemTypeName("int4");
					$$->location = @1;
				}
			| INTEGER
				{
					$$ = SystemTypeName("int4");
					$$->location = @1;
				}
			| SMALLINT
				{
					$$ = SystemTypeName("int2");
					$$->location = @1;
				}
			| BIGINT
				{
					$$ = SystemTypeName("int8");
					$$->location = @1;
				}
			| REAL
				{
					$$ = SystemTypeName("float4");
					$$->location = @1;
				}
			| FLOAT_P opt_float
				{
					$$ = $2;
					$$->location = @1;
				}
			| DOUBLE_P PRECISION
				{
					$$ = SystemTypeName("float8");
					$$->location = @1;
				}
			| DECIMAL_P opt_type_modifiers
				{
					$$ = SystemTypeName("numeric");
					$$->typmods = $2;
					$$->location = @1;
				}
			| DEC opt_type_modifiers
				{
					$$ = SystemTypeName("numeric");
					$$->typmods = $2;
					$$->location = @1;
				}
			| NUMERIC opt_type_modifiers
				{
					$$ = SystemTypeName("numeric");
					$$->typmods = $2;
					$$->location = @1;
				}
			| BOOLEAN_P
				{
					$$ = SystemTypeName("bool");
					$$->location = @1;
				}
		;

opt_float:	'(' Iconst ')'
				{
					/*
					 * Check FLOAT() precision limits assuming IEEE floating
					 * types - thomas 1997-09-18
					 */
					if ($2 < 1)
						ereport(ERROR,
								(errcode(PG_ERRCODE_INVALID_PARAMETER_VALUE),
								 errmsg("precision for type float must be at least 1 bit"),
								 parser_errposition(@2)));
					else if ($2 <= 24)
						$$ = SystemTypeName("float4");
					else if ($2 <= 53)
						$$ = SystemTypeName("float8");
					else
						ereport(ERROR,
								(errcode(PG_ERRCODE_INVALID_PARAMETER_VALUE),
								 errmsg("precision for type float must be less than 54 bits"),
								 parser_errposition(@2)));
				}
			| /*EMPTY*/
				{
					$$ = SystemTypeName("float4");
				}
		;

/*
 * SQL bit-field data types
 * The following implements BIT() and BIT VARYING().
 */
Bit:		BitWithLength
				{
					$$ = $1;
				}
			| BitWithoutLength
				{
					$$ = $1;
				}
		;

/* ConstBit is like Bit except "BIT" defaults to unspecified length */
/* See notes for ConstCharacter, which addresses same issue for "CHAR" */
ConstBit:	BitWithLength
				{
					$$ = $1;
				}
			| BitWithoutLength
				{
					$$ = $1;
					$$->typmods = NIL;
				}
		;

BitWithLength:
			BIT opt_varying '(' expr_list_opt_comma ')'
				{
					const char *typname;

					typname = $2 ? "varbit" : "bit";
					$$ = SystemTypeName(typname);
					$$->typmods = $4;
					$$->location = @1;
				}
		;

BitWithoutLength:
			BIT opt_varying
				{
					/* bit defaults to bit(1), varbit to no limit */
					if ($2)
					{
						$$ = SystemTypeName("varbit");
					}
					else
					{
						$$ = SystemTypeName("bit");
						$$->typmods = list_make1(makeIntConst(1, -1));
					}
					$$->location = @1;
				}
		;


/*
 * SQL character data types
 * The following implements CHAR() and VARCHAR().
 */
Character:  CharacterWithLength
				{
					$$ = $1;
				}
			| CharacterWithoutLength
				{
					$$ = $1;
				}
		;

ConstCharacter:  CharacterWithLength
				{
					$$ = $1;
				}
			| CharacterWithoutLength
				{
					/* Length was not specified so allow to be unrestricted.
					 * This handles problems with fixed-length (bpchar) strings
					 * which in column definitions must default to a length
					 * of one, but should not be constrained if the length
					 * was not specified.
					 */
					$$ = $1;
					$$->typmods = NIL;
				}
		;

CharacterWithLength:  character '(' Iconst ')'
				{
					$$ = SystemTypeName($1);
					$$->typmods = list_make1(makeIntConst($3, @3));
					$$->location = @1;
				}
		;

CharacterWithoutLength:	 character
				{
					$$ = SystemTypeName($1);
					/* char defaults to char(1), varchar to no limit */
					if (strcmp($1, "bpchar") == 0)
						$$->typmods = list_make1(makeIntConst(1, -1));
					$$->location = @1;
				}
		;

character:	CHARACTER opt_varying
										{ $$ = $2 ? "varchar": "bpchar"; }
			| CHAR_P opt_varying
										{ $$ = $2 ? "varchar": "bpchar"; }
			| VARCHAR
										{ $$ = "varchar"; }
			| NATIONAL CHARACTER opt_varying
										{ $$ = $3 ? "varchar": "bpchar"; }
			| NATIONAL CHAR_P opt_varying
										{ $$ = $3 ? "varchar": "bpchar"; }
			| NCHAR opt_varying
										{ $$ = $2 ? "varchar": "bpchar"; }
		;

opt_varying:
			VARYING									{ $$ = true; }
			| /*EMPTY*/								{ $$ = false; }
		;

/*
 * SQL date/time types
 */
ConstDatetime:
			TIMESTAMP '(' Iconst ')' opt_timezone
				{
					if ($5)
						$$ = SystemTypeName("timestamptz");
					else
						$$ = SystemTypeName("timestamp");
					$$->typmods = list_make1(makeIntConst($3, @3));
					$$->location = @1;
				}
			| TIMESTAMP opt_timezone
				{
					if ($2)
						$$ = SystemTypeName("timestamptz");
					else
						$$ = SystemTypeName("timestamp");
					$$->location = @1;
				}
			| TIME '(' Iconst ')' opt_timezone
				{
					if ($5)
						$$ = SystemTypeName("timetz");
					else
						$$ = SystemTypeName("time");
					$$->typmods = list_make1(makeIntConst($3, @3));
					$$->location = @1;
				}
			| TIME opt_timezone
				{
					if ($2)
						$$ = SystemTypeName("timetz");
					else
						$$ = SystemTypeName("time");
					$$->location = @1;
				}
		;

ConstInterval:
			INTERVAL
				{
					$$ = SystemTypeName("interval");
					$$->location = @1;
				}
		;

opt_timezone:
			WITH_LA TIME ZONE						{ $$ = true; }
			| WITHOUT TIME ZONE						{ $$ = false; }
			| /*EMPTY*/								{ $$ = false; }
		;

year_keyword:
	YEAR_P | YEARS_P

month_keyword:
	MONTH_P | MONTHS_P

day_keyword:
	DAY_P | DAYS_P

hour_keyword:
	HOUR_P | HOURS_P

minute_keyword:
	MINUTE_P | MINUTES_P

second_keyword:
	SECOND_P | SECONDS_P

millisecond_keyword:
	MILLISECOND_P | MILLISECONDS_P

microsecond_keyword:
	MICROSECOND_P | MICROSECONDS_P

week_keyword:
	WEEK_P | WEEKS_P

quarter_keyword:
	QUARTER_P | QUARTERS_P

decade_keyword:
	DECADE_P | DECADES_P

century_keyword:
	CENTURY_P | CENTURIES_P

millennium_keyword:
	MILLENNIUM_P | MILLENNIA_P

opt_interval:
			year_keyword
				{ $$ = list_make1(makeIntConst(INTERVAL_MASK(YEAR), @1)); }
			| month_keyword
				{ $$ = list_make1(makeIntConst(INTERVAL_MASK(MONTH), @1)); }
			| day_keyword
				{ $$ = list_make1(makeIntConst(INTERVAL_MASK(DAY), @1)); }
			| hour_keyword
				{ $$ = list_make1(makeIntConst(INTERVAL_MASK(HOUR), @1)); }
			| minute_keyword
				{ $$ = list_make1(makeIntConst(INTERVAL_MASK(MINUTE), @1)); }
			| second_keyword
				{ $$ = list_make1(makeIntConst(INTERVAL_MASK(SECOND), @1)); }
			| millisecond_keyword
				{ $$ = list_make1(makeIntConst(INTERVAL_MASK(MILLISECOND), @1)); }
			| microsecond_keyword
				{ $$ = list_make1(makeIntConst(INTERVAL_MASK(MICROSECOND), @1)); }
			| week_keyword
				{ $$ = list_make1(makeIntConst(INTERVAL_MASK(WEEK), @1)); }
			| quarter_keyword
				{ $$ = list_make1(makeIntConst(INTERVAL_MASK(QUARTER), @1)); }
			| decade_keyword
				{ $$ = list_make1(makeIntConst(INTERVAL_MASK(DECADE), @1)); }
			| century_keyword
				{ $$ = list_make1(makeIntConst(INTERVAL_MASK(CENTURY), @1)); }
			| millennium_keyword
				{ $$ = list_make1(makeIntConst(INTERVAL_MASK(MILLENNIUM), @1)); }
			| year_keyword TO month_keyword
				{
					$$ = list_make1(makeIntConst(INTERVAL_MASK(YEAR) |
												 INTERVAL_MASK(MONTH), @1));
				}
			| day_keyword TO hour_keyword
				{
					$$ = list_make1(makeIntConst(INTERVAL_MASK(DAY) |
												 INTERVAL_MASK(HOUR), @1));
				}
			| day_keyword TO minute_keyword
				{
					$$ = list_make1(makeIntConst(INTERVAL_MASK(DAY) |
												 INTERVAL_MASK(HOUR) |
												 INTERVAL_MASK(MINUTE), @1));
				}
			| day_keyword TO second_keyword
				{
					$$ = list_make1(makeIntConst(INTERVAL_MASK(DAY) |
												 INTERVAL_MASK(HOUR) |
												 INTERVAL_MASK(MINUTE) |
												 INTERVAL_MASK(SECOND), @1));
				}
			| hour_keyword TO minute_keyword
				{
					$$ = list_make1(makeIntConst(INTERVAL_MASK(HOUR) |
												 INTERVAL_MASK(MINUTE), @1));
				}
			| hour_keyword TO second_keyword
				{
					$$ = list_make1(makeIntConst(INTERVAL_MASK(HOUR) |
												 INTERVAL_MASK(MINUTE) |
												 INTERVAL_MASK(SECOND), @1));
				}
			| minute_keyword TO second_keyword
				{
					$$ = list_make1(makeIntConst(INTERVAL_MASK(MINUTE) |
												 INTERVAL_MASK(SECOND), @1));
				}
			| /*EMPTY*/
				{ $$ = NIL; }
		;

/*****************************************************************************
 *
 *	expression grammar
 *
 *****************************************************************************/

/*
 * General expressions
 * This is the heart of the expression syntax.
 *
 * We have two expression types: a_expr is the unrestricted kind, and
 * b_expr is a subset that must be used in some places to avoid shift/reduce
 * conflicts.  For example, we can't do BETWEEN as "BETWEEN a_expr AND a_expr"
 * because that use of AND conflicts with AND as a boolean operator.  So,
 * b_expr is used in BETWEEN and we remove boolean keywords from b_expr.
 *
 * Note that '(' a_expr ')' is a b_expr, so an unrestricted expression can
 * always be used by surrounding it with parens.
 *
 * c_expr is all the productions that are common to a_expr and b_expr;
 * it's factored out just to eliminate redundant coding.
 *
 * Be careful of productions involving more than one terminal token.
 * By default, bison will assign such productions the precedence of their
 * last terminal, but in nearly all cases you want it to be the precedence
 * of the first terminal instead; otherwise you will not get the behavior
 * you expect!  So we use %prec annotations freely to set precedences.
 */
a_expr:		c_expr									{ $$ = $1; }
			|
			a_expr TYPECAST Typename
					{ $$ = makeTypeCast($1, $3, 0, @2); }
			| a_expr COLLATE any_name
				{
					PGCollateClause *n = makeNode(PGCollateClause);
					n->arg = $1;
					n->collname = $3;
					n->location = @2;
					$$ = (PGNode *) n;
				}
			| a_expr AT TIME ZONE a_expr			%prec AT
				{
					$$ = (PGNode *) makeFuncCall(SystemFuncName("timezone"),
											   list_make2($5, $1),
											   @2);
				}
		/*
		 * These operators must be called out explicitly in order to make use
		 * of bison's automatic operator-precedence handling.  All other
		 * operator names are handled by the generic productions using "Op",
		 * below; and all those operators will have the same precedence.
		 *
		 * If you add more explicitly-known operators, be sure to add them
		 * also to b_expr and to the MathOp list below.
		 */
			| '+' a_expr					%prec UMINUS
				{ $$ = (PGNode *) makeSimpleAExpr(PG_AEXPR_OP, "+", NULL, $2, @1); }
			| '-' a_expr					%prec UMINUS
				{ $$ = doNegate($2, @1); }
			| a_expr '+' a_expr
				{ $$ = (PGNode *) makeSimpleAExpr(PG_AEXPR_OP, "+", $1, $3, @2); }
			| a_expr '-' a_expr
				{ $$ = (PGNode *) makeSimpleAExpr(PG_AEXPR_OP, "-", $1, $3, @2); }
			| a_expr '*' a_expr
				{ $$ = (PGNode *) makeSimpleAExpr(PG_AEXPR_OP, "*", $1, $3, @2); }
			| a_expr '/' a_expr
				{ $$ = (PGNode *) makeSimpleAExpr(PG_AEXPR_OP, "/", $1, $3, @2); }
			| a_expr INTEGER_DIVISION a_expr
				{ $$ = (PGNode *) makeSimpleAExpr(PG_AEXPR_OP, "//", $1, $3, @2); }
			| a_expr '%' a_expr
				{ $$ = (PGNode *) makeSimpleAExpr(PG_AEXPR_OP, "%", $1, $3, @2); }
			| a_expr '^' a_expr
				{ $$ = (PGNode *) makeSimpleAExpr(PG_AEXPR_OP, "^", $1, $3, @2); }
			| a_expr POWER_OF a_expr
				{ $$ = (PGNode *) makeSimpleAExpr(PG_AEXPR_OP, "**", $1, $3, @2); }
			| a_expr '<' a_expr
				{ $$ = (PGNode *) makeSimpleAExpr(PG_AEXPR_OP, "<", $1, $3, @2); }
			| a_expr '>' a_expr
				{ $$ = (PGNode *) makeSimpleAExpr(PG_AEXPR_OP, ">", $1, $3, @2); }
			| a_expr '=' a_expr
				{ $$ = (PGNode *) makeSimpleAExpr(PG_AEXPR_OP, "=", $1, $3, @2); }
			| a_expr LESS_EQUALS a_expr
				{ $$ = (PGNode *) makeSimpleAExpr(PG_AEXPR_OP, "<=", $1, $3, @2); }
			| a_expr GREATER_EQUALS a_expr
				{ $$ = (PGNode *) makeSimpleAExpr(PG_AEXPR_OP, ">=", $1, $3, @2); }
			| a_expr NOT_EQUALS a_expr
				{ $$ = (PGNode *) makeSimpleAExpr(PG_AEXPR_OP, "<>", $1, $3, @2); }

			| a_expr qual_Op a_expr				%prec Op
				{ $$ = (PGNode *) makeAExpr(PG_AEXPR_OP, $2, $1, $3, @2); }
			| qual_Op a_expr					%prec Op
				{ $$ = (PGNode *) makeAExpr(PG_AEXPR_OP, $1, NULL, $2, @1); }
			| a_expr qual_Op					%prec POSTFIXOP
				{ $$ = (PGNode *) makeAExpr(PG_AEXPR_OP, $2, $1, NULL, @2); }

			| a_expr AND a_expr
				{ $$ = makeAndExpr($1, $3, @2); }
			| a_expr OR a_expr
				{ $$ = makeOrExpr($1, $3, @2); }
			| NOT a_expr
				{ $$ = makeNotExpr($2, @1); }
			| NOT_LA a_expr						%prec NOT
				{ $$ = makeNotExpr($2, @1); }
			| a_expr GLOB a_expr %prec GLOB
				{
					$$ = (PGNode *) makeSimpleAExpr(PG_AEXPR_GLOB, "~~~",
												   $1, $3, @2);
				}
			| a_expr LIKE a_expr
				{
					$$ = (PGNode *) makeSimpleAExpr(PG_AEXPR_LIKE, "~~",
												   $1, $3, @2);
				}
			| a_expr LIKE a_expr ESCAPE a_expr					%prec LIKE
				{
					PGFuncCall *n = makeFuncCall(SystemFuncName("like_escape"),
											   list_make3($1, $3, $5),
											   @2);
					$$ = (PGNode *) n;
				}
			| a_expr NOT_LA LIKE a_expr							%prec NOT_LA
				{
					$$ = (PGNode *) makeSimpleAExpr(PG_AEXPR_LIKE, "!~~",
												   $1, $4, @2);
				}
			| a_expr NOT_LA LIKE a_expr ESCAPE a_expr			%prec NOT_LA
				{
					PGFuncCall *n = makeFuncCall(SystemFuncName("not_like_escape"),
											   list_make3($1, $4, $6),
											   @2);
					$$ = (PGNode *) n;
				}
			| a_expr ILIKE a_expr
				{
					$$ = (PGNode *) makeSimpleAExpr(PG_AEXPR_ILIKE, "~~*",
												   $1, $3, @2);
				}
			| a_expr ILIKE a_expr ESCAPE a_expr					%prec ILIKE
				{
					PGFuncCall *n = makeFuncCall(SystemFuncName("ilike_escape"),
											   list_make3($1, $3, $5),
											   @2);
					$$ = (PGNode *) n;
				}
			| a_expr NOT_LA ILIKE a_expr						%prec NOT_LA
				{
					$$ = (PGNode *) makeSimpleAExpr(PG_AEXPR_ILIKE, "!~~*",
												   $1, $4, @2);
				}
			| a_expr NOT_LA ILIKE a_expr ESCAPE a_expr			%prec NOT_LA
				{
					PGFuncCall *n = makeFuncCall(SystemFuncName("not_ilike_escape"),
											   list_make3($1, $4, $6),
											   @2);
					$$ = (PGNode *) n;
				}

			| a_expr SIMILAR TO a_expr							%prec SIMILAR
				{
					PGFuncCall *n = makeFuncCall(SystemFuncName("similar_escape"),
											   list_make2($4, makeNullAConst(-1)),
											   @2);
					$$ = (PGNode *) makeSimpleAExpr(PG_AEXPR_SIMILAR, "~",
												   $1, (PGNode *) n, @2);
				}
			| a_expr SIMILAR TO a_expr ESCAPE a_expr			%prec SIMILAR
				{
					PGFuncCall *n = makeFuncCall(SystemFuncName("similar_escape"),
											   list_make2($4, $6),
											   @2);
					$$ = (PGNode *) makeSimpleAExpr(PG_AEXPR_SIMILAR, "~",
												   $1, (PGNode *) n, @2);
				}
			| a_expr NOT_LA SIMILAR TO a_expr					%prec NOT_LA
				{
					PGFuncCall *n = makeFuncCall(SystemFuncName("similar_escape"),
											   list_make2($5, makeNullAConst(-1)),
											   @2);
					$$ = (PGNode *) makeSimpleAExpr(PG_AEXPR_SIMILAR, "!~",
												   $1, (PGNode *) n, @2);
				}
			| a_expr NOT_LA SIMILAR TO a_expr ESCAPE a_expr		%prec NOT_LA
				{
					PGFuncCall *n = makeFuncCall(SystemFuncName("similar_escape"),
											   list_make2($5, $7),
											   @2);
					$$ = (PGNode *) makeSimpleAExpr(PG_AEXPR_SIMILAR, "!~",
												   $1, (PGNode *) n, @2);
				}

			/* PGNullTest clause
			 * Define SQL-style Null test clause.
			 * Allow two forms described in the standard:
			 *	a IS NULL
			 *	a IS NOT NULL
			 * Allow two SQL extensions
			 *	a ISNULL
			 *	a NOTNULL
			 */
			| a_expr IS NULL_P							%prec IS
				{
					PGNullTest *n = makeNode(PGNullTest);
					n->arg = (PGExpr *) $1;
					n->nulltesttype = PG_IS_NULL;
					n->location = @2;
					$$ = (PGNode *)n;
				}
			| a_expr ISNULL
				{
					PGNullTest *n = makeNode(PGNullTest);
					n->arg = (PGExpr *) $1;
					n->nulltesttype = PG_IS_NULL;
					n->location = @2;
					$$ = (PGNode *)n;
				}
			| a_expr IS NOT NULL_P						%prec IS
				{
					PGNullTest *n = makeNode(PGNullTest);
					n->arg = (PGExpr *) $1;
					n->nulltesttype = IS_NOT_NULL;
					n->location = @2;
					$$ = (PGNode *)n;
				}
			| a_expr NOT NULL_P
				{
					PGNullTest *n = makeNode(PGNullTest);
					n->arg = (PGExpr *) $1;
					n->nulltesttype = IS_NOT_NULL;
					n->location = @2;
					$$ = (PGNode *)n;
				}
			| a_expr NOTNULL
				{
					PGNullTest *n = makeNode(PGNullTest);
					n->arg = (PGExpr *) $1;
					n->nulltesttype = IS_NOT_NULL;
					n->location = @2;
					$$ = (PGNode *)n;
				}
			| a_expr LAMBDA_ARROW a_expr
			{
				PGLambdaFunction *n = makeNode(PGLambdaFunction);
				n->lhs = $1;
				n->rhs = $3;
				n->location = @2;
				$$ = (PGNode *) n;
			}
			| a_expr DOUBLE_ARROW a_expr %prec Op
			{
							$$ = (PGNode *) makeSimpleAExpr(PG_AEXPR_OP, "->>", $1, $3, @2);
			}
			| row OVERLAPS row
				{
					if (list_length($1) != 2)
						ereport(ERROR,
								(errcode(PG_ERRCODE_SYNTAX_ERROR),
								 errmsg("wrong number of parameters on left side of OVERLAPS expression"),
								 parser_errposition(@1)));
					if (list_length($3) != 2)
						ereport(ERROR,
								(errcode(PG_ERRCODE_SYNTAX_ERROR),
								 errmsg("wrong number of parameters on right side of OVERLAPS expression"),
								 parser_errposition(@3)));
					$$ = (PGNode *) makeFuncCall(SystemFuncName("overlaps"),
											   list_concat($1, $3),
											   @2);
				}
			| a_expr IS TRUE_P							%prec IS
				{
					PGBooleanTest *b = makeNode(PGBooleanTest);
					b->arg = (PGExpr *) $1;
					b->booltesttype = PG_IS_TRUE;
					b->location = @2;
					$$ = (PGNode *)b;
				}
			| a_expr IS NOT TRUE_P						%prec IS
				{
					PGBooleanTest *b = makeNode(PGBooleanTest);
					b->arg = (PGExpr *) $1;
					b->booltesttype = IS_NOT_TRUE;
					b->location = @2;
					$$ = (PGNode *)b;
				}
			| a_expr IS FALSE_P							%prec IS
				{
					PGBooleanTest *b = makeNode(PGBooleanTest);
					b->arg = (PGExpr *) $1;
					b->booltesttype = IS_FALSE;
					b->location = @2;
					$$ = (PGNode *)b;
				}
			| a_expr IS NOT FALSE_P						%prec IS
				{
					PGBooleanTest *b = makeNode(PGBooleanTest);
					b->arg = (PGExpr *) $1;
					b->booltesttype = IS_NOT_FALSE;
					b->location = @2;
					$$ = (PGNode *)b;
				}
			| a_expr IS UNKNOWN							%prec IS
				{
					PGBooleanTest *b = makeNode(PGBooleanTest);
					b->arg = (PGExpr *) $1;
					b->booltesttype = IS_UNKNOWN;
					b->location = @2;
					$$ = (PGNode *)b;
				}
			| a_expr IS NOT UNKNOWN						%prec IS
				{
					PGBooleanTest *b = makeNode(PGBooleanTest);
					b->arg = (PGExpr *) $1;
					b->booltesttype = IS_NOT_UNKNOWN;
					b->location = @2;
					$$ = (PGNode *)b;
				}
			| a_expr IS DISTINCT FROM a_expr			%prec IS
				{
					$$ = (PGNode *) makeSimpleAExpr(PG_AEXPR_DISTINCT, "=", $1, $5, @2);
				}
			| a_expr IS NOT DISTINCT FROM a_expr		%prec IS
				{
					$$ = (PGNode *) makeSimpleAExpr(PG_AEXPR_NOT_DISTINCT, "=", $1, $6, @2);
				}
			| a_expr IS OF '(' type_list ')'			%prec IS
				{
					$$ = (PGNode *) makeSimpleAExpr(PG_AEXPR_OF, "=", $1, (PGNode *) $5, @2);
				}
			| a_expr IS NOT OF '(' type_list ')'		%prec IS
				{
					$$ = (PGNode *) makeSimpleAExpr(PG_AEXPR_OF, "<>", $1, (PGNode *) $6, @2);
				}
			| a_expr BETWEEN opt_asymmetric b_expr AND a_expr		%prec BETWEEN
				{
					$$ = (PGNode *) makeSimpleAExpr(PG_AEXPR_BETWEEN,
												   "BETWEEN",
												   $1,
												   (PGNode *) list_make2($4, $6),
												   @2);
				}
			| a_expr NOT_LA BETWEEN opt_asymmetric b_expr AND a_expr %prec NOT_LA
				{
					$$ = (PGNode *) makeSimpleAExpr(PG_AEXPR_NOT_BETWEEN,
												   "NOT BETWEEN",
												   $1,
												   (PGNode *) list_make2($5, $7),
												   @2);
				}
			| a_expr BETWEEN SYMMETRIC b_expr AND a_expr			%prec BETWEEN
				{
					$$ = (PGNode *) makeSimpleAExpr(PG_AEXPR_BETWEEN_SYM,
												   "BETWEEN SYMMETRIC",
												   $1,
												   (PGNode *) list_make2($4, $6),
												   @2);
				}
			| a_expr NOT_LA BETWEEN SYMMETRIC b_expr AND a_expr		%prec NOT_LA
				{
					$$ = (PGNode *) makeSimpleAExpr(PG_AEXPR_NOT_BETWEEN_SYM,
												   "NOT BETWEEN SYMMETRIC",
												   $1,
												   (PGNode *) list_make2($5, $7),
												   @2);
				}
			| a_expr IN_P in_expr
				{
					/* in_expr returns a PGSubLink or a list of a_exprs */
					if (IsA($3, PGSubLink))
					{
						/* generate foo = ANY (subquery) */
						PGSubLink *n = (PGSubLink *) $3;
						n->subLinkType = PG_ANY_SUBLINK;
						n->subLinkId = 0;
						n->testexpr = $1;
						n->operName = NIL;		/* show it's IN not = ANY */
						n->location = @2;
						$$ = (PGNode *)n;
					}
					else
					{
						/* generate scalar IN expression */
						$$ = (PGNode *) makeSimpleAExpr(PG_AEXPR_IN, "=", $1, $3, @2);
					}
				}
			| a_expr NOT_LA IN_P in_expr						%prec NOT_LA
				{
					/* in_expr returns a PGSubLink or a list of a_exprs */
					if (IsA($4, PGSubLink))
					{
						/* generate NOT (foo = ANY (subquery)) */
						/* Make an = ANY node */
						PGSubLink *n = (PGSubLink *) $4;
						n->subLinkType = PG_ANY_SUBLINK;
						n->subLinkId = 0;
						n->testexpr = $1;
						n->operName = NIL;		/* show it's IN not = ANY */
						n->location = @2;
						/* Stick a NOT on top; must have same parse location */
						$$ = makeNotExpr((PGNode *) n, @2);
					}
					else
					{
						/* generate scalar NOT IN expression */
						$$ = (PGNode *) makeSimpleAExpr(PG_AEXPR_IN, "<>", $1, $4, @2);
					}
				}
			| a_expr subquery_Op sub_type select_with_parens	%prec Op
				{
					PGSubLink *n = makeNode(PGSubLink);
					n->subLinkType = $3;
					n->subLinkId = 0;
					n->testexpr = $1;
					n->operName = $2;
					n->subselect = $4;
					n->location = @2;
					$$ = (PGNode *)n;
				}
			| a_expr subquery_Op sub_type '(' a_expr ')'		%prec Op
				{
					if ($3 == PG_ANY_SUBLINK)
						$$ = (PGNode *) makeAExpr(PG_AEXPR_OP_ANY, $2, $1, $5, @2);
					else
						$$ = (PGNode *) makeAExpr(PG_AEXPR_OP_ALL, $2, $1, $5, @2);
				}
			| DEFAULT
				{
					/*
					 * The SQL spec only allows DEFAULT in "contextually typed
					 * expressions", but for us, it's easier to allow it in
					 * any a_expr and then throw error during parse analysis
					 * if it's in an inappropriate context.  This way also
					 * lets us say something smarter than "syntax error".
					 */
					PGSetToDefault *n = makeNode(PGSetToDefault);
					/* parse analysis will fill in the rest */
					n->location = @1;
					$$ = (PGNode *)n;
				}
			| '*' COLUMNS '(' a_expr ')'
				{
					PGAStar *star = makeNode(PGAStar);
					star->expr = $4;
					star->columns = true;
					star->unpacked = true;
					star->location = @1;
					$$ = (PGNode *) star;
				}
			| COLUMNS '(' a_expr ')'
				{
					PGAStar *star = makeNode(PGAStar);
					star->expr = $3;
					star->columns = true;
					star->location = @1;
					$$ = (PGNode *) star;
				}
			| '*' opt_except_list opt_replace_list opt_rename_list
				{
					PGAStar *star = makeNode(PGAStar);
					star->except_list = $2;
					star->replace_list = $3;
					star->rename_list = $4;
					star->location = @1;
					$$ = (PGNode *) star;
				}
			| ColId '.' '*' opt_except_list opt_replace_list opt_rename_list
				{
					PGAStar *star = makeNode(PGAStar);
					star->relation = $1;
					star->except_list = $4;
					star->replace_list = $5;
					star->rename_list = $6;
					star->location = @1;
					$$ = (PGNode *) star;
				}
		;

/*
 * Restricted expressions
 *
 * b_expr is a subset of the complete expression syntax defined by a_expr.
 *
 * Presently, AND, NOT, IS, and IN are the a_expr keywords that would
 * cause trouble in the places where b_expr is used.  For simplicity, we
 * just eliminate all the boolean-keyword-operator productions from b_expr.
 */
b_expr:		c_expr
				{ $$ = $1; }
			| b_expr TYPECAST Typename
				{ $$ = makeTypeCast($1, $3, 0, @2); }
			| '+' b_expr					%prec UMINUS
				{ $$ = (PGNode *) makeSimpleAExpr(PG_AEXPR_OP, "+", NULL, $2, @1); }
			| '-' b_expr					%prec UMINUS
				{ $$ = doNegate($2, @1); }
			| b_expr '+' b_expr
				{ $$ = (PGNode *) makeSimpleAExpr(PG_AEXPR_OP, "+", $1, $3, @2); }
			| b_expr '-' b_expr
				{ $$ = (PGNode *) makeSimpleAExpr(PG_AEXPR_OP, "-", $1, $3, @2); }
			| b_expr '*' b_expr
				{ $$ = (PGNode *) makeSimpleAExpr(PG_AEXPR_OP, "*", $1, $3, @2); }
			| b_expr '/' b_expr
				{ $$ = (PGNode *) makeSimpleAExpr(PG_AEXPR_OP, "/", $1, $3, @2); }
			| b_expr INTEGER_DIVISION b_expr
				{ $$ = (PGNode *) makeSimpleAExpr(PG_AEXPR_OP, "//", $1, $3, @2); }
			| b_expr '%' b_expr
				{ $$ = (PGNode *) makeSimpleAExpr(PG_AEXPR_OP, "%", $1, $3, @2); }
			| b_expr '^' b_expr
				{ $$ = (PGNode *) makeSimpleAExpr(PG_AEXPR_OP, "^", $1, $3, @2); }
			| b_expr POWER_OF b_expr
				{ $$ = (PGNode *) makeSimpleAExpr(PG_AEXPR_OP, "**", $1, $3, @2); }
			| b_expr '<' b_expr
				{ $$ = (PGNode *) makeSimpleAExpr(PG_AEXPR_OP, "<", $1, $3, @2); }
			| b_expr '>' b_expr
				{ $$ = (PGNode *) makeSimpleAExpr(PG_AEXPR_OP, ">", $1, $3, @2); }
			| b_expr '=' b_expr
				{ $$ = (PGNode *) makeSimpleAExpr(PG_AEXPR_OP, "=", $1, $3, @2); }
			| b_expr LESS_EQUALS b_expr
				{ $$ = (PGNode *) makeSimpleAExpr(PG_AEXPR_OP, "<=", $1, $3, @2); }
			| b_expr GREATER_EQUALS b_expr
				{ $$ = (PGNode *) makeSimpleAExpr(PG_AEXPR_OP, ">=", $1, $3, @2); }
			| b_expr NOT_EQUALS b_expr
				{ $$ = (PGNode *) makeSimpleAExpr(PG_AEXPR_OP, "<>", $1, $3, @2); }
			| b_expr qual_Op b_expr				%prec Op
				{ $$ = (PGNode *) makeAExpr(PG_AEXPR_OP, $2, $1, $3, @2); }
			| qual_Op b_expr					%prec Op
				{ $$ = (PGNode *) makeAExpr(PG_AEXPR_OP, $1, NULL, $2, @1); }
			| b_expr qual_Op					%prec POSTFIXOP
				{ $$ = (PGNode *) makeAExpr(PG_AEXPR_OP, $2, $1, NULL, @2); }
			| b_expr IS DISTINCT FROM b_expr		%prec IS
				{
					$$ = (PGNode *) makeSimpleAExpr(PG_AEXPR_DISTINCT, "=", $1, $5, @2);
				}
			| b_expr IS NOT DISTINCT FROM b_expr	%prec IS
				{
					$$ = (PGNode *) makeSimpleAExpr(PG_AEXPR_NOT_DISTINCT, "=", $1, $6, @2);
				}
			| b_expr IS OF '(' type_list ')'		%prec IS
				{
					$$ = (PGNode *) makeSimpleAExpr(PG_AEXPR_OF, "=", $1, (PGNode *) $5, @2);
				}
			| b_expr IS NOT OF '(' type_list ')'	%prec IS
				{
					$$ = (PGNode *) makeSimpleAExpr(PG_AEXPR_OF, "<>", $1, (PGNode *) $6, @2);
				}
		;

/*
 * Productions that can be used in both a_expr and b_expr.
 *
 * Note: productions that refer recursively to a_expr or b_expr mostly
 * cannot appear here.	However, it's OK to refer to a_exprs that occur
 * inside parentheses, such as function arguments; that cannot introduce
 * ambiguity to the b_expr syntax.
 */
c_expr:		d_expr
			| indirection_expr_or_a_expr opt_extended_indirection
				{
					if ($2)
					{
						PGAIndirection *n = makeNode(PGAIndirection);
						n->arg = (PGNode *) $1;
						n->indirection = check_indirection($2, yyscanner);
						$$ = (PGNode *) n;
					}
					else
						$$ = (PGNode *) $1;
				}
		;

d_expr:		columnref_opt_indirection								{ $$ = $1; }
			| AexprConst							{ $$ = $1; }
			| select_with_parens			%prec UMINUS
				{
					PGSubLink *n = makeNode(PGSubLink);
					n->subLinkType = PG_EXPR_SUBLINK;
					n->subLinkId = 0;
					n->testexpr = NULL;
					n->operName = NIL;
					n->subselect = $1;
					n->location = @1;
					$$ = (PGNode *)n;
				}
			| select_with_parens indirection
				{
					/*
					 * Because the select_with_parens nonterminal is designed
					 * to "eat" as many levels of parens as possible, the
					 * '(' a_expr ')' opt_indirection production above will
					 * fail to match a sub-SELECT with indirection decoration;
					 * the sub-SELECT won't be regarded as an a_expr as long
					 * as there are parens around it.  To support applying
					 * subscripting or field selection to a sub-SELECT result,
					 * we need this redundant-looking production.
					 */
					PGSubLink *n = makeNode(PGSubLink);
					PGAIndirection *a = makeNode(PGAIndirection);
					n->subLinkType = PG_EXPR_SUBLINK;
					n->subLinkId = 0;
					n->testexpr = NULL;
					n->operName = NIL;
					n->subselect = $1;
					n->location = @1;
					a->arg = (PGNode *)n;
					a->indirection = check_indirection($2, yyscanner);
					$$ = (PGNode *)a;
				}
			| EXISTS select_with_parens
				{
					PGSubLink *n = makeNode(PGSubLink);
					n->subLinkType = PG_EXISTS_SUBLINK;
					n->subLinkId = 0;
					n->testexpr = NULL;
					n->operName = NIL;
					n->subselect = $2;
					n->location = @1;
					$$ = (PGNode *)n;
				}
			| grouping_or_grouping_id '(' expr_list_opt_comma ')'
			  {
				  PGGroupingFunc *g = makeNode(PGGroupingFunc);
				  g->args = $3;
				  g->location = @1;
				  $$ = (PGNode *)g;
			  }
		;

indirection_expr_or_a_expr:
			'(' a_expr ')'
				{
					$$ = $2;
				}
			| indirection_expr
				{
					$$ = $1;
				}
			| row {
				PGFuncCall *n = makeFuncCall(SystemFuncName("row"), $1, @1);
				$$ = (PGNode *) n;
			}
		;

param_expr:
			'?'
				{
					$$ = makeParamRef(0, @1);
				}
			| PARAM
				{
					PGParamRef *p = makeNode(PGParamRef);
					p->number = $1;
					p->location = @1;
					$$ = (PGNode *) p;
				}
			| '$' ColLabel
				{
					$$ = makeNamedParamRef($2, @1);
				}
			;

indirection_expr:
            param_expr
			| struct_expr
			| map_expr
			| func_expr
			| case_expr
			| list_expr
			| list_comprehension
			| ARRAY select_with_parens
				{
					PGSubLink *n = makeNode(PGSubLink);
					n->subLinkType = PG_ARRAY_SUBLINK;
					n->subLinkId = 0;
					n->testexpr = NULL;
					n->operName = NULL;
					n->subselect = $2;
					n->location = @2;
					$$ = (PGNode *)n;
				}
			| ARRAY '[' opt_expr_list_opt_comma ']' {
				PGList *func_name = list_make1(makeString("construct_array"));
				PGFuncCall *n = makeFuncCall(func_name, $3, @1);
				$$ = (PGNode *) n;
			}
			| '#' ICONST
				{
					PGPositionalReference *n = makeNode(PGPositionalReference);
					n->position = $2;
					n->location = @1;
					$$ = (PGNode *) n;
				}
		;

list_expr:  '[' opt_expr_list_opt_comma ']' {
                PGFuncCall *n = makeFuncCall(SystemFuncName("list_value"), $2, @2);
                $$ = (PGNode *) n;
            }
        ;

struct_expr:		'{' dict_arguments_opt_comma '}'
				{
					PGFuncCall *f = makeFuncCall(SystemFuncName("struct_pack"), $2, @2);
					$$ = (PGNode *) f;
				}
		;

map_expr:  MAP '{' opt_map_arguments_opt_comma '}'
                {
                    PGList *key_list = NULL;
                    PGList *value_list = NULL;
                    PGListCell *lc;
                    PGList *entry_list = $3;
                    foreach(lc, entry_list)
                    {
                        PGList *l = (PGList *) lc->data.ptr_value;
                        key_list = lappend(key_list, (PGNode *) l->head->data.ptr_value);
                        value_list = lappend(value_list, (PGNode *) l->tail->data.ptr_value);
                    }
                    PGNode *keys   = (PGNode *) makeFuncCall(SystemFuncName("list_value"), key_list, @3);
                    PGNode *values = (PGNode *) makeFuncCall(SystemFuncName("list_value"), value_list, @3);
                    PGFuncCall *f = makeFuncCall(SystemFuncName("map"), list_make2(keys, values), @3);
                    $$ = (PGNode *) f;
                }



func_application:       func_name '(' ')'
				{
					$$ = (PGNode *) makeFuncCall($1, NIL, @1);
				}
			| func_name '(' sort_clause opt_ignore_nulls ')'
				{
					PGFuncCall *n = makeFuncCall($1, NIL, @1);
					n->agg_order = $3;
					n->agg_ignore_nulls = $4;
					$$ = (PGNode *)n;
				}
			| func_name '(' func_arg_list opt_sort_clause opt_ignore_nulls ')'
				{
					PGFuncCall *n = makeFuncCall($1, $3, @1);
					n->agg_order = $4;
					n->agg_ignore_nulls = $5;
					$$ = (PGNode *)n;
				}
			| func_name '(' VARIADIC func_arg_expr opt_sort_clause opt_ignore_nulls ')'
				{
					PGFuncCall *n = makeFuncCall($1, list_make1($4), @1);
					n->func_variadic = true;
					n->agg_order = $5;
					n->agg_ignore_nulls = $6;
					$$ = (PGNode *)n;
				}
			| func_name '(' func_arg_list ',' VARIADIC func_arg_expr opt_sort_clause opt_ignore_nulls ')'
				{
					PGFuncCall *n = makeFuncCall($1, lappend($3, $6), @1);
					n->func_variadic = true;
					n->agg_order = $7;
					n->agg_ignore_nulls = $8;
					$$ = (PGNode *)n;
				}
			| func_name '(' ALL func_arg_list opt_sort_clause opt_ignore_nulls ')'
				{
					PGFuncCall *n = makeFuncCall($1, $4, @1);
					n->agg_order = $5;
					n->agg_ignore_nulls = $6;
					/* Ideally we'd mark the PGFuncCall node to indicate
					 * "must be an aggregate", but there's no provision
					 * for that in PGFuncCall at the moment.
					 */
					$$ = (PGNode *)n;
				}
			| func_name '(' DISTINCT func_arg_list opt_sort_clause opt_ignore_nulls ')'
				{
					PGFuncCall *n = makeFuncCall($1, $4, @1);
					n->agg_order = $5;
					n->agg_ignore_nulls = $6;
					n->agg_distinct = true;
					$$ = (PGNode *)n;
				}
		;


/*
 * func_expr and its cousin func_expr_windowless are split out from c_expr just
 * so that we have classifications for "everything that is a function call or
 * looks like one".  This isn't very important, but it saves us having to
 * document which variants are legal in places like "FROM function()" or the
 * backwards-compatible functional-index syntax for CREATE INDEX.
 * (Note that many of the special SQL functions wouldn't actually make any
 * sense as functional index entries, but we ignore that consideration here.)
 */
func_expr: func_application within_group_clause filter_clause export_clause over_clause
				{
					PGFuncCall *n = (PGFuncCall *) $1;
					/*
					 * The order clause for WITHIN GROUP and the one for
					 * plain-aggregate ORDER BY share a field, so we have to
					 * check here that at most one is present.  We also check
					 * for DISTINCT and VARIADIC here to give a better error
					 * location.  Other consistency checks are deferred to
					 * parse analysis.
					 */
					if ($2 != NIL)
					{
						if (n->agg_order != NIL)
							ereport(ERROR,
									(errcode(PG_ERRCODE_SYNTAX_ERROR),
									 errmsg("cannot use multiple ORDER BY clauses with WITHIN GROUP"),
									 parser_errposition(@2)));
						if (n->agg_distinct)
							ereport(ERROR,
									(errcode(PG_ERRCODE_SYNTAX_ERROR),
									 errmsg("cannot use DISTINCT with WITHIN GROUP"),
									 parser_errposition(@2)));
						if (n->func_variadic)
							ereport(ERROR,
									(errcode(PG_ERRCODE_SYNTAX_ERROR),
									 errmsg("cannot use VARIADIC with WITHIN GROUP"),
									 parser_errposition(@2)));
						n->agg_order = $2;
						n->agg_within_group = true;
					}
					n->agg_filter = $3;
					n->export_state = $4;
					n->over = $5;
					$$ = (PGNode *) n;
				}
			| func_expr_common_subexpr
				{ $$ = $1; }
		;

/*
 * As func_expr but does not accept WINDOW functions directly
 * (but they can still be contained in arguments for functions etc).
 * Use this when window expressions are not allowed, where needed to
 * disambiguate the grammar (e.g. in CREATE INDEX).
 */
func_expr_windowless:
			func_application						{ $$ = $1; }
			| func_expr_common_subexpr				{ $$ = $1; }
		;

/*
 * Special expressions that are considered to be functions.
 */
func_expr_common_subexpr:
			COLLATION FOR '(' a_expr ')'
				{
					$$ = (PGNode *) makeFuncCall(SystemFuncName("pg_collation_for"),
											   list_make1($4),
											   @1);
				}
			| CAST '(' a_expr AS Typename ')'
				{ $$ = makeTypeCast($3, $5, 0, @1); }
			| TRY_CAST '(' a_expr AS Typename ')'
				{ $$ = makeTypeCast($3, $5, 1, @1); }
			| EXTRACT '(' extract_list ')'
				{
					$$ = (PGNode *) makeFuncCall(SystemFuncName("date_part"), $3, @1);
				}
			| OVERLAY '(' overlay_list ')'
				{
					/* overlay(A PLACING B FROM C FOR D) is converted to
					 * overlay(A, B, C, D)
					 * overlay(A PLACING B FROM C) is converted to
					 * overlay(A, B, C)
					 */
					$$ = (PGNode *) makeFuncCall(SystemFuncName("overlay"), $3, @1);
				}
			| POSITION '(' position_list ')'
				{
					/* position(A in B) is converted to position_inverse(A, B) */
					$$ = (PGNode *) makeFuncCall(SystemFuncName("__internal_position_operator"), $3, @1);
				}
			| SUBSTRING '(' substr_list ')'
				{
					/* substring(A from B for C) is converted to
					 * substring(A, B, C) - thomas 2000-11-28
					 */
					$$ = (PGNode *) makeFuncCall(SystemFuncName("substring"), $3, @1);
				}
			| TREAT '(' a_expr AS Typename ')'
				{
					/* TREAT(expr AS target) converts expr of a particular type to target,
					 * which is defined to be a subtype of the original expression.
					 * In SQL99, this is intended for use with structured UDTs,
					 * but let's make this a generally useful form allowing stronger
					 * coercions than are handled by implicit casting.
					 *
					 * Convert SystemTypeName() to SystemFuncName() even though
					 * at the moment they result in the same thing.
					 */
					$$ = (PGNode *) makeFuncCall(SystemFuncName(((PGValue *)llast($5->names))->val.str),
												list_make1($3),
												@1);
				}
			| TRIM '(' BOTH trim_list ')'
				{
					/* various trim expressions are defined in SQL
					 * - thomas 1997-07-19
					 */
					$$ = (PGNode *) makeFuncCall(SystemFuncName("trim"), $4, @1);
				}
			| TRIM '(' LEADING trim_list ')'
				{
					$$ = (PGNode *) makeFuncCall(SystemFuncName("ltrim"), $4, @1);
				}
			| TRIM '(' TRAILING trim_list ')'
				{
					$$ = (PGNode *) makeFuncCall(SystemFuncName("rtrim"), $4, @1);
				}
			| TRIM '(' trim_list ')'
				{
					$$ = (PGNode *) makeFuncCall(SystemFuncName("trim"), $3, @1);
				}
			| NULLIF '(' a_expr ',' a_expr ')'
				{
					$$ = (PGNode *) makeSimpleAExpr(PG_AEXPR_NULLIF, "=", $3, $5, @1);
				}
			| COALESCE '(' expr_list_opt_comma ')'
				{
					PGCoalesceExpr *c = makeNode(PGCoalesceExpr);
					c->args = $3;
					c->location = @1;
					$$ = (PGNode *)c;
				}
		;

list_comprehension_lhs:
		columnrefList
		{
			PGFuncCall *n = makeFuncCall(SystemFuncName("row"), $1, @1);
			$$ = (PGNode *) n;
		}
	;

list_comprehension:
				'[' a_expr FOR list_comprehension_lhs IN_P a_expr ']'
				{
					PGLambdaFunction *lambda = makeNode(PGLambdaFunction);
					lambda->lhs = $4;
					lambda->rhs = $2;
					lambda->location = @1;
					PGFuncCall *n = makeFuncCall(SystemFuncName("list_apply"), list_make2($6, lambda), @1);
					$$ = (PGNode *) n;
				}
				| '[' a_expr FOR list_comprehension_lhs IN_P c_expr IF_P a_expr']'
				{
					PGLambdaFunction *lambda = makeNode(PGLambdaFunction);
					lambda->lhs = $4;
					lambda->rhs = $2;
					lambda->location = @1;

					PGLambdaFunction *lambda_filter = makeNode(PGLambdaFunction);
					lambda_filter->lhs = $4;
					lambda_filter->rhs = $8;
					lambda_filter->location = @8;
					PGFuncCall *filter = makeFuncCall(SystemFuncName("list_filter"), list_make2($6, lambda_filter), @1);
					PGFuncCall *n = makeFuncCall(SystemFuncName("list_apply"), list_make2(filter, lambda), @1);
					$$ = (PGNode *) n;
				}
			;

/* We allow several variants for SQL and other compatibility. */
/*
 * Aggregate decoration clauses
 */
within_group_clause:
			WITHIN GROUP_P '(' sort_clause ')'		{ $$ = $4; }
			| /*EMPTY*/								{ $$ = NIL; }
		;

filter_clause:
			FILTER '(' WHERE a_expr ')'				{ $$ = $4; }
			| FILTER '(' a_expr ')'					{ $$ = $3; }
			| /*EMPTY*/								{ $$ = NULL; }
		;

export_clause:
			EXPORT_STATE            				{ $$ = true; }
			| /*EMPTY*/								{ $$ = false; }
		;

/*
 * Window Definitions
 */
window_clause:
			WINDOW window_definition_list			{ $$ = $2; }
			| /*EMPTY*/								{ $$ = NIL; }
		;

window_definition_list:
			window_definition						{ $$ = list_make1($1); }
			| window_definition_list ',' window_definition
													{ $$ = lappend($1, $3); }
		;

window_definition:
			ColId AS window_specification
				{
					PGWindowDef *n = $3;
					n->name = $1;
					$$ = n;
				}
		;

over_clause: OVER window_specification
				{ $$ = $2; }
			| OVER ColId
				{
					PGWindowDef *n = makeNode(PGWindowDef);
					n->name = $2;
					n->refname = NULL;
					n->partitionClause = NIL;
					n->orderClause = NIL;
					n->frameOptions = FRAMEOPTION_DEFAULTS;
					n->startOffset = NULL;
					n->endOffset = NULL;
					n->location = @2;
					$$ = n;
				}
			| /*EMPTY*/
				{ $$ = NULL; }
		;

window_specification: '(' opt_existing_window_name opt_partition_clause
						opt_sort_clause opt_frame_clause ')'
				{
					PGWindowDef *n = makeNode(PGWindowDef);
					n->name = NULL;
					n->refname = $2;
					n->partitionClause = $3;
					n->orderClause = $4;
					/* copy relevant fields of opt_frame_clause */
					n->frameOptions = $5->frameOptions;
					n->startOffset = $5->startOffset;
					n->endOffset = $5->endOffset;
					n->location = @1;
					$$ = n;
				}
		;

/*
 * If we see PARTITION, RANGE, ROWS or GROUPS as the first token after the '('
 * of a window_specification, we want the assumption to be that there is
 * no existing_window_name; but those keywords are unreserved and so could
 * be ColIds.  We fix this by making them have the same precedence as IDENT
 * and giving the empty production here a slightly higher precedence, so
 * that the shift/reduce conflict is resolved in favor of reducing the rule.
 * These keywords are thus precluded from being an existing_window_name but
 * are not reserved for any other purpose.
 */
opt_existing_window_name: ColId						{ $$ = $1; }
			| /*EMPTY*/				%prec Op		{ $$ = NULL; }
		;

opt_partition_clause: PARTITION BY expr_list		{ $$ = $3; }
			| /*EMPTY*/								{ $$ = NIL; }
		;

/*
 * For frame clauses, we return a PGWindowDef, but only some fields are used:
 * frameOptions, startOffset, and endOffset.
 */
opt_frame_clause:
			RANGE frame_extent opt_window_exclusion_clause
				{
					PGWindowDef *n = $2;

					n->frameOptions |= FRAMEOPTION_NONDEFAULT | FRAMEOPTION_RANGE;
					n->frameOptions |= $3;
					$$ = n;
				}
			| ROWS frame_extent opt_window_exclusion_clause
				{
					PGWindowDef *n = $2;

					n->frameOptions |= FRAMEOPTION_NONDEFAULT | FRAMEOPTION_ROWS;
					n->frameOptions |= $3;
					$$ = n;
				}
			| GROUPS frame_extent opt_window_exclusion_clause
				{
					PGWindowDef *n = $2;

					n->frameOptions |= FRAMEOPTION_NONDEFAULT | FRAMEOPTION_GROUPS;
					n->frameOptions |= $3;
					$$ = n;
				}
			| /*EMPTY*/
				{
					PGWindowDef *n = makeNode(PGWindowDef);

					n->frameOptions = FRAMEOPTION_DEFAULTS;
					n->startOffset = NULL;
					n->endOffset = NULL;
					$$ = n;
				}
		;

frame_extent: frame_bound
				{
					PGWindowDef *n = $1;

					/* reject invalid cases */
					if (n->frameOptions & FRAMEOPTION_START_UNBOUNDED_FOLLOWING)
						ereport(ERROR,
								(errcode(PG_ERRCODE_WINDOWING_ERROR),
								 errmsg("frame start cannot be UNBOUNDED FOLLOWING"),
								 parser_errposition(@1)));
					if (n->frameOptions & FRAMEOPTION_START_OFFSET_FOLLOWING)
						ereport(ERROR,
								(errcode(PG_ERRCODE_WINDOWING_ERROR),
								 errmsg("frame starting from following row cannot end with current row"),
								 parser_errposition(@1)));
					n->frameOptions |= FRAMEOPTION_END_CURRENT_ROW;
					$$ = n;
				}
			| BETWEEN frame_bound AND frame_bound
				{
					PGWindowDef *n1 = $2;
					PGWindowDef *n2 = $4;

					/* form merged options */
					int		frameOptions = n1->frameOptions;
					/* shift converts START_ options to END_ options */
					frameOptions |= n2->frameOptions << 1;
					frameOptions |= FRAMEOPTION_BETWEEN;
					/* reject invalid cases */
					if (frameOptions & FRAMEOPTION_START_UNBOUNDED_FOLLOWING)
						ereport(ERROR,
								(errcode(PG_ERRCODE_WINDOWING_ERROR),
								 errmsg("frame start cannot be UNBOUNDED FOLLOWING"),
								 parser_errposition(@2)));
					if (frameOptions & FRAMEOPTION_END_UNBOUNDED_PRECEDING)
						ereport(ERROR,
								(errcode(PG_ERRCODE_WINDOWING_ERROR),
								 errmsg("frame end cannot be UNBOUNDED PRECEDING"),
								 parser_errposition(@4)));
					if ((frameOptions & FRAMEOPTION_START_CURRENT_ROW) &&
						(frameOptions & FRAMEOPTION_END_OFFSET_PRECEDING))
						ereport(ERROR,
								(errcode(PG_ERRCODE_WINDOWING_ERROR),
								 errmsg("frame starting from current row cannot have preceding rows"),
								 parser_errposition(@4)));
					if ((frameOptions & FRAMEOPTION_START_OFFSET_FOLLOWING) &&
						(frameOptions & (FRAMEOPTION_END_OFFSET_PRECEDING |
										 FRAMEOPTION_END_CURRENT_ROW)))
						ereport(ERROR,
								(errcode(PG_ERRCODE_WINDOWING_ERROR),
								 errmsg("frame starting from following row cannot have preceding rows"),
								 parser_errposition(@4)));
					n1->frameOptions = frameOptions;
					n1->endOffset = n2->startOffset;
					$$ = n1;
				}
		;

/*
 * This is used for both frame start and frame end, with output set up on
 * the assumption it's frame start; the frame_extent productions must reject
 * invalid cases.
 */
frame_bound:
			UNBOUNDED PRECEDING
				{
					PGWindowDef *n = makeNode(PGWindowDef);

					n->frameOptions = FRAMEOPTION_START_UNBOUNDED_PRECEDING;
					n->startOffset = NULL;
					n->endOffset = NULL;
					$$ = n;
				}
			| UNBOUNDED FOLLOWING
				{
					PGWindowDef *n = makeNode(PGWindowDef);

					n->frameOptions = FRAMEOPTION_START_UNBOUNDED_FOLLOWING;
					n->startOffset = NULL;
					n->endOffset = NULL;
					$$ = n;
				}
			| CURRENT_P ROW
				{
					PGWindowDef *n = makeNode(PGWindowDef);

					n->frameOptions = FRAMEOPTION_START_CURRENT_ROW;
					n->startOffset = NULL;
					n->endOffset = NULL;
					$$ = n;
				}
			| a_expr PRECEDING
				{
					PGWindowDef *n = makeNode(PGWindowDef);

					n->frameOptions = FRAMEOPTION_START_OFFSET_PRECEDING;
					n->startOffset = $1;
					n->endOffset = NULL;
					$$ = n;
				}
			| a_expr FOLLOWING
				{
					PGWindowDef *n = makeNode(PGWindowDef);

					n->frameOptions = FRAMEOPTION_START_OFFSET_FOLLOWING;
					n->startOffset = $1;
					n->endOffset = NULL;
					$$ = n;
				}
		;

opt_window_exclusion_clause:
			EXCLUDE CURRENT_P ROW	{ $$ = FRAMEOPTION_EXCLUDE_CURRENT_ROW; }
			| EXCLUDE GROUP_P		{ $$ = FRAMEOPTION_EXCLUDE_GROUP; }
			| EXCLUDE TIES			{ $$ = FRAMEOPTION_EXCLUDE_TIES; }
			| EXCLUDE NO OTHERS		{ $$ = 0; }
			| /*EMPTY*/				{ $$ = 0; }
		;


/*
 * Supporting nonterminals for expressions.
 */

/* Explicit row production.
 *
 * SQL99 allows an optional ROW keyword, so we can now do single-element rows
 * without conflicting with the parenthesized a_expr production.  Without the
 * ROW keyword, there must be more than one a_expr inside the parens.
 */
qualified_row:	ROW '(' expr_list_opt_comma ')'					{ $$ = $3; }
			| ROW '(' ')'							{ $$ = NIL; }
		;

row:		qualified_row							{ $$ = $1;}
			| '(' expr_list ',' a_expr ')'			{ $$ = lappend($2, $4); }
		;

dict_arg:
	ColIdOrString ':' a_expr						{
		PGNamedArgExpr *na = makeNode(PGNamedArgExpr);
		na->name = $1;
		na->arg = (PGExpr *) $3;
		na->argnumber = -1;
		na->location = @1;
		$$ = (PGNode *) na;
	}

dict_arguments:
	dict_arg						{ $$ = list_make1($1); }
	| dict_arguments ',' dict_arg	{ $$ = lappend($1, $3); }


dict_arguments_opt_comma:
			dict_arguments								{ $$ = $1; }
			| dict_arguments ','							{ $$ = $1; }
		;

map_arg:
			a_expr ':' a_expr
			{
				$$ = list_make2($1, $3);
			}
	;

map_arguments:
			map_arg									{ $$ = list_make1($1); }
			| map_arguments ',' map_arg				{ $$ = lappend($1, $3); }
	;


map_arguments_opt_comma:
			map_arguments							{ $$ = $1; }
			| map_arguments ','						{ $$ = $1; }
		;


opt_map_arguments_opt_comma:
			map_arguments_opt_comma					{ $$ = $1; }
			| /* empty */							{ $$ = NULL; }
		;

sub_type:	ANY										{ $$ = PG_ANY_SUBLINK; }
			| SOME									{ $$ = PG_ANY_SUBLINK; }
			| ALL									{ $$ = PG_ALL_SUBLINK; }
		;

all_Op:		Op										{ $$ = $1; }
			| MathOp								{ $$ = (char*) $1; }
		;

MathOp:		 '+'									{ $$ = "+"; }
			| '-'									{ $$ = "-"; }
			| '*'									{ $$ = "*"; }
			| '/'									{ $$ = "/"; }
			| INTEGER_DIVISION						{ $$ = "//"; }
			| '%'									{ $$ = "%"; }
			| '^'									{ $$ = "^"; }
			| POWER_OF								{ $$ = "**"; }
			| '<'									{ $$ = "<"; }
			| '>'									{ $$ = ">"; }
			| '='									{ $$ = "="; }
			| LESS_EQUALS							{ $$ = "<="; }
			| GREATER_EQUALS						{ $$ = ">="; }
			| NOT_EQUALS							{ $$ = "<>"; }
		;

qual_Op:	Op
					{ $$ = list_make1(makeString($1)); }
			| OPERATOR '(' any_operator ')'
					{ $$ = $3; }
		;

qual_all_Op:
			all_Op
					{ $$ = list_make1(makeString($1)); }
			| OPERATOR '(' any_operator ')'
					{ $$ = $3; }
		;

subquery_Op:
			all_Op
					{ $$ = list_make1(makeString($1)); }
			| OPERATOR '(' any_operator ')'
					{ $$ = $3; }
			| LIKE
					{ $$ = list_make1(makeString("~~")); }
			| NOT_LA LIKE
					{ $$ = list_make1(makeString("!~~")); }
			| GLOB
					{ $$ = list_make1(makeString("~~~")); }
			| NOT_LA GLOB
					{ $$ = list_make1(makeString("!~~~")); }
			| ILIKE
					{ $$ = list_make1(makeString("~~*")); }
			| NOT_LA ILIKE
					{ $$ = list_make1(makeString("!~~*")); }
/* cannot put SIMILAR TO here, because SIMILAR TO is a hack.
 * the regular expression is preprocessed by a function (similar_escape),
 * and the ~ operator for posix regular expressions is used.
 *        x SIMILAR TO y     ->    x ~ similar_escape(y)
 * this transformation is made on the fly by the parser upwards.
 * however the PGSubLink structure which handles any/some/all stuff
 * is not ready for such a thing.
 */
			;


any_operator:
			all_Op
					{ $$ = list_make1(makeString($1)); }
			| ColId '.' any_operator
					{ $$ = lcons(makeString($1), $3); }
		;

c_expr_list:
			c_expr
				{
					$$ = list_make1($1);
				}
			| c_expr_list ',' c_expr
				{
					$$ = lappend($1, $3);
				}
		;

c_expr_list_opt_comma:
			c_expr_list
				{
					$$ = $1;
				}
			|
			c_expr_list ','
				{
					$$ = $1;
				}
		;

expr_list:	a_expr
				{
					$$ = list_make1($1);
				}
			| expr_list ',' a_expr
				{
					$$ = lappend($1, $3);
				}
		;

expr_list_opt_comma:
			expr_list
				{
					$$ = $1;
				}
			|
			expr_list ','
				{
					$$ = $1;
				}
		;

opt_expr_list_opt_comma:
			expr_list_opt_comma
				{
					$$ = $1;
				}
			| /* empty */
				{
					$$ = NULL;
				}
		;



/* function arguments can have names */
func_arg_list:  func_arg_expr
				{
					$$ = list_make1($1);
				}
			| func_arg_list ',' func_arg_expr
				{
					$$ = lappend($1, $3);
				}
		;

func_arg_expr:  a_expr
				{
					$$ = $1;
				}
			| param_name COLON_EQUALS a_expr
				{
					PGNamedArgExpr *na = makeNode(PGNamedArgExpr);
					na->name = $1;
					na->arg = (PGExpr *) $3;
					na->argnumber = -1;		/* until determined */
					na->location = @1;
					$$ = (PGNode *) na;
				}
			| param_name EQUALS_GREATER a_expr
				{
					PGNamedArgExpr *na = makeNode(PGNamedArgExpr);
					na->name = $1;
					na->arg = (PGExpr *) $3;
					na->argnumber = -1;		/* until determined */
					na->location = @1;
					$$ = (PGNode *) na;
				}
		;

type_list:	Typename								{ $$ = list_make1($1); }
			| type_list ',' Typename				{ $$ = lappend($1, $3); }
		;

extract_list:
			extract_arg FROM a_expr
				{
					$$ = list_make2(makeStringConst($1, @1), $3);
				}
			| /*EMPTY*/								{ $$ = NIL; }
		;

/* Allow delimited string Sconst in extract_arg as an SQL extension.
 * - thomas 2001-04-12
 */
extract_arg:
			IDENT											{ $$ = $1; }
			| year_keyword									{ $$ = (char*) "year"; }
			| month_keyword									{ $$ = (char*) "month"; }
			| day_keyword									{ $$ = (char*) "day"; }
			| hour_keyword									{ $$ = (char*) "hour"; }
			| minute_keyword								{ $$ = (char*) "minute"; }
			| second_keyword								{ $$ = (char*) "second"; }
			| millisecond_keyword							{ $$ = (char*) "millisecond"; }
			| microsecond_keyword							{ $$ = (char*) "microsecond"; }
			| week_keyword									{ $$ = (char*) "week"; }
			| quarter_keyword								{ $$ = (char*) "quarter"; }
			| decade_keyword								{ $$ = (char*) "decade"; }
			| century_keyword								{ $$ = (char*) "century"; }
			| millennium_keyword							{ $$ = (char*) "millennium"; }
			| Sconst										{ $$ = $1; }
		;

/* OVERLAY() arguments
 * SQL99 defines the OVERLAY() function:
 * o overlay(text placing text from int for int)
 * o overlay(text placing text from int)
 * and similarly for binary strings
 */
overlay_list:
			a_expr overlay_placing substr_from substr_for
				{
					$$ = list_make4($1, $2, $3, $4);
				}
			| a_expr overlay_placing substr_from
				{
					$$ = list_make3($1, $2, $3);
				}
		;

overlay_placing:
			PLACING a_expr
				{ $$ = $2; }
		;

/* position_list uses b_expr not a_expr to avoid conflict with general IN */

position_list:
			b_expr IN_P b_expr						{ $$ = list_make2($1, $3); }
			| /*EMPTY*/								{ $$ = NIL; }
		;

/* SUBSTRING() arguments
 * SQL9x defines a specific syntax for arguments to SUBSTRING():
 * o substring(text from int for int)
 * o substring(text from int) get entire string from starting point "int"
 * o substring(text for int) get first "int" characters of string
 * o substring(text from pattern) get entire string matching pattern
 * o substring(text from pattern for escape) same with specified escape char
 * We also want to support generic substring functions which accept
 * the usual generic list of arguments. So we will accept both styles
 * here, and convert the SQL9x style to the generic list for further
 * processing. - thomas 2000-11-28
 */
substr_list:
			a_expr substr_from substr_for
				{
					$$ = list_make3($1, $2, $3);
				}
			| a_expr substr_for substr_from
				{
					/* not legal per SQL99, but might as well allow it */
					$$ = list_make3($1, $3, $2);
				}
			| a_expr substr_from
				{
					$$ = list_make2($1, $2);
				}
			| a_expr substr_for
				{
					/*
					 * Since there are no cases where this syntax allows
					 * a textual FOR value, we forcibly cast the argument
					 * to int4.  The possible matches in pg_proc are
					 * substring(text,int4) and substring(text,text),
					 * and we don't want the parser to choose the latter,
					 * which it is likely to do if the second argument
					 * is unknown or doesn't have an implicit cast to int4.
					 */
					$$ = list_make3($1, makeIntConst(1, -1),
									makeTypeCast($2,
												 SystemTypeName("int4"), 0, -1));
				}
			| expr_list
				{
					$$ = $1;
				}
			| /*EMPTY*/
				{ $$ = NIL; }
		;

substr_from:
			FROM a_expr								{ $$ = $2; }
		;

substr_for: FOR a_expr								{ $$ = $2; }
		;

trim_list:	a_expr FROM expr_list_opt_comma					{ $$ = lappend($3, $1); }
			| FROM expr_list_opt_comma						{ $$ = $2; }
			| expr_list_opt_comma								{ $$ = $1; }
		;

in_expr:	select_with_parens
				{
					PGSubLink *n = makeNode(PGSubLink);
					n->subselect = $1;
					/* other fields will be filled later */
					$$ = (PGNode *)n;
				}
			| '(' expr_list_opt_comma ')'						{ $$ = (PGNode *)$2; }
			| columnref_opt_indirection
			| indirection_expr { $$ = (PGNode *)$1; }
		;

/*
 * Define SQL-style CASE clause.
 * - Full specification
 *	CASE WHEN a = b THEN c ... ELSE d END
 * - Implicit argument
 *	CASE a WHEN b THEN c ... ELSE d END
 */
case_expr:	CASE case_arg when_clause_list case_default END_P
				{
					PGCaseExpr *c = makeNode(PGCaseExpr);
					c->casetype = InvalidOid; /* not analyzed yet */
					c->arg = (PGExpr *) $2;
					c->args = $3;
					c->defresult = (PGExpr *) $4;
					c->location = @1;
					$$ = (PGNode *)c;
				}
		;

when_clause_list:
			/* There must be at least one */
			when_clause								{ $$ = list_make1($1); }
			| when_clause_list when_clause			{ $$ = lappend($1, $2); }
		;

when_clause:
			WHEN a_expr THEN a_expr
				{
					PGCaseWhen *w = makeNode(PGCaseWhen);
					w->expr = (PGExpr *) $2;
					w->result = (PGExpr *) $4;
					w->location = @1;
					$$ = (PGNode *)w;
				}
		;

case_default:
			ELSE a_expr								{ $$ = $2; }
			| /*EMPTY*/								{ $$ = NULL; }
		;

case_arg:	a_expr									{ $$ = $1; }
			| /*EMPTY*/								{ $$ = NULL; }
		;

columnrefList:
			columnref								{ $$ = list_make1($1); }
			| columnrefList ',' columnref				{ $$ = lappend($1, $3); }
		;

columnref: ColId
		{
			$$ = makeColumnRef($1, NIL, @1, yyscanner);
		}
	;

columnref_opt_indirection:	ColId
				{
					$$ = makeColumnRef($1, NIL, @1, yyscanner);
				}
			| ColId indirection
				{
					$$ = makeColumnRef($1, $2, @1, yyscanner);
				}
		;

indirection_el:
			'[' a_expr ']'
				{
					PGAIndices *ai = makeNode(PGAIndices);
					ai->is_slice = false;
					ai->lidx = NULL;
					ai->uidx = $2;
					$$ = (PGNode *) ai;
				}
			| '[' opt_slice_bound ':' opt_slice_bound ']'
				{
					PGAIndices *ai = makeNode(PGAIndices);
					ai->is_slice = true;
					ai->lidx = $2;
					ai->uidx = $4;
					$$ = (PGNode *) ai;
				}
			| '[' opt_slice_bound ':' opt_slice_bound ':' opt_slice_bound ']' {
				    	PGAIndices *ai = makeNode(PGAIndices);
				    	ai->is_slice = true;
				    	ai->lidx = $2;
				    	ai->uidx = $4;
				    	ai->step = $6;
				    	$$ = (PGNode *) ai;
				}
			| '[' opt_slice_bound ':' '-' ':' opt_slice_bound ']' {
					PGAIndices *ai = makeNode(PGAIndices);
					ai->is_slice = true;
					ai->lidx = $2;
					ai->step = $6;
					$$ = (PGNode *) ai;
				}
				;

opt_slice_bound:
			a_expr									{ $$ = $1; }
			| /*EMPTY*/								{ $$ = NULL; }
		;


opt_indirection:
			/*EMPTY*/								{ $$ = NIL; }
			| opt_indirection indirection_el		{ $$ = lappend($1, $2); }
		;

opt_func_arguments:
	/* empty */ 				{ $$ = NULL; }
	| '(' ')'					{ $$ = list_make1(NULL); }
	| '(' func_arg_list ')' 	{ $$ = $2; }
	;

extended_indirection_el:
			'.' attr_name opt_func_arguments
				{
					if ($3) {
						PGFuncCall *n = makeFuncCall(list_make1(makeString($2)), $3->head->data.ptr_value ? $3 : NULL, @2);
						$$ = (PGNode *) n;
					} else {
						$$ = (PGNode *) makeString($2);
					}
				}
			| '[' a_expr ']'
				{
					PGAIndices *ai = makeNode(PGAIndices);
					ai->is_slice = false;
					ai->lidx = NULL;
					ai->uidx = $2;
					$$ = (PGNode *) ai;
				}
			| '[' opt_slice_bound ':' opt_slice_bound ']'
				{
					PGAIndices *ai = makeNode(PGAIndices);
					ai->is_slice = true;
					ai->lidx = $2;
					ai->uidx = $4;
					$$ = (PGNode *) ai;
				}
		    	| '[' opt_slice_bound ':' opt_slice_bound ':' opt_slice_bound ']' {
					PGAIndices *ai = makeNode(PGAIndices);
					ai->is_slice = true;
					ai->lidx = $2;
					ai->uidx = $4;
					ai->step = $6;
                 			$$ = (PGNode *) ai;
                		}

			| '[' opt_slice_bound ':' '-' ':' opt_slice_bound ']' {
					PGAIndices *ai = makeNode(PGAIndices);
					ai->is_slice = true;
					ai->lidx = $2;
					ai->step = $6;
					$$ = (PGNode *) ai;
				}
		;

extended_indirection:
			extended_indirection_el									{ $$ = list_make1($1); }
			| extended_indirection extended_indirection_el			{ $$ = lappend($1, $2); }
		;

opt_extended_indirection:
			/*EMPTY*/												{ $$ = NIL; }
			| opt_extended_indirection extended_indirection_el		{ $$ = lappend($1, $2); }
		;



opt_asymmetric: ASYMMETRIC
			| /*EMPTY*/
		;


/*****************************************************************************
 *
 *	target list for SELECT
 *
 *****************************************************************************/

opt_target_list_opt_comma: target_list_opt_comma						{ $$ = $1; }
			| /* EMPTY */							{ $$ = NIL; }
		;

target_list:
			target_el								{ $$ = list_make1($1); }
			| target_list ',' target_el				{ $$ = lappend($1, $3); }
		;

target_list_opt_comma:
			target_list								{ $$ = $1; }
			| target_list ','						{ $$ = $1; }
		;

target_el:	a_expr AS ColLabelOrString
				{
					$$ = makeNode(PGResTarget);
					$$->name = $3;
					$$->indirection = NIL;
					$$->val = (PGNode *)$1;
					$$->location = @1;
				}
			/*
			 * We support omitting AS only for column labels that aren't
			 * any known keyword.  There is an ambiguity against postfix
			 * operators: is "a ! b" an infix expression, or a postfix
			 * expression and a column label?  We prefer to resolve this
			 * as an infix expression, which we accomplish by assigning
			 * IDENT a precedence higher than POSTFIXOP.
			 */
			| a_expr IDENT
				{
					$$ = makeNode(PGResTarget);
					$$->name = $2;
					$$->indirection = NIL;
					$$->val = (PGNode *)$1;
					$$->location = @1;
				}
			| a_expr
				{
					$$ = makeNode(PGResTarget);
					$$->name = NULL;
					$$->indirection = NIL;
					$$->val = (PGNode *)$1;
					$$->location = @1;
				}
            | ColId ':' a_expr
				{
					$$ = makeNode(PGResTarget);
					$$->name = $1;
					$$->indirection = NIL;
					$$->val = (PGNode *)$3;
					$$->location = @1;
				}
		;

except_list: EXCLUDE '(' except_name_list_opt_comma ')'					{ $$ = $3; }
			| EXCLUDE except_name										{ $$ = list_make1($2); }
		;

except_name:
		ColIdOrString
			{
				$$ = list_make1($1);
			}
		| except_name '.' ColIdOrString
			{
				$$ = lappend($1, $3);
			}
		;

except_name_list:	except_name
					{ $$ = list_make1($1); }
			| except_name_list ',' except_name
					{ $$ = lappend($1, $3); }
		;

except_name_list_opt_comma:
			except_name_list								{ $$ = $1; }
			| except_name_list ','							{ $$ = $1; }
		;


opt_except_list: except_list						{ $$ = $1; }
			| /*EMPTY*/								{ $$ = NULL; }
		;

replace_list_el: a_expr AS ColId					{ $$ = list_make2($1, makeString($3)); }
		;

replace_list:
			replace_list_el							{ $$ = list_make1($1); }
			| replace_list ',' replace_list_el		{ $$ = lappend($1, $3); }
		;

replace_list_opt_comma:
			replace_list								{ $$ = $1; }
			| replace_list ','							{ $$ = $1; }
		;

opt_replace_list: REPLACE '(' replace_list_opt_comma ')'		{ $$ = $3; }
			| REPLACE replace_list_el				{ $$ = list_make1($2); }
			| /*EMPTY*/								{ $$ = NULL; }
		;

rename_list_el: except_name AS ColId						{ $$ = list_make2($1, $3); }
		;

rename_list:
			rename_list_el									{ $$ = list_make1($1); }
			| rename_list ',' rename_list_el				{ $$ = lappend($1, $3); }
		;

rename_list_opt_comma:
			rename_list										{ $$ = $1; }
			| rename_list ','								{ $$ = $1; }
		;
opt_rename_list: RENAME '(' rename_list_opt_comma ')'		{ $$ = $3; }
			| RENAME rename_list_el							{ $$ = list_make1($2); }
			| /*EMPTY*/										{ $$ = NULL; }
		;

/*****************************************************************************
 *
 *	Names and constants
 *
 *****************************************************************************/

qualified_name_list:
			qualified_name							{ $$ = list_make1($1); }
			| qualified_name_list ',' qualified_name { $$ = lappend($1, $3); }
		;


name_list:	name
					{ $$ = list_make1(makeString($1)); }
			| name_list ',' name
					{ $$ = lappend($1, makeString($3)); }
		;


name_list_opt_comma:
			name_list								{ $$ = $1; }
			| name_list ','							{ $$ = $1; }
		;

name_list_opt_comma_opt_bracket:
			name_list_opt_comma										{ $$ = $1; }
			| '(' name_list_opt_comma ')'							{ $$ = $2; }
		;

name:		ColIdOrString							{ $$ = $1; };


/*
 * The production for a qualified func_name has to exactly match the
 * production for a qualified columnref, because we cannot tell which we
 * are parsing until we see what comes after it ('(' or Sconst for a func_name,
 * anything else for a columnref).  Therefore we allow 'indirection' which
 * may contain subscripts, and reject that case in the C code.  (If we
 * ever implement SQL99-like methods, such syntax may actually become legal!)
 */
func_name:	function_name_token
					{ $$ = list_make1(makeString($1)); }
			|
			ColId indirection
					{
						$$ = check_func_name(lcons(makeString($1), $2),
											 yyscanner);
					}
		;


/*
 * Constants
 */
AexprConst: Iconst
				{
					$$ = makeIntConst($1, @1);
				}
			| FCONST
				{
					$$ = makeFloatConst($1, @1);
				}
			| Sconst opt_indirection
				{
					if ($2)
					{
						PGAIndirection *n = makeNode(PGAIndirection);
						n->arg = makeStringConst($1, @1);
						n->indirection = check_indirection($2, yyscanner);
						$$ = (PGNode *) n;
					}
					else
						$$ = makeStringConst($1, @1);
				}
			| BCONST
				{
					$$ = makeBitStringConst($1, @1);
				}
			| XCONST
				{
					/* This is a bit constant per SQL99:
					 * Without Feature F511, "BIT data type",
					 * a <general literal> shall not be a
					 * <bit string literal> or a <hex string literal>.
					 */
					$$ = makeBitStringConst($1, @1);
				}
			| func_name Sconst
				{
					/* generic type 'literal' syntax */
					PGTypeName *t = makeTypeNameFromNameList($1);
					t->location = @1;
					$$ = makeStringConstCast($2, @2, t);
				}
			| func_name '(' func_arg_list opt_sort_clause opt_ignore_nulls ')' Sconst
				{
					/* generic syntax with a type modifier */
					PGTypeName *t = makeTypeNameFromNameList($1);
					PGListCell *lc;

					/*
					 * We must use func_arg_list and opt_sort_clause in the
					 * production to avoid reduce/reduce conflicts, but we
					 * don't actually wish to allow PGNamedArgExpr in this
					 * context, ORDER BY, nor IGNORE NULLS.
					 */
					foreach(lc, $3)
					{
						PGNamedArgExpr *arg = (PGNamedArgExpr *) lfirst(lc);

						if (IsA(arg, PGNamedArgExpr))
							ereport(ERROR,
									(errcode(PG_ERRCODE_SYNTAX_ERROR),
									 errmsg("type modifier cannot have parameter name"),
									 parser_errposition(arg->location)));
					}
					if ($4 != NIL)
							ereport(ERROR,
									(errcode(PG_ERRCODE_SYNTAX_ERROR),
									 errmsg("type modifier cannot have ORDER BY"),
									 parser_errposition(@4)));
					if ($5 != false)
							ereport(ERROR,
									(errcode(PG_ERRCODE_SYNTAX_ERROR),
									 errmsg("type modifier cannot have IGNORE NULLS"),
									 parser_errposition(@5)));


					t->typmods = $3;
					t->location = @1;
					$$ = makeStringConstCast($7, @7, t);
				}
			| ConstTypename Sconst
				{
					$$ = makeStringConstCast($2, @2, $1);
				}
			| ConstInterval '(' a_expr ')' opt_interval
				{
					$$ = makeIntervalNode($3, @3, $5);
				}
			| ConstInterval Iconst opt_interval
				{
					$$ = makeIntervalNode($2, @2, $3);
				}
			| ConstInterval Sconst opt_interval
				{
					$$ = makeIntervalNode($2, @2, $3);
				}
			| TRUE_P
				{
					$$ = makeBoolAConst(true, @1);
				}
			| FALSE_P
				{
					$$ = makeBoolAConst(false, @1);
				}
			| NULL_P
				{
					$$ = makeNullAConst(@1);
				}
		;

Iconst:		ICONST									{ $$ = $1; };

/* Role specifications */
/*
 * Name classification hierarchy.
 *
 * IDENT is the lexeme returned by the lexer for identifiers that match
 * no known keyword.  In most cases, we can accept certain keywords as
 * names, not only IDENTs.	We prefer to accept as many such keywords
 * as possible to minimize the impact of "reserved words" on programmers.
 * So, we divide names into several possible classes.  The classification
 * is chosen in part to make keywords acceptable as names wherever possible.
 */


/* Type/function identifier --- names that can be type or function names.
 */
type_function_name:	IDENT							{ $$ = $1; }
			| unreserved_keyword					{ $$ = pstrdup($1); }
			| type_func_name_keyword				{ $$ = pstrdup($1); }
		;

function_name_token:	IDENT						{ $$ = $1; }
			| unreserved_keyword					{ $$ = pstrdup($1); }
			| func_name_keyword						{ $$ = pstrdup($1); }
		;

type_name_token:	IDENT						{ $$ = $1; }
			| unreserved_keyword					{ $$ = pstrdup($1); }
			| type_name_keyword						{ $$ = pstrdup($1); }
		;

any_name:	ColId						{ $$ = list_make1(makeString($1)); }
			| ColId attrs				{ $$ = lcons(makeString($1), $2); }
		;

attrs:		'.' attr_name
					{ $$ = list_make1(makeString($2)); }
			| attrs '.' attr_name
					{ $$ = lappend($1, makeString($3)); }
		;

opt_name_list:
			'(' name_list_opt_comma ')'						{ $$ = $2; }
			| /*EMPTY*/								{ $$ = NIL; }
		;

param_name:	type_function_name
		;


ColLabelOrString:	ColLabel						{ $$ = $1; }
					| SCONST						{ $$ = $1; }
		;
