mirror of
https://git.postgresql.org/git/postgresql.git
synced 2024-08-23 12:30:41 +02:00
9042 lines
223 KiB
Plaintext
9042 lines
223 KiB
Plaintext
%{
|
|
|
|
/*#define YYDEBUG 1*/
|
|
/*-------------------------------------------------------------------------
|
|
*
|
|
* gram.y
|
|
* POSTGRES SQL YACC rules/actions
|
|
*
|
|
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
|
|
* Portions Copyright (c) 1994, Regents of the University of California
|
|
*
|
|
*
|
|
* IDENTIFICATION
|
|
* $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.526 2006/02/04 19:06:46 adunstan Exp $
|
|
*
|
|
* HISTORY
|
|
* AUTHOR DATE MAJOR EVENT
|
|
* Andrew Yu Sept, 1994 POSTQUEL to SQL conversion
|
|
* Andrew Yu Oct, 1994 lispy code conversion
|
|
*
|
|
* NOTES
|
|
* CAPITALS are used to represent terminal symbols.
|
|
* non-capitals are used to represent non-terminals.
|
|
* SQL92-specific syntax is separated from plain SQL/Postgres syntax
|
|
* to help isolate the non-extensible portions of the parser.
|
|
*
|
|
* In general, nothing in this file should initiate database accesses
|
|
* nor depend on changeable state (such as SET variables). If you do
|
|
* database accesses, your code will fail when we have aborted the
|
|
* current transaction and are just parsing commands to find the next
|
|
* ROLLBACK or COMMIT. If you make use of SET variables, then you
|
|
* will do the wrong thing in multi-query strings like this:
|
|
* SET SQL_inheritance TO off; SELECT * FROM foo;
|
|
* because the entire string is parsed by gram.y before the SET gets
|
|
* executed. Anything that depends on the database or changeable state
|
|
* should be handled inside parse_analyze() so that it happens at the
|
|
* right time not the wrong time. The handling of SQL_inheritance is
|
|
* a good example.
|
|
*
|
|
* WARNINGS
|
|
* If you use a list, make sure the datum is a node so that the printing
|
|
* routines work.
|
|
*
|
|
* Sometimes we assign constants to makeStrings. Make sure we don't free
|
|
* those.
|
|
*
|
|
*-------------------------------------------------------------------------
|
|
*/
|
|
#include "postgres.h"
|
|
|
|
#include <ctype.h>
|
|
#include <limits.h>
|
|
|
|
#include "catalog/index.h"
|
|
#include "catalog/namespace.h"
|
|
#include "nodes/makefuncs.h"
|
|
#include "parser/gramparse.h"
|
|
#include "storage/lmgr.h"
|
|
#include "utils/date.h"
|
|
#include "utils/datetime.h"
|
|
#include "utils/numeric.h"
|
|
|
|
|
|
extern List *parsetree; /* final parse result is delivered here */
|
|
|
|
static bool QueryIsRule = FALSE;
|
|
|
|
/*
|
|
* If you need access to certain yacc-generated variables and find that
|
|
* they're static by default, uncomment the next line. (this is not a
|
|
* problem, yet.)
|
|
*/
|
|
/*#define __YYSCLASS*/
|
|
|
|
static Node *makeColumnRef(char *relname, List *indirection);
|
|
static Node *makeTypeCast(Node *arg, TypeName *typename);
|
|
static Node *makeStringConst(char *str, TypeName *typename);
|
|
static Node *makeIntConst(int val);
|
|
static Node *makeFloatConst(char *str);
|
|
static Node *makeAConst(Value *v);
|
|
static Node *makeRowNullTest(NullTestType test, RowExpr *row);
|
|
static DefElem *makeDefElem(char *name, Node *arg);
|
|
static A_Const *makeBoolAConst(bool state);
|
|
static FuncCall *makeOverlaps(List *largs, List *rargs);
|
|
static void check_qualified_name(List *names);
|
|
static List *check_func_name(List *names);
|
|
static List *extractArgTypes(List *parameters);
|
|
static SelectStmt *findLeftmostSelect(SelectStmt *node);
|
|
static void insertSelectOptions(SelectStmt *stmt,
|
|
List *sortClause, Node *lockingClause,
|
|
Node *limitOffset, Node *limitCount);
|
|
static Node *makeSetOp(SetOperation op, bool all, Node *larg, Node *rarg);
|
|
static Node *doNegate(Node *n);
|
|
static void doNegateFloat(Value *v);
|
|
|
|
%}
|
|
|
|
|
|
%union
|
|
{
|
|
int ival;
|
|
char chr;
|
|
char *str;
|
|
const char *keyword;
|
|
bool boolean;
|
|
JoinType jtype;
|
|
DropBehavior dbehavior;
|
|
OnCommitAction oncommit;
|
|
ContainsOids withoids;
|
|
List *list;
|
|
Node *node;
|
|
Value *value;
|
|
ObjectType objtype;
|
|
|
|
TypeName *typnam;
|
|
FunctionParameter *fun_param;
|
|
FunctionParameterMode fun_param_mode;
|
|
FuncWithArgs *funwithargs;
|
|
DefElem *defelt;
|
|
SortBy *sortby;
|
|
JoinExpr *jexpr;
|
|
IndexElem *ielem;
|
|
Alias *alias;
|
|
RangeVar *range;
|
|
A_Indices *aind;
|
|
ResTarget *target;
|
|
PrivTarget *privtarget;
|
|
|
|
InsertStmt *istmt;
|
|
VariableSetStmt *vsetstmt;
|
|
}
|
|
|
|
%type <node> stmt schema_stmt
|
|
AlterDatabaseStmt AlterDatabaseSetStmt AlterDomainStmt AlterGroupStmt
|
|
AlterObjectSchemaStmt AlterOwnerStmt AlterSeqStmt AlterTableStmt
|
|
AlterUserStmt AlterUserSetStmt AlterRoleStmt AlterRoleSetStmt
|
|
AnalyzeStmt ClosePortalStmt ClusterStmt CommentStmt
|
|
ConstraintsSetStmt CopyStmt CreateAsStmt CreateCastStmt
|
|
CreateDomainStmt CreateGroupStmt CreateOpClassStmt CreatePLangStmt
|
|
CreateSchemaStmt CreateSeqStmt CreateStmt CreateTableSpaceStmt
|
|
CreateAssertStmt CreateTrigStmt CreateUserStmt CreateRoleStmt
|
|
CreatedbStmt DeclareCursorStmt DefineStmt DeleteStmt
|
|
DropGroupStmt DropOpClassStmt DropPLangStmt DropStmt
|
|
DropAssertStmt DropTrigStmt DropRuleStmt DropCastStmt DropRoleStmt
|
|
DropUserStmt DropdbStmt DropTableSpaceStmt ExplainStmt FetchStmt
|
|
GrantStmt GrantRoleStmt IndexStmt InsertStmt ListenStmt LoadStmt
|
|
LockStmt NotifyStmt ExplainableStmt PreparableStmt
|
|
CreateFunctionStmt AlterFunctionStmt ReindexStmt RemoveAggrStmt
|
|
RemoveFuncStmt RemoveOperStmt RenameStmt RevokeStmt RevokeRoleStmt
|
|
RuleActionStmt RuleActionStmtOrEmpty RuleStmt
|
|
SelectStmt TransactionStmt TruncateStmt
|
|
UnlistenStmt UpdateStmt VacuumStmt
|
|
VariableResetStmt VariableSetStmt VariableShowStmt
|
|
ViewStmt CheckPointStmt CreateConversionStmt
|
|
DeallocateStmt PrepareStmt ExecuteStmt
|
|
DropOwnedStmt ReassignOwnedStmt
|
|
|
|
%type <node> select_no_parens select_with_parens select_clause
|
|
simple_select
|
|
|
|
%type <node> alter_column_default opclass_item alter_using
|
|
%type <ival> add_drop
|
|
|
|
%type <node> alter_table_cmd alter_rel_cmd
|
|
%type <list> alter_table_cmds alter_rel_cmds
|
|
|
|
%type <dbehavior> opt_drop_behavior
|
|
|
|
%type <list> createdb_opt_list alterdb_opt_list copy_opt_list
|
|
transaction_mode_list
|
|
%type <defelt> createdb_opt_item alterdb_opt_item copy_opt_item
|
|
transaction_mode_item
|
|
|
|
%type <ival> opt_lock lock_type cast_context
|
|
%type <boolean> opt_force opt_or_replace
|
|
opt_grant_grant_option opt_grant_admin_option
|
|
opt_nowait
|
|
|
|
%type <boolean> like_including_defaults
|
|
|
|
%type <list> OptRoleList
|
|
%type <defelt> OptRoleElem
|
|
|
|
%type <str> OptSchemaName
|
|
%type <list> OptSchemaEltList
|
|
|
|
%type <boolean> TriggerActionTime TriggerForSpec opt_trusted
|
|
%type <str> opt_lancompiler
|
|
|
|
%type <str> TriggerEvents
|
|
%type <value> TriggerFuncArg
|
|
|
|
%type <str> relation_name copy_file_name
|
|
database_name access_method_clause access_method attr_name
|
|
index_name name function_name file_name
|
|
|
|
%type <list> func_name handler_name qual_Op qual_all_Op subquery_Op
|
|
opt_class opt_validator
|
|
|
|
%type <range> qualified_name OptConstrFromTable
|
|
|
|
%type <str> all_Op MathOp SpecialRuleRelation
|
|
|
|
%type <str> iso_level opt_encoding
|
|
%type <node> grantee
|
|
%type <list> grantee_list
|
|
%type <str> privilege
|
|
%type <list> privileges privilege_list
|
|
%type <privtarget> privilege_target
|
|
%type <funwithargs> function_with_argtypes
|
|
%type <list> function_with_argtypes_list
|
|
%type <chr> TriggerOneEvent
|
|
|
|
%type <list> stmtblock stmtmulti
|
|
OptTableElementList TableElementList OptInherit definition
|
|
opt_distinct opt_definition func_args
|
|
func_args_list func_as createfunc_opt_list alterfunc_opt_list
|
|
oper_argtypes RuleActionList RuleActionMulti
|
|
opt_column_list columnList opt_name_list
|
|
sort_clause opt_sort_clause sortby_list index_params
|
|
name_list from_clause from_list opt_array_bounds
|
|
qualified_name_list any_name any_name_list
|
|
any_operator expr_list attrs
|
|
target_list update_target_list insert_column_list
|
|
insert_target_list def_list indirection opt_indirection
|
|
group_clause TriggerFuncArgs select_limit
|
|
opt_select_limit opclass_item_list
|
|
transaction_mode_list_or_empty
|
|
TableFuncElementList
|
|
prep_type_clause prep_type_list
|
|
execute_param_clause using_clause
|
|
|
|
%type <range> into_clause OptTempTableName
|
|
|
|
%type <defelt> createfunc_opt_item common_func_opt_item
|
|
%type <fun_param> func_arg
|
|
%type <fun_param_mode> arg_class
|
|
%type <typnam> func_return func_type aggr_argtype
|
|
|
|
%type <boolean> TriggerForType OptTemp
|
|
%type <oncommit> OnCommitOption
|
|
%type <withoids> OptWithOids WithOidsAs
|
|
|
|
%type <node> for_locking_clause opt_for_locking_clause
|
|
%type <list> locked_rels_list
|
|
%type <boolean> opt_all
|
|
|
|
%type <node> join_outer join_qual
|
|
%type <jtype> join_type
|
|
|
|
%type <list> extract_list overlay_list position_list
|
|
%type <list> substr_list trim_list
|
|
%type <ival> opt_interval
|
|
%type <node> overlay_placing substr_from substr_for
|
|
|
|
%type <boolean> opt_instead opt_analyze
|
|
%type <boolean> index_opt_unique opt_verbose opt_full
|
|
%type <boolean> opt_freeze opt_default opt_recheck
|
|
%type <defelt> opt_binary opt_oids copy_delimiter
|
|
|
|
%type <boolean> copy_from opt_hold
|
|
|
|
%type <ival> opt_column event cursor_options
|
|
%type <objtype> reindex_type drop_type comment_type
|
|
|
|
%type <node> fetch_direction select_limit_value select_offset_value
|
|
|
|
%type <list> OptSeqList
|
|
%type <defelt> OptSeqElem
|
|
|
|
%type <istmt> insert_rest
|
|
|
|
%type <vsetstmt> set_rest
|
|
|
|
%type <node> TableElement ConstraintElem TableFuncElement
|
|
%type <node> columnDef
|
|
%type <defelt> def_elem
|
|
%type <node> def_arg columnElem where_clause
|
|
a_expr b_expr c_expr func_expr AexprConst indirection_el
|
|
columnref in_expr having_clause func_table array_expr
|
|
%type <list> row type_list array_expr_list
|
|
%type <node> case_expr case_arg when_clause case_default
|
|
%type <list> when_clause_list
|
|
%type <ival> sub_type
|
|
%type <list> OptCreateAs CreateAsList
|
|
%type <node> CreateAsElement
|
|
%type <value> NumericOnly FloatOnly IntegerOnly
|
|
%type <alias> alias_clause
|
|
%type <sortby> sortby
|
|
%type <ielem> index_elem
|
|
%type <node> table_ref
|
|
%type <jexpr> joined_table
|
|
%type <range> relation_expr
|
|
%type <range> relation_expr_opt_alias
|
|
%type <target> target_el insert_target_el update_target_el insert_column_item
|
|
|
|
%type <typnam> Typename SimpleTypename ConstTypename
|
|
GenericType Numeric opt_float
|
|
Character ConstCharacter
|
|
CharacterWithLength CharacterWithoutLength
|
|
ConstDatetime ConstInterval
|
|
Bit ConstBit BitWithLength BitWithoutLength
|
|
%type <str> character
|
|
%type <str> extract_arg
|
|
%type <str> opt_charset
|
|
%type <ival> opt_numeric opt_decimal
|
|
%type <boolean> opt_varying opt_timezone
|
|
|
|
%type <ival> Iconst SignedIconst
|
|
%type <str> Sconst comment_text
|
|
%type <str> RoleId opt_granted_by opt_boolean ColId_or_Sconst
|
|
%type <list> var_list var_list_or_default
|
|
%type <str> ColId ColLabel var_name type_name param_name
|
|
%type <node> var_value zone_value
|
|
|
|
%type <keyword> unreserved_keyword func_name_keyword
|
|
%type <keyword> col_name_keyword reserved_keyword
|
|
|
|
%type <node> TableConstraint TableLikeClause
|
|
%type <list> ColQualList
|
|
%type <node> ColConstraint ColConstraintElem ConstraintAttr
|
|
%type <ival> key_actions key_delete key_match key_update key_action
|
|
%type <ival> ConstraintAttributeSpec ConstraintDeferrabilitySpec
|
|
ConstraintTimeSpec
|
|
|
|
%type <list> constraints_set_list
|
|
%type <boolean> constraints_set_mode
|
|
%type <str> OptTableSpace OptConsTableSpace OptTableSpaceOwner
|
|
|
|
|
|
/*
|
|
* If you make any token changes, update the keyword table in
|
|
* parser/keywords.c and add new keywords to the appropriate one of
|
|
* the reserved-or-not-so-reserved keyword lists, below; search
|
|
* this file for "Name classification hierarchy".
|
|
*/
|
|
|
|
/* ordinary key words in alphabetical order */
|
|
%token <keyword> ABORT_P ABSOLUTE_P ACCESS ACTION ADD_P ADMIN AFTER
|
|
AGGREGATE ALL ALSO ALTER ANALYSE ANALYZE AND ANY ARRAY AS ASC
|
|
ASSERTION ASSIGNMENT ASYMMETRIC AT AUTHORIZATION
|
|
|
|
BACKWARD BEFORE BEGIN_P BETWEEN BIGINT BINARY BIT
|
|
BOOLEAN_P BOTH BY
|
|
|
|
CACHE CALLED CASCADE CASE CAST CHAIN CHAR_P
|
|
CHARACTER CHARACTERISTICS CHECK CHECKPOINT CLASS CLOSE
|
|
CLUSTER COALESCE COLLATE COLUMN COMMENT COMMIT
|
|
COMMITTED CONNECTION CONSTRAINT CONSTRAINTS CONVERSION_P CONVERT COPY CREATE CREATEDB
|
|
CREATEROLE CREATEUSER CROSS CSV CURRENT_DATE CURRENT_ROLE CURRENT_TIME
|
|
CURRENT_TIMESTAMP CURRENT_USER CURSOR CYCLE
|
|
|
|
DATABASE DAY_P DEALLOCATE DEC DECIMAL_P DECLARE DEFAULT DEFAULTS
|
|
DEFERRABLE DEFERRED DEFINER DELETE_P DELIMITER DELIMITERS
|
|
DESC DISABLE_P DISTINCT DO DOMAIN_P DOUBLE_P DROP
|
|
|
|
EACH ELSE ENABLE_P ENCODING ENCRYPTED END_P ESCAPE EXCEPT EXCLUDING
|
|
EXCLUSIVE EXECUTE EXISTS EXPLAIN EXTERNAL EXTRACT
|
|
|
|
FALSE_P FETCH FIRST_P FLOAT_P FOR FORCE FOREIGN FORWARD
|
|
FREEZE FROM FULL FUNCTION
|
|
|
|
GLOBAL GRANT GRANTED GREATEST GROUP_P
|
|
|
|
HANDLER HAVING HEADER_P HOLD HOUR_P
|
|
|
|
IF_P ILIKE IMMEDIATE IMMUTABLE IMPLICIT_P IN_P INCLUDING INCREMENT
|
|
INDEX INHERIT INHERITS INITIALLY INNER_P INOUT INPUT_P
|
|
INSENSITIVE INSERT INSTEAD INT_P INTEGER INTERSECT
|
|
INTERVAL INTO INVOKER IS ISNULL ISOLATION
|
|
|
|
JOIN
|
|
|
|
KEY
|
|
|
|
LANCOMPILER LANGUAGE LARGE_P LAST_P LEADING LEAST LEFT LEVEL
|
|
LIKE LIMIT LISTEN LOAD LOCAL LOCALTIME LOCALTIMESTAMP LOCATION
|
|
LOCK_P LOGIN_P
|
|
|
|
MATCH MAXVALUE MINUTE_P MINVALUE MODE MONTH_P MOVE
|
|
|
|
NAMES NATIONAL NATURAL NCHAR NEW NEXT NO NOCREATEDB
|
|
NOCREATEROLE NOCREATEUSER NOINHERIT NOLOGIN_P NONE NOSUPERUSER
|
|
NOT NOTHING NOTIFY NOTNULL NOWAIT NULL_P NULLIF NUMERIC
|
|
|
|
OBJECT_P OF OFF OFFSET OIDS OLD ON ONLY OPERATOR OPTION OR
|
|
ORDER OUT_P OUTER_P OVERLAPS OVERLAY OWNED OWNER
|
|
|
|
PARTIAL PASSWORD PLACING POSITION
|
|
PRECISION PRESERVE PREPARE PREPARED PRIMARY
|
|
PRIOR PRIVILEGES PROCEDURAL PROCEDURE
|
|
|
|
QUOTE
|
|
|
|
READ REAL REASSIGN RECHECK REFERENCES REINDEX RELATIVE_P RELEASE RENAME
|
|
REPEATABLE REPLACE RESET RESTART RESTRICT RETURNS REVOKE RIGHT
|
|
ROLE ROLLBACK ROW ROWS RULE
|
|
|
|
SAVEPOINT SCHEMA SCROLL SECOND_P SECURITY SELECT SEQUENCE
|
|
SERIALIZABLE SESSION SESSION_USER SET SETOF SHARE
|
|
SHOW SIMILAR SIMPLE SMALLINT SOME STABLE START STATEMENT
|
|
STATISTICS STDIN STDOUT STORAGE STRICT_P SUBSTRING SUPERUSER_P SYMMETRIC
|
|
SYSID SYSTEM_P
|
|
|
|
TABLE TABLESPACE TEMP TEMPLATE TEMPORARY THEN TIME TIMESTAMP
|
|
TO TOAST TRAILING TRANSACTION TREAT TRIGGER TRIM TRUE_P
|
|
TRUNCATE TRUSTED TYPE_P
|
|
|
|
UNCOMMITTED UNENCRYPTED UNION UNIQUE UNKNOWN UNLISTEN UNTIL
|
|
UPDATE USER USING
|
|
|
|
VACUUM VALID VALIDATOR VALUES VARCHAR VARYING
|
|
VERBOSE VIEW VOLATILE
|
|
|
|
WHEN WHERE WITH WITHOUT WORK WRITE
|
|
|
|
YEAR_P
|
|
|
|
ZONE
|
|
|
|
/* The grammar thinks these are keywords, but they are not in the keywords.c
|
|
* list and so can never be entered directly. The filter in parser.c
|
|
* creates these tokens when required.
|
|
*/
|
|
%token UNIONJOIN
|
|
|
|
/* Special token types, not actually keywords - see the "lex" file */
|
|
%token <str> IDENT FCONST SCONST BCONST XCONST Op
|
|
%token <ival> ICONST PARAM
|
|
|
|
/* precedence: lowest to highest */
|
|
%nonassoc SET /* see relation_expr_opt_alias */
|
|
%left UNION EXCEPT
|
|
%left INTERSECT
|
|
%left OR
|
|
%left AND
|
|
%right NOT
|
|
%right '='
|
|
%nonassoc '<' '>'
|
|
%nonassoc LIKE ILIKE SIMILAR
|
|
%nonassoc ESCAPE
|
|
%nonassoc OVERLAPS
|
|
%nonassoc BETWEEN
|
|
%nonassoc IN_P
|
|
%left POSTFIXOP /* dummy for postfix Op rules */
|
|
%left Op OPERATOR /* multi-character ops and user-defined operators */
|
|
%nonassoc NOTNULL
|
|
%nonassoc ISNULL
|
|
%nonassoc IS NULL_P TRUE_P FALSE_P UNKNOWN /* sets precedence for IS NULL, etc */
|
|
%left '+' '-'
|
|
%left '*' '/' '%'
|
|
%left '^'
|
|
/* Unary Operators */
|
|
%left AT ZONE /* sets precedence for AT TIME ZONE */
|
|
%right UMINUS
|
|
%left '[' ']'
|
|
%left '(' ')'
|
|
%left TYPECAST
|
|
%left '.'
|
|
/*
|
|
* These might seem to be low-precedence, but actually they are not part
|
|
* of the arithmetic hierarchy at all in their use as JOIN operators.
|
|
* We make them high-precedence to support their use as function names.
|
|
* They wouldn't be given a precedence at all, were it not that we need
|
|
* left-associativity among the JOIN rules themselves.
|
|
*/
|
|
%left JOIN UNIONJOIN CROSS LEFT FULL RIGHT INNER_P NATURAL
|
|
%%
|
|
|
|
/*
|
|
* Handle comment-only lines, and ;; SELECT * FROM pg_class ;;;
|
|
* psql already handles such cases, but other interfaces don't.
|
|
* bjm 1999/10/05
|
|
*/
|
|
stmtblock: stmtmulti { parsetree = $1; }
|
|
;
|
|
|
|
/* the thrashing around here is to discard "empty" statements... */
|
|
stmtmulti: stmtmulti ';' stmt
|
|
{ if ($3 != NULL)
|
|
$$ = lappend($1, $3);
|
|
else
|
|
$$ = $1;
|
|
}
|
|
| stmt
|
|
{ if ($1 != NULL)
|
|
$$ = list_make1($1);
|
|
else
|
|
$$ = NIL;
|
|
}
|
|
;
|
|
|
|
stmt :
|
|
AlterDatabaseStmt
|
|
| AlterDatabaseSetStmt
|
|
| AlterDomainStmt
|
|
| AlterFunctionStmt
|
|
| AlterGroupStmt
|
|
| AlterObjectSchemaStmt
|
|
| AlterOwnerStmt
|
|
| AlterSeqStmt
|
|
| AlterTableStmt
|
|
| AlterRoleSetStmt
|
|
| AlterRoleStmt
|
|
| AlterUserSetStmt
|
|
| AlterUserStmt
|
|
| AnalyzeStmt
|
|
| CheckPointStmt
|
|
| ClosePortalStmt
|
|
| ClusterStmt
|
|
| CommentStmt
|
|
| ConstraintsSetStmt
|
|
| CopyStmt
|
|
| CreateAsStmt
|
|
| CreateAssertStmt
|
|
| CreateCastStmt
|
|
| CreateConversionStmt
|
|
| CreateDomainStmt
|
|
| CreateFunctionStmt
|
|
| CreateGroupStmt
|
|
| CreateOpClassStmt
|
|
| CreatePLangStmt
|
|
| CreateSchemaStmt
|
|
| CreateSeqStmt
|
|
| CreateStmt
|
|
| CreateTableSpaceStmt
|
|
| CreateTrigStmt
|
|
| CreateRoleStmt
|
|
| CreateUserStmt
|
|
| CreatedbStmt
|
|
| DeallocateStmt
|
|
| DeclareCursorStmt
|
|
| DefineStmt
|
|
| DeleteStmt
|
|
| DropAssertStmt
|
|
| DropCastStmt
|
|
| DropGroupStmt
|
|
| DropOpClassStmt
|
|
| DropOwnedStmt
|
|
| DropPLangStmt
|
|
| DropRuleStmt
|
|
| DropStmt
|
|
| DropTableSpaceStmt
|
|
| DropTrigStmt
|
|
| DropRoleStmt
|
|
| DropUserStmt
|
|
| DropdbStmt
|
|
| ExecuteStmt
|
|
| ExplainStmt
|
|
| FetchStmt
|
|
| GrantStmt
|
|
| GrantRoleStmt
|
|
| IndexStmt
|
|
| InsertStmt
|
|
| ListenStmt
|
|
| LoadStmt
|
|
| LockStmt
|
|
| NotifyStmt
|
|
| PrepareStmt
|
|
| ReassignOwnedStmt
|
|
| ReindexStmt
|
|
| RemoveAggrStmt
|
|
| RemoveFuncStmt
|
|
| RemoveOperStmt
|
|
| RenameStmt
|
|
| RevokeStmt
|
|
| RevokeRoleStmt
|
|
| RuleStmt
|
|
| SelectStmt
|
|
| TransactionStmt
|
|
| TruncateStmt
|
|
| UnlistenStmt
|
|
| UpdateStmt
|
|
| VacuumStmt
|
|
| VariableResetStmt
|
|
| VariableSetStmt
|
|
| VariableShowStmt
|
|
| ViewStmt
|
|
| /*EMPTY*/
|
|
{ $$ = NULL; }
|
|
;
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* Create a new Postgres DBMS role
|
|
*
|
|
*****************************************************************************/
|
|
|
|
CreateRoleStmt:
|
|
CREATE ROLE RoleId opt_with OptRoleList
|
|
{
|
|
CreateRoleStmt *n = makeNode(CreateRoleStmt);
|
|
n->stmt_type = ROLESTMT_ROLE;
|
|
n->role = $3;
|
|
n->options = $5;
|
|
$$ = (Node *)n;
|
|
}
|
|
;
|
|
|
|
|
|
opt_with: WITH {}
|
|
| /*EMPTY*/ {}
|
|
;
|
|
|
|
/*
|
|
* Options for CREATE ROLE and ALTER ROLE (also used by CREATE/ALTER USER
|
|
* for backwards compatibility). Note: the only option required by SQL99
|
|
* is "WITH ADMIN name".
|
|
*/
|
|
OptRoleList:
|
|
OptRoleList OptRoleElem { $$ = lappend($1, $2); }
|
|
| /* EMPTY */ { $$ = NIL; }
|
|
;
|
|
|
|
OptRoleElem:
|
|
PASSWORD Sconst
|
|
{
|
|
$$ = makeDefElem("password",
|
|
(Node *)makeString($2));
|
|
}
|
|
| PASSWORD NULL_P
|
|
{
|
|
$$ = makeDefElem("password", NULL);
|
|
}
|
|
| ENCRYPTED PASSWORD Sconst
|
|
{
|
|
$$ = makeDefElem("encryptedPassword",
|
|
(Node *)makeString($3));
|
|
}
|
|
| UNENCRYPTED PASSWORD Sconst
|
|
{
|
|
$$ = makeDefElem("unencryptedPassword",
|
|
(Node *)makeString($3));
|
|
}
|
|
| SUPERUSER_P
|
|
{
|
|
$$ = makeDefElem("superuser", (Node *)makeInteger(TRUE));
|
|
}
|
|
| NOSUPERUSER
|
|
{
|
|
$$ = makeDefElem("superuser", (Node *)makeInteger(FALSE));
|
|
}
|
|
| INHERIT
|
|
{
|
|
$$ = makeDefElem("inherit", (Node *)makeInteger(TRUE));
|
|
}
|
|
| NOINHERIT
|
|
{
|
|
$$ = makeDefElem("inherit", (Node *)makeInteger(FALSE));
|
|
}
|
|
| CREATEDB
|
|
{
|
|
$$ = makeDefElem("createdb", (Node *)makeInteger(TRUE));
|
|
}
|
|
| NOCREATEDB
|
|
{
|
|
$$ = makeDefElem("createdb", (Node *)makeInteger(FALSE));
|
|
}
|
|
| CREATEROLE
|
|
{
|
|
$$ = makeDefElem("createrole", (Node *)makeInteger(TRUE));
|
|
}
|
|
| NOCREATEROLE
|
|
{
|
|
$$ = makeDefElem("createrole", (Node *)makeInteger(FALSE));
|
|
}
|
|
| CREATEUSER
|
|
{
|
|
/* For backwards compatibility, synonym for SUPERUSER */
|
|
$$ = makeDefElem("superuser", (Node *)makeInteger(TRUE));
|
|
}
|
|
| NOCREATEUSER
|
|
{
|
|
$$ = makeDefElem("superuser", (Node *)makeInteger(FALSE));
|
|
}
|
|
| LOGIN_P
|
|
{
|
|
$$ = makeDefElem("canlogin", (Node *)makeInteger(TRUE));
|
|
}
|
|
| NOLOGIN_P
|
|
{
|
|
$$ = makeDefElem("canlogin", (Node *)makeInteger(FALSE));
|
|
}
|
|
| CONNECTION LIMIT SignedIconst
|
|
{
|
|
$$ = makeDefElem("connectionlimit", (Node *)makeInteger($3));
|
|
}
|
|
| VALID UNTIL Sconst
|
|
{
|
|
$$ = makeDefElem("validUntil", (Node *)makeString($3));
|
|
}
|
|
/* Supported but not documented for roles, for use by ALTER GROUP. */
|
|
| USER name_list
|
|
{
|
|
$$ = makeDefElem("rolemembers", (Node *)$2);
|
|
}
|
|
/* The following are not supported by ALTER ROLE/USER/GROUP */
|
|
| SYSID Iconst
|
|
{
|
|
$$ = makeDefElem("sysid", (Node *)makeInteger($2));
|
|
}
|
|
| ADMIN name_list
|
|
{
|
|
$$ = makeDefElem("adminmembers", (Node *)$2);
|
|
}
|
|
| ROLE name_list
|
|
{
|
|
$$ = makeDefElem("rolemembers", (Node *)$2);
|
|
}
|
|
| IN_P ROLE name_list
|
|
{
|
|
$$ = makeDefElem("addroleto", (Node *)$3);
|
|
}
|
|
| IN_P GROUP_P name_list
|
|
{
|
|
$$ = makeDefElem("addroleto", (Node *)$3);
|
|
}
|
|
;
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* Create a new Postgres DBMS user (role with implied login ability)
|
|
*
|
|
*****************************************************************************/
|
|
|
|
CreateUserStmt:
|
|
CREATE USER RoleId opt_with OptRoleList
|
|
{
|
|
CreateRoleStmt *n = makeNode(CreateRoleStmt);
|
|
n->stmt_type = ROLESTMT_USER;
|
|
n->role = $3;
|
|
n->options = $5;
|
|
$$ = (Node *)n;
|
|
}
|
|
;
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* Alter a postgresql DBMS role
|
|
*
|
|
*****************************************************************************/
|
|
|
|
AlterRoleStmt:
|
|
ALTER ROLE RoleId opt_with OptRoleList
|
|
{
|
|
AlterRoleStmt *n = makeNode(AlterRoleStmt);
|
|
n->role = $3;
|
|
n->action = +1; /* add, if there are members */
|
|
n->options = $5;
|
|
$$ = (Node *)n;
|
|
}
|
|
;
|
|
|
|
AlterRoleSetStmt:
|
|
ALTER ROLE RoleId SET set_rest
|
|
{
|
|
AlterRoleSetStmt *n = makeNode(AlterRoleSetStmt);
|
|
n->role = $3;
|
|
n->variable = $5->name;
|
|
n->value = $5->args;
|
|
$$ = (Node *)n;
|
|
}
|
|
| ALTER ROLE RoleId VariableResetStmt
|
|
{
|
|
AlterRoleSetStmt *n = makeNode(AlterRoleSetStmt);
|
|
n->role = $3;
|
|
n->variable = ((VariableResetStmt *)$4)->name;
|
|
n->value = NIL;
|
|
$$ = (Node *)n;
|
|
}
|
|
;
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* Alter a postgresql DBMS user
|
|
*
|
|
*****************************************************************************/
|
|
|
|
AlterUserStmt:
|
|
ALTER USER RoleId opt_with OptRoleList
|
|
{
|
|
AlterRoleStmt *n = makeNode(AlterRoleStmt);
|
|
n->role = $3;
|
|
n->action = +1; /* add, if there are members */
|
|
n->options = $5;
|
|
$$ = (Node *)n;
|
|
}
|
|
;
|
|
|
|
|
|
AlterUserSetStmt:
|
|
ALTER USER RoleId SET set_rest
|
|
{
|
|
AlterRoleSetStmt *n = makeNode(AlterRoleSetStmt);
|
|
n->role = $3;
|
|
n->variable = $5->name;
|
|
n->value = $5->args;
|
|
$$ = (Node *)n;
|
|
}
|
|
| ALTER USER RoleId VariableResetStmt
|
|
{
|
|
AlterRoleSetStmt *n = makeNode(AlterRoleSetStmt);
|
|
n->role = $3;
|
|
n->variable = ((VariableResetStmt *)$4)->name;
|
|
n->value = NIL;
|
|
$$ = (Node *)n;
|
|
}
|
|
;
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* Drop a postgresql DBMS role
|
|
*
|
|
* XXX Ideally this would have CASCADE/RESTRICT options, but since a role
|
|
* might own objects in multiple databases, there is presently no way to
|
|
* implement either cascading or restricting. Caveat DBA.
|
|
*****************************************************************************/
|
|
|
|
DropRoleStmt:
|
|
DROP ROLE name_list
|
|
{
|
|
DropRoleStmt *n = makeNode(DropRoleStmt);
|
|
n->missing_ok = FALSE;
|
|
n->roles = $3;
|
|
$$ = (Node *)n;
|
|
}
|
|
| DROP ROLE IF_P EXISTS name_list
|
|
{
|
|
DropRoleStmt *n = makeNode(DropRoleStmt);
|
|
n->missing_ok = TRUE;
|
|
n->roles = $5;
|
|
$$ = (Node *)n;
|
|
}
|
|
;
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* Drop a postgresql DBMS user
|
|
*
|
|
* XXX Ideally this would have CASCADE/RESTRICT options, but since a user
|
|
* might own objects in multiple databases, there is presently no way to
|
|
* implement either cascading or restricting. Caveat DBA.
|
|
*****************************************************************************/
|
|
|
|
DropUserStmt:
|
|
DROP USER name_list
|
|
{
|
|
DropRoleStmt *n = makeNode(DropRoleStmt);
|
|
n->missing_ok = FALSE;
|
|
n->roles = $3;
|
|
$$ = (Node *)n;
|
|
}
|
|
| DROP USER IF_P EXISTS name_list
|
|
{
|
|
DropRoleStmt *n = makeNode(DropRoleStmt);
|
|
n->roles = $5;
|
|
n->missing_ok = TRUE;
|
|
$$ = (Node *)n;
|
|
}
|
|
;
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* Create a postgresql group (role without login ability)
|
|
*
|
|
*****************************************************************************/
|
|
|
|
CreateGroupStmt:
|
|
CREATE GROUP_P RoleId opt_with OptRoleList
|
|
{
|
|
CreateRoleStmt *n = makeNode(CreateRoleStmt);
|
|
n->stmt_type = ROLESTMT_GROUP;
|
|
n->role = $3;
|
|
n->options = $5;
|
|
$$ = (Node *)n;
|
|
}
|
|
;
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* Alter a postgresql group
|
|
*
|
|
*****************************************************************************/
|
|
|
|
AlterGroupStmt:
|
|
ALTER GROUP_P RoleId add_drop USER name_list
|
|
{
|
|
AlterRoleStmt *n = makeNode(AlterRoleStmt);
|
|
n->role = $3;
|
|
n->action = $4;
|
|
n->options = list_make1(makeDefElem("rolemembers",
|
|
(Node *)$6));
|
|
$$ = (Node *)n;
|
|
}
|
|
;
|
|
|
|
add_drop: ADD_P { $$ = +1; }
|
|
| DROP { $$ = -1; }
|
|
;
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* Drop a postgresql group
|
|
*
|
|
* XXX see above notes about cascading DROP USER; groups have same problem.
|
|
*****************************************************************************/
|
|
|
|
DropGroupStmt:
|
|
DROP GROUP_P name_list
|
|
{
|
|
DropRoleStmt *n = makeNode(DropRoleStmt);
|
|
n->missing_ok = FALSE;
|
|
n->roles = $3;
|
|
$$ = (Node *)n;
|
|
}
|
|
| DROP GROUP_P IF_P EXISTS name_list
|
|
{
|
|
DropRoleStmt *n = makeNode(DropRoleStmt);
|
|
n->missing_ok = TRUE;
|
|
n->roles = $5;
|
|
$$ = (Node *)n;
|
|
}
|
|
;
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* Manipulate a schema
|
|
*
|
|
*****************************************************************************/
|
|
|
|
CreateSchemaStmt:
|
|
CREATE SCHEMA OptSchemaName AUTHORIZATION RoleId OptSchemaEltList
|
|
{
|
|
CreateSchemaStmt *n = makeNode(CreateSchemaStmt);
|
|
/* One can omit the schema name or the authorization id. */
|
|
if ($3 != NULL)
|
|
n->schemaname = $3;
|
|
else
|
|
n->schemaname = $5;
|
|
n->authid = $5;
|
|
n->schemaElts = $6;
|
|
$$ = (Node *)n;
|
|
}
|
|
| CREATE SCHEMA ColId OptSchemaEltList
|
|
{
|
|
CreateSchemaStmt *n = makeNode(CreateSchemaStmt);
|
|
/* ...but not both */
|
|
n->schemaname = $3;
|
|
n->authid = NULL;
|
|
n->schemaElts = $4;
|
|
$$ = (Node *)n;
|
|
}
|
|
;
|
|
|
|
OptSchemaName:
|
|
ColId { $$ = $1; }
|
|
| /* EMPTY */ { $$ = NULL; }
|
|
;
|
|
|
|
OptSchemaEltList:
|
|
OptSchemaEltList schema_stmt { $$ = lappend($1, $2); }
|
|
| /* EMPTY */ { $$ = NIL; }
|
|
;
|
|
|
|
/*
|
|
* schema_stmt are the ones that can show up inside a CREATE SCHEMA
|
|
* statement (in addition to by themselves).
|
|
*/
|
|
schema_stmt:
|
|
CreateStmt
|
|
| IndexStmt
|
|
| CreateSeqStmt
|
|
| CreateTrigStmt
|
|
| GrantStmt
|
|
| ViewStmt
|
|
;
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* Set PG internal variable
|
|
* SET name TO 'var_value'
|
|
* Include SQL92 syntax (thomas 1997-10-22):
|
|
* SET TIME ZONE 'var_value'
|
|
*
|
|
*****************************************************************************/
|
|
|
|
VariableSetStmt:
|
|
SET set_rest
|
|
{
|
|
VariableSetStmt *n = $2;
|
|
n->is_local = false;
|
|
$$ = (Node *) n;
|
|
}
|
|
| SET LOCAL set_rest
|
|
{
|
|
VariableSetStmt *n = $3;
|
|
n->is_local = true;
|
|
$$ = (Node *) n;
|
|
}
|
|
| SET SESSION set_rest
|
|
{
|
|
VariableSetStmt *n = $3;
|
|
n->is_local = false;
|
|
$$ = (Node *) n;
|
|
}
|
|
;
|
|
|
|
set_rest: var_name TO var_list_or_default
|
|
{
|
|
VariableSetStmt *n = makeNode(VariableSetStmt);
|
|
n->name = $1;
|
|
n->args = $3;
|
|
$$ = n;
|
|
}
|
|
| var_name '=' var_list_or_default
|
|
{
|
|
VariableSetStmt *n = makeNode(VariableSetStmt);
|
|
n->name = $1;
|
|
n->args = $3;
|
|
$$ = n;
|
|
}
|
|
| TIME ZONE zone_value
|
|
{
|
|
VariableSetStmt *n = makeNode(VariableSetStmt);
|
|
n->name = "timezone";
|
|
if ($3 != NULL)
|
|
n->args = list_make1($3);
|
|
$$ = n;
|
|
}
|
|
| TRANSACTION transaction_mode_list
|
|
{
|
|
VariableSetStmt *n = makeNode(VariableSetStmt);
|
|
n->name = "TRANSACTION";
|
|
n->args = $2;
|
|
$$ = n;
|
|
}
|
|
| SESSION CHARACTERISTICS AS TRANSACTION transaction_mode_list
|
|
{
|
|
VariableSetStmt *n = makeNode(VariableSetStmt);
|
|
n->name = "SESSION CHARACTERISTICS";
|
|
n->args = $5;
|
|
$$ = n;
|
|
}
|
|
| NAMES opt_encoding
|
|
{
|
|
VariableSetStmt *n = makeNode(VariableSetStmt);
|
|
n->name = "client_encoding";
|
|
if ($2 != NULL)
|
|
n->args = list_make1(makeStringConst($2, NULL));
|
|
$$ = n;
|
|
}
|
|
| ROLE ColId_or_Sconst
|
|
{
|
|
VariableSetStmt *n = makeNode(VariableSetStmt);
|
|
n->name = "role";
|
|
n->args = list_make1(makeStringConst($2, NULL));
|
|
$$ = n;
|
|
}
|
|
| SESSION AUTHORIZATION ColId_or_Sconst
|
|
{
|
|
VariableSetStmt *n = makeNode(VariableSetStmt);
|
|
n->name = "session_authorization";
|
|
n->args = list_make1(makeStringConst($3, NULL));
|
|
$$ = n;
|
|
}
|
|
| SESSION AUTHORIZATION DEFAULT
|
|
{
|
|
VariableSetStmt *n = makeNode(VariableSetStmt);
|
|
n->name = "session_authorization";
|
|
n->args = NIL;
|
|
$$ = n;
|
|
}
|
|
;
|
|
|
|
var_name:
|
|
ColId { $$ = $1; }
|
|
| var_name '.' ColId
|
|
{
|
|
int qLen = strlen($1);
|
|
char* qualName = palloc(qLen + strlen($3) + 2);
|
|
strcpy(qualName, $1);
|
|
qualName[qLen] = '.';
|
|
strcpy(qualName + qLen + 1, $3);
|
|
$$ = qualName;
|
|
}
|
|
;
|
|
|
|
var_list_or_default:
|
|
var_list { $$ = $1; }
|
|
| DEFAULT { $$ = NIL; }
|
|
;
|
|
|
|
var_list: var_value { $$ = list_make1($1); }
|
|
| var_list ',' var_value { $$ = lappend($1, $3); }
|
|
;
|
|
|
|
var_value: opt_boolean
|
|
{ $$ = makeStringConst($1, NULL); }
|
|
| ColId_or_Sconst
|
|
{ $$ = makeStringConst($1, NULL); }
|
|
| NumericOnly
|
|
{ $$ = makeAConst($1); }
|
|
;
|
|
|
|
iso_level: READ UNCOMMITTED { $$ = "read uncommitted"; }
|
|
| READ COMMITTED { $$ = "read committed"; }
|
|
| REPEATABLE READ { $$ = "repeatable read"; }
|
|
| SERIALIZABLE { $$ = "serializable"; }
|
|
;
|
|
|
|
opt_boolean:
|
|
TRUE_P { $$ = "true"; }
|
|
| FALSE_P { $$ = "false"; }
|
|
| ON { $$ = "on"; }
|
|
| OFF { $$ = "off"; }
|
|
;
|
|
|
|
/* Timezone values can be:
|
|
* - a string such as 'pst8pdt'
|
|
* - an identifier such as "pst8pdt"
|
|
* - an integer or floating point number
|
|
* - a time interval per SQL99
|
|
* ColId gives reduce/reduce errors against ConstInterval and LOCAL,
|
|
* so use IDENT and reject anything which is a reserved word.
|
|
*/
|
|
zone_value:
|
|
Sconst
|
|
{
|
|
$$ = makeStringConst($1, NULL);
|
|
}
|
|
| IDENT
|
|
{
|
|
$$ = makeStringConst($1, NULL);
|
|
}
|
|
| ConstInterval Sconst opt_interval
|
|
{
|
|
A_Const *n = (A_Const *) makeStringConst($2, $1);
|
|
if ($3 != INTERVAL_FULL_RANGE)
|
|
{
|
|
if (($3 & ~(INTERVAL_MASK(HOUR) | INTERVAL_MASK(MINUTE))) != 0)
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_SYNTAX_ERROR),
|
|
errmsg("time zone interval must be HOUR or HOUR TO MINUTE")));
|
|
n->typename->typmod = INTERVAL_TYPMOD(INTERVAL_FULL_PRECISION, $3);
|
|
}
|
|
$$ = (Node *)n;
|
|
}
|
|
| ConstInterval '(' Iconst ')' Sconst opt_interval
|
|
{
|
|
A_Const *n = (A_Const *) makeStringConst($5, $1);
|
|
if ($3 < 0)
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
|
errmsg("INTERVAL(%d) precision must not be negative",
|
|
$3)));
|
|
if ($3 > MAX_INTERVAL_PRECISION)
|
|
{
|
|
ereport(WARNING,
|
|
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
|
errmsg("INTERVAL(%d) precision reduced to maximum allowed, %d",
|
|
$3, MAX_INTERVAL_PRECISION)));
|
|
$3 = MAX_INTERVAL_PRECISION;
|
|
}
|
|
|
|
if (($6 != INTERVAL_FULL_RANGE)
|
|
&& (($6 & ~(INTERVAL_MASK(HOUR) | INTERVAL_MASK(MINUTE))) != 0))
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_SYNTAX_ERROR),
|
|
errmsg("time zone interval must be HOUR or HOUR TO MINUTE")));
|
|
|
|
n->typename->typmod = INTERVAL_TYPMOD($3, $6);
|
|
|
|
$$ = (Node *)n;
|
|
}
|
|
| NumericOnly { $$ = makeAConst($1); }
|
|
| DEFAULT { $$ = NULL; }
|
|
| LOCAL { $$ = NULL; }
|
|
;
|
|
|
|
opt_encoding:
|
|
Sconst { $$ = $1; }
|
|
| DEFAULT { $$ = NULL; }
|
|
| /*EMPTY*/ { $$ = NULL; }
|
|
;
|
|
|
|
ColId_or_Sconst:
|
|
ColId { $$ = $1; }
|
|
| SCONST { $$ = $1; }
|
|
;
|
|
|
|
|
|
VariableShowStmt:
|
|
SHOW var_name
|
|
{
|
|
VariableShowStmt *n = makeNode(VariableShowStmt);
|
|
n->name = $2;
|
|
$$ = (Node *) n;
|
|
}
|
|
| SHOW TIME ZONE
|
|
{
|
|
VariableShowStmt *n = makeNode(VariableShowStmt);
|
|
n->name = "timezone";
|
|
$$ = (Node *) n;
|
|
}
|
|
| SHOW TRANSACTION ISOLATION LEVEL
|
|
{
|
|
VariableShowStmt *n = makeNode(VariableShowStmt);
|
|
n->name = "transaction_isolation";
|
|
$$ = (Node *) n;
|
|
}
|
|
| SHOW SESSION AUTHORIZATION
|
|
{
|
|
VariableShowStmt *n = makeNode(VariableShowStmt);
|
|
n->name = "session_authorization";
|
|
$$ = (Node *) n;
|
|
}
|
|
| SHOW ALL
|
|
{
|
|
VariableShowStmt *n = makeNode(VariableShowStmt);
|
|
n->name = "all";
|
|
$$ = (Node *) n;
|
|
}
|
|
;
|
|
|
|
VariableResetStmt:
|
|
RESET var_name
|
|
{
|
|
VariableResetStmt *n = makeNode(VariableResetStmt);
|
|
n->name = $2;
|
|
$$ = (Node *) n;
|
|
}
|
|
| RESET TIME ZONE
|
|
{
|
|
VariableResetStmt *n = makeNode(VariableResetStmt);
|
|
n->name = "timezone";
|
|
$$ = (Node *) n;
|
|
}
|
|
| RESET TRANSACTION ISOLATION LEVEL
|
|
{
|
|
VariableResetStmt *n = makeNode(VariableResetStmt);
|
|
n->name = "transaction_isolation";
|
|
$$ = (Node *) n;
|
|
}
|
|
| RESET SESSION AUTHORIZATION
|
|
{
|
|
VariableResetStmt *n = makeNode(VariableResetStmt);
|
|
n->name = "session_authorization";
|
|
$$ = (Node *) n;
|
|
}
|
|
| RESET ALL
|
|
{
|
|
VariableResetStmt *n = makeNode(VariableResetStmt);
|
|
n->name = "all";
|
|
$$ = (Node *) n;
|
|
}
|
|
;
|
|
|
|
|
|
ConstraintsSetStmt:
|
|
SET CONSTRAINTS constraints_set_list constraints_set_mode
|
|
{
|
|
ConstraintsSetStmt *n = makeNode(ConstraintsSetStmt);
|
|
n->constraints = $3;
|
|
n->deferred = $4;
|
|
$$ = (Node *) n;
|
|
}
|
|
;
|
|
|
|
constraints_set_list:
|
|
ALL { $$ = NIL; }
|
|
| name_list { $$ = $1; }
|
|
;
|
|
|
|
constraints_set_mode:
|
|
DEFERRED { $$ = TRUE; }
|
|
| IMMEDIATE { $$ = FALSE; }
|
|
;
|
|
|
|
|
|
/*
|
|
* Checkpoint statement
|
|
*/
|
|
CheckPointStmt:
|
|
CHECKPOINT
|
|
{
|
|
CheckPointStmt *n = makeNode(CheckPointStmt);
|
|
$$ = (Node *)n;
|
|
}
|
|
;
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* ALTER [ TABLE | INDEX ] variations
|
|
*
|
|
*****************************************************************************/
|
|
|
|
AlterTableStmt:
|
|
ALTER TABLE relation_expr alter_table_cmds
|
|
{
|
|
AlterTableStmt *n = makeNode(AlterTableStmt);
|
|
n->relation = $3;
|
|
n->cmds = $4;
|
|
n->relkind = OBJECT_TABLE;
|
|
$$ = (Node *)n;
|
|
}
|
|
| ALTER INDEX relation_expr alter_rel_cmds
|
|
{
|
|
AlterTableStmt *n = makeNode(AlterTableStmt);
|
|
n->relation = $3;
|
|
n->cmds = $4;
|
|
n->relkind = OBJECT_INDEX;
|
|
$$ = (Node *)n;
|
|
}
|
|
;
|
|
|
|
alter_table_cmds:
|
|
alter_table_cmd { $$ = list_make1($1); }
|
|
| alter_table_cmds ',' alter_table_cmd { $$ = lappend($1, $3); }
|
|
;
|
|
|
|
/* Subcommands that are for ALTER TABLE only */
|
|
alter_table_cmd:
|
|
/* ALTER TABLE <relation> ADD [COLUMN] <coldef> */
|
|
ADD_P opt_column columnDef
|
|
{
|
|
AlterTableCmd *n = makeNode(AlterTableCmd);
|
|
n->subtype = AT_AddColumn;
|
|
n->def = $3;
|
|
$$ = (Node *)n;
|
|
}
|
|
/* ALTER TABLE <relation> ALTER [COLUMN] <colname> {SET DEFAULT <expr>|DROP DEFAULT} */
|
|
| ALTER opt_column ColId alter_column_default
|
|
{
|
|
AlterTableCmd *n = makeNode(AlterTableCmd);
|
|
n->subtype = AT_ColumnDefault;
|
|
n->name = $3;
|
|
n->def = $4;
|
|
$$ = (Node *)n;
|
|
}
|
|
/* ALTER TABLE <relation> ALTER [COLUMN] <colname> DROP NOT NULL */
|
|
| ALTER opt_column ColId DROP NOT NULL_P
|
|
{
|
|
AlterTableCmd *n = makeNode(AlterTableCmd);
|
|
n->subtype = AT_DropNotNull;
|
|
n->name = $3;
|
|
$$ = (Node *)n;
|
|
}
|
|
/* ALTER TABLE <relation> ALTER [COLUMN] <colname> SET NOT NULL */
|
|
| ALTER opt_column ColId SET NOT NULL_P
|
|
{
|
|
AlterTableCmd *n = makeNode(AlterTableCmd);
|
|
n->subtype = AT_SetNotNull;
|
|
n->name = $3;
|
|
$$ = (Node *)n;
|
|
}
|
|
/* ALTER TABLE <relation> ALTER [COLUMN] <colname> SET STATISTICS <IntegerOnly> */
|
|
| ALTER opt_column ColId SET STATISTICS IntegerOnly
|
|
{
|
|
AlterTableCmd *n = makeNode(AlterTableCmd);
|
|
n->subtype = AT_SetStatistics;
|
|
n->name = $3;
|
|
n->def = (Node *) $6;
|
|
$$ = (Node *)n;
|
|
}
|
|
/* ALTER TABLE <relation> ALTER [COLUMN] <colname> SET STORAGE <storagemode> */
|
|
| ALTER opt_column ColId SET STORAGE ColId
|
|
{
|
|
AlterTableCmd *n = makeNode(AlterTableCmd);
|
|
n->subtype = AT_SetStorage;
|
|
n->name = $3;
|
|
n->def = (Node *) makeString($6);
|
|
$$ = (Node *)n;
|
|
}
|
|
/* ALTER TABLE <relation> DROP [COLUMN] <colname> [RESTRICT|CASCADE] */
|
|
| DROP opt_column ColId opt_drop_behavior
|
|
{
|
|
AlterTableCmd *n = makeNode(AlterTableCmd);
|
|
n->subtype = AT_DropColumn;
|
|
n->name = $3;
|
|
n->behavior = $4;
|
|
$$ = (Node *)n;
|
|
}
|
|
/*
|
|
* ALTER TABLE <relation> ALTER [COLUMN] <colname> TYPE <typename>
|
|
* [ USING <expression> ]
|
|
*/
|
|
| ALTER opt_column ColId TYPE_P Typename alter_using
|
|
{
|
|
AlterTableCmd *n = makeNode(AlterTableCmd);
|
|
n->subtype = AT_AlterColumnType;
|
|
n->name = $3;
|
|
n->def = (Node *) $5;
|
|
n->transform = $6;
|
|
$$ = (Node *)n;
|
|
}
|
|
/* ALTER TABLE <relation> ADD CONSTRAINT ... */
|
|
| ADD_P TableConstraint
|
|
{
|
|
AlterTableCmd *n = makeNode(AlterTableCmd);
|
|
n->subtype = AT_AddConstraint;
|
|
n->def = $2;
|
|
$$ = (Node *)n;
|
|
}
|
|
/* ALTER TABLE <relation> DROP CONSTRAINT <name> [RESTRICT|CASCADE] */
|
|
| DROP CONSTRAINT name opt_drop_behavior
|
|
{
|
|
AlterTableCmd *n = makeNode(AlterTableCmd);
|
|
n->subtype = AT_DropConstraint;
|
|
n->name = $3;
|
|
n->behavior = $4;
|
|
$$ = (Node *)n;
|
|
}
|
|
/* ALTER TABLE <relation> SET WITHOUT OIDS */
|
|
| SET WITHOUT OIDS
|
|
{
|
|
AlterTableCmd *n = makeNode(AlterTableCmd);
|
|
n->subtype = AT_DropOids;
|
|
$$ = (Node *)n;
|
|
}
|
|
/* ALTER TABLE <name> CREATE TOAST TABLE -- ONLY */
|
|
| CREATE TOAST TABLE
|
|
{
|
|
AlterTableCmd *n = makeNode(AlterTableCmd);
|
|
n->subtype = AT_ToastTable;
|
|
$$ = (Node *)n;
|
|
}
|
|
/* ALTER TABLE <name> CLUSTER ON <indexname> */
|
|
| CLUSTER ON name
|
|
{
|
|
AlterTableCmd *n = makeNode(AlterTableCmd);
|
|
n->subtype = AT_ClusterOn;
|
|
n->name = $3;
|
|
$$ = (Node *)n;
|
|
}
|
|
/* ALTER TABLE <name> SET WITHOUT CLUSTER */
|
|
| SET WITHOUT CLUSTER
|
|
{
|
|
AlterTableCmd *n = makeNode(AlterTableCmd);
|
|
n->subtype = AT_DropCluster;
|
|
n->name = NULL;
|
|
$$ = (Node *)n;
|
|
}
|
|
/* ALTER TABLE <name> ENABLE TRIGGER <trig> */
|
|
| ENABLE_P TRIGGER name
|
|
{
|
|
AlterTableCmd *n = makeNode(AlterTableCmd);
|
|
n->subtype = AT_EnableTrig;
|
|
n->name = $3;
|
|
$$ = (Node *)n;
|
|
}
|
|
/* ALTER TABLE <name> ENABLE TRIGGER ALL */
|
|
| ENABLE_P TRIGGER ALL
|
|
{
|
|
AlterTableCmd *n = makeNode(AlterTableCmd);
|
|
n->subtype = AT_EnableTrigAll;
|
|
$$ = (Node *)n;
|
|
}
|
|
/* ALTER TABLE <name> ENABLE TRIGGER USER */
|
|
| ENABLE_P TRIGGER USER
|
|
{
|
|
AlterTableCmd *n = makeNode(AlterTableCmd);
|
|
n->subtype = AT_EnableTrigUser;
|
|
$$ = (Node *)n;
|
|
}
|
|
/* ALTER TABLE <name> DISABLE TRIGGER <trig> */
|
|
| DISABLE_P TRIGGER name
|
|
{
|
|
AlterTableCmd *n = makeNode(AlterTableCmd);
|
|
n->subtype = AT_DisableTrig;
|
|
n->name = $3;
|
|
$$ = (Node *)n;
|
|
}
|
|
/* ALTER TABLE <name> DISABLE TRIGGER ALL */
|
|
| DISABLE_P TRIGGER ALL
|
|
{
|
|
AlterTableCmd *n = makeNode(AlterTableCmd);
|
|
n->subtype = AT_DisableTrigAll;
|
|
$$ = (Node *)n;
|
|
}
|
|
/* ALTER TABLE <name> DISABLE TRIGGER USER */
|
|
| DISABLE_P TRIGGER USER
|
|
{
|
|
AlterTableCmd *n = makeNode(AlterTableCmd);
|
|
n->subtype = AT_DisableTrigUser;
|
|
$$ = (Node *)n;
|
|
}
|
|
| alter_rel_cmd
|
|
{
|
|
$$ = $1;
|
|
}
|
|
;
|
|
|
|
alter_rel_cmds:
|
|
alter_rel_cmd { $$ = list_make1($1); }
|
|
| alter_rel_cmds ',' alter_rel_cmd { $$ = lappend($1, $3); }
|
|
;
|
|
|
|
/* Subcommands that are for ALTER TABLE or ALTER INDEX */
|
|
alter_rel_cmd:
|
|
/* ALTER [TABLE|INDEX] <name> OWNER TO RoleId */
|
|
OWNER TO RoleId
|
|
{
|
|
AlterTableCmd *n = makeNode(AlterTableCmd);
|
|
n->subtype = AT_ChangeOwner;
|
|
n->name = $3;
|
|
$$ = (Node *)n;
|
|
}
|
|
/* ALTER [TABLE|INDEX] <name> SET TABLESPACE <tablespacename> */
|
|
| SET TABLESPACE name
|
|
{
|
|
AlterTableCmd *n = makeNode(AlterTableCmd);
|
|
n->subtype = AT_SetTableSpace;
|
|
n->name = $3;
|
|
$$ = (Node *)n;
|
|
}
|
|
;
|
|
|
|
alter_column_default:
|
|
SET DEFAULT a_expr
|
|
{
|
|
/* Treat SET DEFAULT NULL the same as DROP DEFAULT */
|
|
if (exprIsNullConstant($3))
|
|
$$ = NULL;
|
|
else
|
|
$$ = $3;
|
|
}
|
|
| DROP DEFAULT { $$ = NULL; }
|
|
;
|
|
|
|
opt_drop_behavior:
|
|
CASCADE { $$ = DROP_CASCADE; }
|
|
| RESTRICT { $$ = DROP_RESTRICT; }
|
|
| /* EMPTY */ { $$ = DROP_RESTRICT; /* default */ }
|
|
;
|
|
|
|
alter_using:
|
|
USING a_expr { $$ = $2; }
|
|
| /* EMPTY */ { $$ = NULL; }
|
|
;
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* QUERY :
|
|
* close <portalname>
|
|
*
|
|
*****************************************************************************/
|
|
|
|
ClosePortalStmt:
|
|
CLOSE name
|
|
{
|
|
ClosePortalStmt *n = makeNode(ClosePortalStmt);
|
|
n->portalname = $2;
|
|
$$ = (Node *)n;
|
|
}
|
|
;
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* QUERY :
|
|
* COPY <relname> ['(' columnList ')'] FROM/TO [WITH options]
|
|
*
|
|
* BINARY, OIDS, and DELIMITERS kept in old locations
|
|
* for backward compatibility. 2002-06-18
|
|
*
|
|
*****************************************************************************/
|
|
|
|
CopyStmt: COPY opt_binary qualified_name opt_column_list opt_oids
|
|
copy_from copy_file_name copy_delimiter opt_with copy_opt_list
|
|
{
|
|
CopyStmt *n = makeNode(CopyStmt);
|
|
n->relation = $3;
|
|
n->attlist = $4;
|
|
n->is_from = $6;
|
|
n->filename = $7;
|
|
|
|
n->options = NIL;
|
|
/* Concatenate user-supplied flags */
|
|
if ($2)
|
|
n->options = lappend(n->options, $2);
|
|
if ($5)
|
|
n->options = lappend(n->options, $5);
|
|
if ($8)
|
|
n->options = lappend(n->options, $8);
|
|
if ($10)
|
|
n->options = list_concat(n->options, $10);
|
|
$$ = (Node *)n;
|
|
}
|
|
;
|
|
|
|
copy_from:
|
|
FROM { $$ = TRUE; }
|
|
| TO { $$ = FALSE; }
|
|
;
|
|
|
|
/*
|
|
* copy_file_name NULL indicates stdio is used. Whether stdin or stdout is
|
|
* used depends on the direction. (It really doesn't make sense to copy from
|
|
* stdout. We silently correct the "typo". - AY 9/94
|
|
*/
|
|
copy_file_name:
|
|
Sconst { $$ = $1; }
|
|
| STDIN { $$ = NULL; }
|
|
| STDOUT { $$ = NULL; }
|
|
;
|
|
|
|
|
|
|
|
copy_opt_list:
|
|
copy_opt_list copy_opt_item { $$ = lappend($1, $2); }
|
|
| /* EMPTY */ { $$ = NIL; }
|
|
;
|
|
|
|
|
|
copy_opt_item:
|
|
BINARY
|
|
{
|
|
$$ = makeDefElem("binary", (Node *)makeInteger(TRUE));
|
|
}
|
|
| OIDS
|
|
{
|
|
$$ = makeDefElem("oids", (Node *)makeInteger(TRUE));
|
|
}
|
|
| DELIMITER opt_as Sconst
|
|
{
|
|
$$ = makeDefElem("delimiter", (Node *)makeString($3));
|
|
}
|
|
| NULL_P opt_as Sconst
|
|
{
|
|
$$ = makeDefElem("null", (Node *)makeString($3));
|
|
}
|
|
| CSV
|
|
{
|
|
$$ = makeDefElem("csv", (Node *)makeInteger(TRUE));
|
|
}
|
|
| HEADER_P
|
|
{
|
|
$$ = makeDefElem("header", (Node *)makeInteger(TRUE));
|
|
}
|
|
| QUOTE opt_as Sconst
|
|
{
|
|
$$ = makeDefElem("quote", (Node *)makeString($3));
|
|
}
|
|
| ESCAPE opt_as Sconst
|
|
{
|
|
$$ = makeDefElem("escape", (Node *)makeString($3));
|
|
}
|
|
| FORCE QUOTE columnList
|
|
{
|
|
$$ = makeDefElem("force_quote", (Node *)$3);
|
|
}
|
|
| FORCE NOT NULL_P columnList
|
|
{
|
|
$$ = makeDefElem("force_notnull", (Node *)$4);
|
|
}
|
|
;
|
|
|
|
/* The following exist for backward compatibility */
|
|
|
|
opt_binary:
|
|
BINARY
|
|
{
|
|
$$ = makeDefElem("binary", (Node *)makeInteger(TRUE));
|
|
}
|
|
| /*EMPTY*/ { $$ = NULL; }
|
|
;
|
|
|
|
opt_oids:
|
|
WITH OIDS
|
|
{
|
|
$$ = makeDefElem("oids", (Node *)makeInteger(TRUE));
|
|
}
|
|
| /*EMPTY*/ { $$ = NULL; }
|
|
;
|
|
|
|
copy_delimiter:
|
|
/* USING DELIMITERS kept for backward compatibility. 2002-06-15 */
|
|
opt_using DELIMITERS Sconst
|
|
{
|
|
$$ = makeDefElem("delimiter", (Node *)makeString($3));
|
|
}
|
|
| /*EMPTY*/ { $$ = NULL; }
|
|
;
|
|
|
|
opt_using:
|
|
USING {}
|
|
| /*EMPTY*/ {}
|
|
;
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* QUERY :
|
|
* CREATE TABLE relname
|
|
*
|
|
*****************************************************************************/
|
|
|
|
CreateStmt: CREATE OptTemp TABLE qualified_name '(' OptTableElementList ')'
|
|
OptInherit OptWithOids OnCommitOption OptTableSpace
|
|
{
|
|
CreateStmt *n = makeNode(CreateStmt);
|
|
$4->istemp = $2;
|
|
n->relation = $4;
|
|
n->tableElts = $6;
|
|
n->inhRelations = $8;
|
|
n->constraints = NIL;
|
|
n->hasoids = $9;
|
|
n->oncommit = $10;
|
|
n->tablespacename = $11;
|
|
$$ = (Node *)n;
|
|
}
|
|
| CREATE OptTemp TABLE qualified_name OF qualified_name
|
|
'(' OptTableElementList ')' OptWithOids OnCommitOption OptTableSpace
|
|
{
|
|
/* SQL99 CREATE TABLE OF <UDT> (cols) seems to be satisfied
|
|
* by our inheritance capabilities. Let's try it...
|
|
*/
|
|
CreateStmt *n = makeNode(CreateStmt);
|
|
$4->istemp = $2;
|
|
n->relation = $4;
|
|
n->tableElts = $8;
|
|
n->inhRelations = list_make1($6);
|
|
n->constraints = NIL;
|
|
n->hasoids = $10;
|
|
n->oncommit = $11;
|
|
n->tablespacename = $12;
|
|
$$ = (Node *)n;
|
|
}
|
|
;
|
|
|
|
/*
|
|
* Redundancy here is needed to avoid shift/reduce conflicts,
|
|
* since TEMP is not a reserved word. See also OptTempTableName.
|
|
*
|
|
* NOTE: we accept both GLOBAL and LOCAL options; since we have no modules
|
|
* the LOCAL keyword is really meaningless.
|
|
*/
|
|
OptTemp: TEMPORARY { $$ = TRUE; }
|
|
| TEMP { $$ = TRUE; }
|
|
| LOCAL TEMPORARY { $$ = TRUE; }
|
|
| LOCAL TEMP { $$ = TRUE; }
|
|
| GLOBAL TEMPORARY { $$ = TRUE; }
|
|
| GLOBAL TEMP { $$ = TRUE; }
|
|
| /*EMPTY*/ { $$ = FALSE; }
|
|
;
|
|
|
|
OptTableElementList:
|
|
TableElementList { $$ = $1; }
|
|
| /*EMPTY*/ { $$ = NIL; }
|
|
;
|
|
|
|
TableElementList:
|
|
TableElement
|
|
{
|
|
$$ = list_make1($1);
|
|
}
|
|
| TableElementList ',' TableElement
|
|
{
|
|
$$ = lappend($1, $3);
|
|
}
|
|
;
|
|
|
|
TableElement:
|
|
columnDef { $$ = $1; }
|
|
| TableLikeClause { $$ = $1; }
|
|
| TableConstraint { $$ = $1; }
|
|
;
|
|
|
|
columnDef: ColId Typename ColQualList
|
|
{
|
|
ColumnDef *n = makeNode(ColumnDef);
|
|
n->colname = $1;
|
|
n->typename = $2;
|
|
n->constraints = $3;
|
|
n->is_local = true;
|
|
$$ = (Node *)n;
|
|
}
|
|
;
|
|
|
|
ColQualList:
|
|
ColQualList ColConstraint { $$ = lappend($1, $2); }
|
|
| /*EMPTY*/ { $$ = NIL; }
|
|
;
|
|
|
|
ColConstraint:
|
|
CONSTRAINT name ColConstraintElem
|
|
{
|
|
switch (nodeTag($3))
|
|
{
|
|
case T_Constraint:
|
|
{
|
|
Constraint *n = (Constraint *)$3;
|
|
n->name = $2;
|
|
}
|
|
break;
|
|
case T_FkConstraint:
|
|
{
|
|
FkConstraint *n = (FkConstraint *)$3;
|
|
n->constr_name = $2;
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
$$ = $3;
|
|
}
|
|
| ColConstraintElem { $$ = $1; }
|
|
| ConstraintAttr { $$ = $1; }
|
|
;
|
|
|
|
/* DEFAULT NULL is already the default for Postgres.
|
|
* But define it here and carry it forward into the system
|
|
* to make it explicit.
|
|
* - thomas 1998-09-13
|
|
*
|
|
* WITH NULL and NULL are not SQL92-standard syntax elements,
|
|
* so leave them out. Use DEFAULT NULL to explicitly indicate
|
|
* that a column may have that value. WITH NULL leads to
|
|
* shift/reduce conflicts with WITH TIME ZONE anyway.
|
|
* - thomas 1999-01-08
|
|
*
|
|
* DEFAULT expression must be b_expr not a_expr to prevent shift/reduce
|
|
* conflict on NOT (since NOT might start a subsequent NOT NULL constraint,
|
|
* or be part of a_expr NOT LIKE or similar constructs).
|
|
*/
|
|
ColConstraintElem:
|
|
NOT NULL_P
|
|
{
|
|
Constraint *n = makeNode(Constraint);
|
|
n->contype = CONSTR_NOTNULL;
|
|
n->name = NULL;
|
|
n->raw_expr = NULL;
|
|
n->cooked_expr = NULL;
|
|
n->keys = NULL;
|
|
n->indexspace = NULL;
|
|
$$ = (Node *)n;
|
|
}
|
|
| NULL_P
|
|
{
|
|
Constraint *n = makeNode(Constraint);
|
|
n->contype = CONSTR_NULL;
|
|
n->name = NULL;
|
|
n->raw_expr = NULL;
|
|
n->cooked_expr = NULL;
|
|
n->keys = NULL;
|
|
n->indexspace = NULL;
|
|
$$ = (Node *)n;
|
|
}
|
|
| UNIQUE OptConsTableSpace
|
|
{
|
|
Constraint *n = makeNode(Constraint);
|
|
n->contype = CONSTR_UNIQUE;
|
|
n->name = NULL;
|
|
n->raw_expr = NULL;
|
|
n->cooked_expr = NULL;
|
|
n->keys = NULL;
|
|
n->indexspace = $2;
|
|
$$ = (Node *)n;
|
|
}
|
|
| PRIMARY KEY OptConsTableSpace
|
|
{
|
|
Constraint *n = makeNode(Constraint);
|
|
n->contype = CONSTR_PRIMARY;
|
|
n->name = NULL;
|
|
n->raw_expr = NULL;
|
|
n->cooked_expr = NULL;
|
|
n->keys = NULL;
|
|
n->indexspace = $3;
|
|
$$ = (Node *)n;
|
|
}
|
|
| CHECK '(' a_expr ')'
|
|
{
|
|
Constraint *n = makeNode(Constraint);
|
|
n->contype = CONSTR_CHECK;
|
|
n->name = NULL;
|
|
n->raw_expr = $3;
|
|
n->cooked_expr = NULL;
|
|
n->keys = NULL;
|
|
n->indexspace = NULL;
|
|
$$ = (Node *)n;
|
|
}
|
|
| DEFAULT b_expr
|
|
{
|
|
Constraint *n = makeNode(Constraint);
|
|
n->contype = CONSTR_DEFAULT;
|
|
n->name = NULL;
|
|
if (exprIsNullConstant($2))
|
|
{
|
|
/* DEFAULT NULL should be reported as empty expr */
|
|
n->raw_expr = NULL;
|
|
}
|
|
else
|
|
{
|
|
n->raw_expr = $2;
|
|
}
|
|
n->cooked_expr = NULL;
|
|
n->keys = NULL;
|
|
n->indexspace = NULL;
|
|
$$ = (Node *)n;
|
|
}
|
|
| REFERENCES qualified_name opt_column_list key_match key_actions
|
|
{
|
|
FkConstraint *n = makeNode(FkConstraint);
|
|
n->constr_name = NULL;
|
|
n->pktable = $2;
|
|
n->fk_attrs = NIL;
|
|
n->pk_attrs = $3;
|
|
n->fk_matchtype = $4;
|
|
n->fk_upd_action = (char) ($5 >> 8);
|
|
n->fk_del_action = (char) ($5 & 0xFF);
|
|
n->deferrable = FALSE;
|
|
n->initdeferred = FALSE;
|
|
$$ = (Node *)n;
|
|
}
|
|
;
|
|
|
|
/*
|
|
* ConstraintAttr represents constraint attributes, which we parse as if
|
|
* they were independent constraint clauses, in order to avoid shift/reduce
|
|
* conflicts (since NOT might start either an independent NOT NULL clause
|
|
* or an attribute). analyze.c is responsible for attaching the attribute
|
|
* information to the preceding "real" constraint node, and for complaining
|
|
* if attribute clauses appear in the wrong place or wrong combinations.
|
|
*
|
|
* See also ConstraintAttributeSpec, which can be used in places where
|
|
* there is no parsing conflict.
|
|
*/
|
|
ConstraintAttr:
|
|
DEFERRABLE
|
|
{
|
|
Constraint *n = makeNode(Constraint);
|
|
n->contype = CONSTR_ATTR_DEFERRABLE;
|
|
$$ = (Node *)n;
|
|
}
|
|
| NOT DEFERRABLE
|
|
{
|
|
Constraint *n = makeNode(Constraint);
|
|
n->contype = CONSTR_ATTR_NOT_DEFERRABLE;
|
|
$$ = (Node *)n;
|
|
}
|
|
| INITIALLY DEFERRED
|
|
{
|
|
Constraint *n = makeNode(Constraint);
|
|
n->contype = CONSTR_ATTR_DEFERRED;
|
|
$$ = (Node *)n;
|
|
}
|
|
| INITIALLY IMMEDIATE
|
|
{
|
|
Constraint *n = makeNode(Constraint);
|
|
n->contype = CONSTR_ATTR_IMMEDIATE;
|
|
$$ = (Node *)n;
|
|
}
|
|
;
|
|
|
|
|
|
/*
|
|
* SQL99 supports wholesale borrowing of a table definition via the LIKE clause.
|
|
* This seems to be a poor man's inheritance capability, with the resulting
|
|
* tables completely decoupled except for the original commonality in definitions.
|
|
*
|
|
* This is very similar to CREATE TABLE AS except for the INCLUDING DEFAULTS extension
|
|
* which is a part of SQL 200N
|
|
*/
|
|
TableLikeClause:
|
|
LIKE qualified_name like_including_defaults
|
|
{
|
|
InhRelation *n = makeNode(InhRelation);
|
|
n->relation = $2;
|
|
n->including_defaults = $3;
|
|
|
|
$$ = (Node *)n;
|
|
}
|
|
;
|
|
|
|
like_including_defaults:
|
|
INCLUDING DEFAULTS { $$ = true; }
|
|
| EXCLUDING DEFAULTS { $$ = false; }
|
|
| /* EMPTY */ { $$ = false; }
|
|
;
|
|
|
|
|
|
/* ConstraintElem specifies constraint syntax which is not embedded into
|
|
* a column definition. ColConstraintElem specifies the embedded form.
|
|
* - thomas 1997-12-03
|
|
*/
|
|
TableConstraint:
|
|
CONSTRAINT name ConstraintElem
|
|
{
|
|
switch (nodeTag($3))
|
|
{
|
|
case T_Constraint:
|
|
{
|
|
Constraint *n = (Constraint *)$3;
|
|
n->name = $2;
|
|
}
|
|
break;
|
|
case T_FkConstraint:
|
|
{
|
|
FkConstraint *n = (FkConstraint *)$3;
|
|
n->constr_name = $2;
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
$$ = $3;
|
|
}
|
|
| ConstraintElem { $$ = $1; }
|
|
;
|
|
|
|
ConstraintElem:
|
|
CHECK '(' a_expr ')'
|
|
{
|
|
Constraint *n = makeNode(Constraint);
|
|
n->contype = CONSTR_CHECK;
|
|
n->name = NULL;
|
|
n->raw_expr = $3;
|
|
n->cooked_expr = NULL;
|
|
n->indexspace = NULL;
|
|
$$ = (Node *)n;
|
|
}
|
|
| UNIQUE '(' columnList ')' OptConsTableSpace
|
|
{
|
|
Constraint *n = makeNode(Constraint);
|
|
n->contype = CONSTR_UNIQUE;
|
|
n->name = NULL;
|
|
n->raw_expr = NULL;
|
|
n->cooked_expr = NULL;
|
|
n->keys = $3;
|
|
n->indexspace = $5;
|
|
$$ = (Node *)n;
|
|
}
|
|
| PRIMARY KEY '(' columnList ')' OptConsTableSpace
|
|
{
|
|
Constraint *n = makeNode(Constraint);
|
|
n->contype = CONSTR_PRIMARY;
|
|
n->name = NULL;
|
|
n->raw_expr = NULL;
|
|
n->cooked_expr = NULL;
|
|
n->keys = $4;
|
|
n->indexspace = $6;
|
|
$$ = (Node *)n;
|
|
}
|
|
| FOREIGN KEY '(' columnList ')' REFERENCES qualified_name
|
|
opt_column_list key_match key_actions ConstraintAttributeSpec
|
|
{
|
|
FkConstraint *n = makeNode(FkConstraint);
|
|
n->constr_name = NULL;
|
|
n->pktable = $7;
|
|
n->fk_attrs = $4;
|
|
n->pk_attrs = $8;
|
|
n->fk_matchtype = $9;
|
|
n->fk_upd_action = (char) ($10 >> 8);
|
|
n->fk_del_action = (char) ($10 & 0xFF);
|
|
n->deferrable = ($11 & 1) != 0;
|
|
n->initdeferred = ($11 & 2) != 0;
|
|
$$ = (Node *)n;
|
|
}
|
|
;
|
|
|
|
opt_column_list:
|
|
'(' columnList ')' { $$ = $2; }
|
|
| /*EMPTY*/ { $$ = NIL; }
|
|
;
|
|
|
|
columnList:
|
|
columnElem { $$ = list_make1($1); }
|
|
| columnList ',' columnElem { $$ = lappend($1, $3); }
|
|
;
|
|
|
|
columnElem: ColId
|
|
{
|
|
$$ = (Node *) makeString($1);
|
|
}
|
|
;
|
|
|
|
key_match: MATCH FULL
|
|
{
|
|
$$ = FKCONSTR_MATCH_FULL;
|
|
}
|
|
| MATCH PARTIAL
|
|
{
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
|
errmsg("MATCH PARTIAL not yet implemented")));
|
|
$$ = FKCONSTR_MATCH_PARTIAL;
|
|
}
|
|
| MATCH SIMPLE
|
|
{
|
|
$$ = FKCONSTR_MATCH_UNSPECIFIED;
|
|
}
|
|
| /*EMPTY*/
|
|
{
|
|
$$ = FKCONSTR_MATCH_UNSPECIFIED;
|
|
}
|
|
;
|
|
|
|
/*
|
|
* We combine the update and delete actions into one value temporarily
|
|
* for simplicity of parsing, and then break them down again in the
|
|
* calling production. update is in the left 8 bits, delete in the right.
|
|
* Note that NOACTION is the default.
|
|
*/
|
|
key_actions:
|
|
key_update
|
|
{ $$ = ($1 << 8) | (FKCONSTR_ACTION_NOACTION & 0xFF); }
|
|
| key_delete
|
|
{ $$ = (FKCONSTR_ACTION_NOACTION << 8) | ($1 & 0xFF); }
|
|
| key_update key_delete
|
|
{ $$ = ($1 << 8) | ($2 & 0xFF); }
|
|
| key_delete key_update
|
|
{ $$ = ($2 << 8) | ($1 & 0xFF); }
|
|
| /*EMPTY*/
|
|
{ $$ = (FKCONSTR_ACTION_NOACTION << 8) | (FKCONSTR_ACTION_NOACTION & 0xFF); }
|
|
;
|
|
|
|
key_update: ON UPDATE key_action { $$ = $3; }
|
|
;
|
|
|
|
key_delete: ON DELETE_P key_action { $$ = $3; }
|
|
;
|
|
|
|
key_action:
|
|
NO ACTION { $$ = FKCONSTR_ACTION_NOACTION; }
|
|
| RESTRICT { $$ = FKCONSTR_ACTION_RESTRICT; }
|
|
| CASCADE { $$ = FKCONSTR_ACTION_CASCADE; }
|
|
| SET NULL_P { $$ = FKCONSTR_ACTION_SETNULL; }
|
|
| SET DEFAULT { $$ = FKCONSTR_ACTION_SETDEFAULT; }
|
|
;
|
|
|
|
OptInherit: INHERITS '(' qualified_name_list ')' { $$ = $3; }
|
|
| /*EMPTY*/ { $$ = NIL; }
|
|
;
|
|
|
|
OptWithOids:
|
|
WITH OIDS { $$ = MUST_HAVE_OIDS; }
|
|
| WITHOUT OIDS { $$ = MUST_NOT_HAVE_OIDS; }
|
|
| /*EMPTY*/ { $$ = DEFAULT_OIDS; }
|
|
;
|
|
|
|
OnCommitOption: ON COMMIT DROP { $$ = ONCOMMIT_DROP; }
|
|
| ON COMMIT DELETE_P ROWS { $$ = ONCOMMIT_DELETE_ROWS; }
|
|
| ON COMMIT PRESERVE ROWS { $$ = ONCOMMIT_PRESERVE_ROWS; }
|
|
| /*EMPTY*/ { $$ = ONCOMMIT_NOOP; }
|
|
;
|
|
|
|
OptTableSpace: TABLESPACE name { $$ = $2; }
|
|
| /*EMPTY*/ { $$ = NULL; }
|
|
;
|
|
|
|
OptConsTableSpace: USING INDEX TABLESPACE name { $$ = $4; }
|
|
| /*EMPTY*/ { $$ = NULL; }
|
|
;
|
|
|
|
|
|
/*
|
|
* Note: CREATE TABLE ... AS SELECT ... is just another spelling for
|
|
* SELECT ... INTO.
|
|
*/
|
|
|
|
CreateAsStmt:
|
|
CREATE OptTemp TABLE qualified_name OptCreateAs WithOidsAs SelectStmt
|
|
{
|
|
/*
|
|
* When the SelectStmt is a set-operation tree, we must
|
|
* stuff the INTO information into the leftmost component
|
|
* Select, because that's where analyze.c will expect
|
|
* to find it. Similarly, the output column names must
|
|
* be attached to that Select's target list.
|
|
*/
|
|
SelectStmt *n = findLeftmostSelect((SelectStmt *) $7);
|
|
if (n->into != NULL)
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_SYNTAX_ERROR),
|
|
errmsg("CREATE TABLE AS may not specify INTO")));
|
|
$4->istemp = $2;
|
|
n->into = $4;
|
|
n->intoColNames = $5;
|
|
n->intoHasOids = $6;
|
|
$$ = $7;
|
|
}
|
|
;
|
|
|
|
/*
|
|
* To avoid a shift/reduce conflict in CreateAsStmt, we need to
|
|
* include the 'AS' terminal in the parsing of WITH/WITHOUT
|
|
* OIDS. Unfortunately that means this production is effectively a
|
|
* duplicate of OptWithOids.
|
|
*/
|
|
WithOidsAs:
|
|
WITH OIDS AS { $$ = MUST_HAVE_OIDS; }
|
|
| WITHOUT OIDS AS { $$ = MUST_NOT_HAVE_OIDS; }
|
|
| AS { $$ = DEFAULT_OIDS; }
|
|
;
|
|
|
|
OptCreateAs:
|
|
'(' CreateAsList ')' { $$ = $2; }
|
|
| /*EMPTY*/ { $$ = NIL; }
|
|
;
|
|
|
|
CreateAsList:
|
|
CreateAsElement { $$ = list_make1($1); }
|
|
| CreateAsList ',' CreateAsElement { $$ = lappend($1, $3); }
|
|
;
|
|
|
|
CreateAsElement:
|
|
ColId
|
|
{
|
|
ColumnDef *n = makeNode(ColumnDef);
|
|
n->colname = $1;
|
|
n->typename = NULL;
|
|
n->inhcount = 0;
|
|
n->is_local = true;
|
|
n->is_not_null = false;
|
|
n->raw_default = NULL;
|
|
n->cooked_default = NULL;
|
|
n->constraints = NIL;
|
|
n->support = NULL;
|
|
$$ = (Node *)n;
|
|
}
|
|
;
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* QUERY :
|
|
* CREATE SEQUENCE seqname
|
|
* ALTER SEQUENCE seqname
|
|
*
|
|
*****************************************************************************/
|
|
|
|
CreateSeqStmt:
|
|
CREATE OptTemp SEQUENCE qualified_name OptSeqList
|
|
{
|
|
CreateSeqStmt *n = makeNode(CreateSeqStmt);
|
|
$4->istemp = $2;
|
|
n->sequence = $4;
|
|
n->options = $5;
|
|
$$ = (Node *)n;
|
|
}
|
|
;
|
|
|
|
AlterSeqStmt:
|
|
ALTER SEQUENCE qualified_name OptSeqList
|
|
{
|
|
AlterSeqStmt *n = makeNode(AlterSeqStmt);
|
|
n->sequence = $3;
|
|
n->options = $4;
|
|
$$ = (Node *)n;
|
|
}
|
|
;
|
|
|
|
OptSeqList: OptSeqList OptSeqElem { $$ = lappend($1, $2); }
|
|
| /*EMPTY*/ { $$ = NIL; }
|
|
;
|
|
|
|
OptSeqElem: CACHE NumericOnly
|
|
{
|
|
$$ = makeDefElem("cache", (Node *)$2);
|
|
}
|
|
| CYCLE
|
|
{
|
|
$$ = makeDefElem("cycle", (Node *)makeInteger(TRUE));
|
|
}
|
|
| NO CYCLE
|
|
{
|
|
$$ = makeDefElem("cycle", (Node *)makeInteger(FALSE));
|
|
}
|
|
| INCREMENT opt_by NumericOnly
|
|
{
|
|
$$ = makeDefElem("increment", (Node *)$3);
|
|
}
|
|
| MAXVALUE NumericOnly
|
|
{
|
|
$$ = makeDefElem("maxvalue", (Node *)$2);
|
|
}
|
|
| MINVALUE NumericOnly
|
|
{
|
|
$$ = makeDefElem("minvalue", (Node *)$2);
|
|
}
|
|
| NO MAXVALUE
|
|
{
|
|
$$ = makeDefElem("maxvalue", NULL);
|
|
}
|
|
| NO MINVALUE
|
|
{
|
|
$$ = makeDefElem("minvalue", NULL);
|
|
}
|
|
| START opt_with NumericOnly
|
|
{
|
|
$$ = makeDefElem("start", (Node *)$3);
|
|
}
|
|
| RESTART opt_with NumericOnly
|
|
{
|
|
$$ = makeDefElem("restart", (Node *)$3);
|
|
}
|
|
;
|
|
|
|
opt_by: BY {}
|
|
| /* empty */ {}
|
|
;
|
|
|
|
NumericOnly:
|
|
FloatOnly { $$ = $1; }
|
|
| IntegerOnly { $$ = $1; }
|
|
;
|
|
|
|
FloatOnly: FCONST { $$ = makeFloat($1); }
|
|
| '-' FCONST
|
|
{
|
|
$$ = makeFloat($2);
|
|
doNegateFloat($$);
|
|
}
|
|
;
|
|
|
|
IntegerOnly: SignedIconst { $$ = makeInteger($1); };
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* QUERIES :
|
|
* CREATE PROCEDURAL LANGUAGE ...
|
|
* DROP PROCEDURAL LANGUAGE ...
|
|
*
|
|
*****************************************************************************/
|
|
|
|
CreatePLangStmt:
|
|
CREATE opt_trusted opt_procedural LANGUAGE ColId_or_Sconst
|
|
{
|
|
CreatePLangStmt *n = makeNode(CreatePLangStmt);
|
|
n->plname = $5;
|
|
/* parameters are all to be supplied by system */
|
|
n->plhandler = NIL;
|
|
n->plvalidator = NIL;
|
|
n->pltrusted = false;
|
|
$$ = (Node *)n;
|
|
}
|
|
| CREATE opt_trusted opt_procedural LANGUAGE ColId_or_Sconst
|
|
HANDLER handler_name opt_validator opt_lancompiler
|
|
{
|
|
CreatePLangStmt *n = makeNode(CreatePLangStmt);
|
|
n->plname = $5;
|
|
n->plhandler = $7;
|
|
n->plvalidator = $8;
|
|
n->pltrusted = $2;
|
|
/* LANCOMPILER is now ignored entirely */
|
|
$$ = (Node *)n;
|
|
}
|
|
;
|
|
|
|
opt_trusted:
|
|
TRUSTED { $$ = TRUE; }
|
|
| /*EMPTY*/ { $$ = FALSE; }
|
|
;
|
|
|
|
/* This ought to be just func_name, but that causes reduce/reduce conflicts
|
|
* (CREATE LANGUAGE is the only place where func_name isn't followed by '(').
|
|
* Work around by using simple names, instead.
|
|
*/
|
|
handler_name:
|
|
name { $$ = list_make1(makeString($1)); }
|
|
| name attrs { $$ = lcons(makeString($1), $2); }
|
|
;
|
|
|
|
opt_validator:
|
|
VALIDATOR handler_name { $$ = $2; }
|
|
| /*EMPTY*/ { $$ = NIL; }
|
|
;
|
|
|
|
opt_lancompiler:
|
|
LANCOMPILER Sconst { $$ = $2; }
|
|
| /*EMPTY*/ { $$ = NULL; }
|
|
;
|
|
|
|
DropPLangStmt:
|
|
DROP opt_procedural LANGUAGE ColId_or_Sconst opt_drop_behavior
|
|
{
|
|
DropPLangStmt *n = makeNode(DropPLangStmt);
|
|
n->plname = $4;
|
|
n->behavior = $5;
|
|
$$ = (Node *)n;
|
|
}
|
|
;
|
|
|
|
opt_procedural:
|
|
PROCEDURAL {}
|
|
| /*EMPTY*/ {}
|
|
;
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* QUERY:
|
|
* CREATE TABLESPACE tablespace LOCATION '/path/to/tablespace/'
|
|
*
|
|
*****************************************************************************/
|
|
|
|
CreateTableSpaceStmt: CREATE TABLESPACE name OptTableSpaceOwner LOCATION Sconst
|
|
{
|
|
CreateTableSpaceStmt *n = makeNode(CreateTableSpaceStmt);
|
|
n->tablespacename = $3;
|
|
n->owner = $4;
|
|
n->location = $6;
|
|
$$ = (Node *) n;
|
|
}
|
|
;
|
|
|
|
OptTableSpaceOwner: OWNER name { $$ = $2; }
|
|
| /*EMPTY */ { $$ = NULL; }
|
|
;
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* QUERY :
|
|
* DROP TABLESPACE <tablespace>
|
|
*
|
|
* No need for drop behaviour as we cannot implement dependencies for
|
|
* objects in other databases; we can only support RESTRICT.
|
|
*
|
|
****************************************************************************/
|
|
|
|
DropTableSpaceStmt: DROP TABLESPACE name
|
|
{
|
|
DropTableSpaceStmt *n = makeNode(DropTableSpaceStmt);
|
|
n->tablespacename = $3;
|
|
$$ = (Node *) n;
|
|
}
|
|
;
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* QUERIES :
|
|
* CREATE TRIGGER ...
|
|
* DROP TRIGGER ...
|
|
*
|
|
*****************************************************************************/
|
|
|
|
CreateTrigStmt:
|
|
CREATE TRIGGER name TriggerActionTime TriggerEvents ON
|
|
qualified_name TriggerForSpec EXECUTE PROCEDURE
|
|
func_name '(' TriggerFuncArgs ')'
|
|
{
|
|
CreateTrigStmt *n = makeNode(CreateTrigStmt);
|
|
n->trigname = $3;
|
|
n->relation = $7;
|
|
n->funcname = $11;
|
|
n->args = $13;
|
|
n->before = $4;
|
|
n->row = $8;
|
|
memcpy(n->actions, $5, 4);
|
|
n->isconstraint = FALSE;
|
|
n->deferrable = FALSE;
|
|
n->initdeferred = FALSE;
|
|
n->constrrel = NULL;
|
|
$$ = (Node *)n;
|
|
}
|
|
| CREATE CONSTRAINT TRIGGER name AFTER TriggerEvents ON
|
|
qualified_name OptConstrFromTable
|
|
ConstraintAttributeSpec
|
|
FOR EACH ROW EXECUTE PROCEDURE
|
|
func_name '(' TriggerFuncArgs ')'
|
|
{
|
|
CreateTrigStmt *n = makeNode(CreateTrigStmt);
|
|
n->trigname = $4;
|
|
n->relation = $8;
|
|
n->funcname = $16;
|
|
n->args = $18;
|
|
n->before = FALSE;
|
|
n->row = TRUE;
|
|
memcpy(n->actions, $6, 4);
|
|
n->isconstraint = TRUE;
|
|
n->deferrable = ($10 & 1) != 0;
|
|
n->initdeferred = ($10 & 2) != 0;
|
|
|
|
n->constrrel = $9;
|
|
$$ = (Node *)n;
|
|
}
|
|
;
|
|
|
|
TriggerActionTime:
|
|
BEFORE { $$ = TRUE; }
|
|
| AFTER { $$ = FALSE; }
|
|
;
|
|
|
|
TriggerEvents:
|
|
TriggerOneEvent
|
|
{
|
|
char *e = palloc(4);
|
|
e[0] = $1; e[1] = '\0';
|
|
$$ = e;
|
|
}
|
|
| TriggerOneEvent OR TriggerOneEvent
|
|
{
|
|
char *e = palloc(4);
|
|
e[0] = $1; e[1] = $3; e[2] = '\0';
|
|
$$ = e;
|
|
}
|
|
| TriggerOneEvent OR TriggerOneEvent OR TriggerOneEvent
|
|
{
|
|
char *e = palloc(4);
|
|
e[0] = $1; e[1] = $3; e[2] = $5; e[3] = '\0';
|
|
$$ = e;
|
|
}
|
|
;
|
|
|
|
TriggerOneEvent:
|
|
INSERT { $$ = 'i'; }
|
|
| DELETE_P { $$ = 'd'; }
|
|
| UPDATE { $$ = 'u'; }
|
|
;
|
|
|
|
TriggerForSpec:
|
|
FOR TriggerForOpt TriggerForType
|
|
{
|
|
$$ = $3;
|
|
}
|
|
| /* EMPTY */
|
|
{
|
|
/*
|
|
* If ROW/STATEMENT not specified, default to
|
|
* STATEMENT, per SQL
|
|
*/
|
|
$$ = FALSE;
|
|
}
|
|
;
|
|
|
|
TriggerForOpt:
|
|
EACH {}
|
|
| /*EMPTY*/ {}
|
|
;
|
|
|
|
TriggerForType:
|
|
ROW { $$ = TRUE; }
|
|
| STATEMENT { $$ = FALSE; }
|
|
;
|
|
|
|
TriggerFuncArgs:
|
|
TriggerFuncArg { $$ = list_make1($1); }
|
|
| TriggerFuncArgs ',' TriggerFuncArg { $$ = lappend($1, $3); }
|
|
| /*EMPTY*/ { $$ = NIL; }
|
|
;
|
|
|
|
TriggerFuncArg:
|
|
ICONST
|
|
{
|
|
char buf[64];
|
|
snprintf(buf, sizeof(buf), "%d", $1);
|
|
$$ = makeString(pstrdup(buf));
|
|
}
|
|
| FCONST { $$ = makeString($1); }
|
|
| Sconst { $$ = makeString($1); }
|
|
| BCONST { $$ = makeString($1); }
|
|
| XCONST { $$ = makeString($1); }
|
|
| ColId { $$ = makeString($1); }
|
|
;
|
|
|
|
OptConstrFromTable:
|
|
FROM qualified_name { $$ = $2; }
|
|
| /*EMPTY*/ { $$ = NULL; }
|
|
;
|
|
|
|
ConstraintAttributeSpec:
|
|
ConstraintDeferrabilitySpec
|
|
{ $$ = $1; }
|
|
| ConstraintDeferrabilitySpec ConstraintTimeSpec
|
|
{
|
|
if ($1 == 0 && $2 != 0)
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_SYNTAX_ERROR),
|
|
errmsg("constraint declared INITIALLY DEFERRED must be DEFERRABLE")));
|
|
$$ = $1 | $2;
|
|
}
|
|
| ConstraintTimeSpec
|
|
{
|
|
if ($1 != 0)
|
|
$$ = 3;
|
|
else
|
|
$$ = 0;
|
|
}
|
|
| ConstraintTimeSpec ConstraintDeferrabilitySpec
|
|
{
|
|
if ($2 == 0 && $1 != 0)
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_SYNTAX_ERROR),
|
|
errmsg("constraint declared INITIALLY DEFERRED must be DEFERRABLE")));
|
|
$$ = $1 | $2;
|
|
}
|
|
| /*EMPTY*/
|
|
{ $$ = 0; }
|
|
;
|
|
|
|
ConstraintDeferrabilitySpec:
|
|
NOT DEFERRABLE { $$ = 0; }
|
|
| DEFERRABLE { $$ = 1; }
|
|
;
|
|
|
|
ConstraintTimeSpec:
|
|
INITIALLY IMMEDIATE { $$ = 0; }
|
|
| INITIALLY DEFERRED { $$ = 2; }
|
|
;
|
|
|
|
|
|
DropTrigStmt:
|
|
DROP TRIGGER name ON qualified_name opt_drop_behavior
|
|
{
|
|
DropPropertyStmt *n = makeNode(DropPropertyStmt);
|
|
n->relation = $5;
|
|
n->property = $3;
|
|
n->behavior = $6;
|
|
n->removeType = OBJECT_TRIGGER;
|
|
$$ = (Node *) n;
|
|
}
|
|
;
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* QUERIES :
|
|
* CREATE ASSERTION ...
|
|
* DROP ASSERTION ...
|
|
*
|
|
*****************************************************************************/
|
|
|
|
CreateAssertStmt:
|
|
CREATE ASSERTION name CHECK '(' a_expr ')'
|
|
ConstraintAttributeSpec
|
|
{
|
|
CreateTrigStmt *n = makeNode(CreateTrigStmt);
|
|
n->trigname = $3;
|
|
n->args = list_make1($6);
|
|
n->isconstraint = TRUE;
|
|
n->deferrable = ($8 & 1) != 0;
|
|
n->initdeferred = ($8 & 2) != 0;
|
|
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
|
errmsg("CREATE ASSERTION is not yet implemented")));
|
|
|
|
$$ = (Node *)n;
|
|
}
|
|
;
|
|
|
|
DropAssertStmt:
|
|
DROP ASSERTION name opt_drop_behavior
|
|
{
|
|
DropPropertyStmt *n = makeNode(DropPropertyStmt);
|
|
n->relation = NULL;
|
|
n->property = $3;
|
|
n->behavior = $4;
|
|
n->removeType = OBJECT_TRIGGER; /* XXX */
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
|
errmsg("DROP ASSERTION is not yet implemented")));
|
|
$$ = (Node *) n;
|
|
}
|
|
;
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* QUERY :
|
|
* define (aggregate,operator,type)
|
|
*
|
|
*****************************************************************************/
|
|
|
|
DefineStmt:
|
|
CREATE AGGREGATE func_name definition
|
|
{
|
|
DefineStmt *n = makeNode(DefineStmt);
|
|
n->kind = OBJECT_AGGREGATE;
|
|
n->defnames = $3;
|
|
n->definition = $4;
|
|
$$ = (Node *)n;
|
|
}
|
|
| CREATE OPERATOR any_operator definition
|
|
{
|
|
DefineStmt *n = makeNode(DefineStmt);
|
|
n->kind = OBJECT_OPERATOR;
|
|
n->defnames = $3;
|
|
n->definition = $4;
|
|
$$ = (Node *)n;
|
|
}
|
|
| CREATE TYPE_P any_name definition
|
|
{
|
|
DefineStmt *n = makeNode(DefineStmt);
|
|
n->kind = OBJECT_TYPE;
|
|
n->defnames = $3;
|
|
n->definition = $4;
|
|
$$ = (Node *)n;
|
|
}
|
|
| CREATE TYPE_P any_name AS '(' TableFuncElementList ')'
|
|
{
|
|
CompositeTypeStmt *n = makeNode(CompositeTypeStmt);
|
|
RangeVar *r = makeNode(RangeVar);
|
|
|
|
/* can't use qualified_name, sigh */
|
|
switch (list_length($3))
|
|
{
|
|
case 1:
|
|
r->catalogname = NULL;
|
|
r->schemaname = NULL;
|
|
r->relname = strVal(linitial($3));
|
|
break;
|
|
case 2:
|
|
r->catalogname = NULL;
|
|
r->schemaname = strVal(linitial($3));
|
|
r->relname = strVal(lsecond($3));
|
|
break;
|
|
case 3:
|
|
r->catalogname = strVal(linitial($3));
|
|
r->schemaname = strVal(lsecond($3));
|
|
r->relname = strVal(lthird($3));
|
|
break;
|
|
default:
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_SYNTAX_ERROR),
|
|
errmsg("improper qualified name (too many dotted names): %s",
|
|
NameListToString($3))));
|
|
break;
|
|
}
|
|
n->typevar = r;
|
|
n->coldeflist = $6;
|
|
$$ = (Node *)n;
|
|
}
|
|
;
|
|
|
|
definition: '(' def_list ')' { $$ = $2; }
|
|
;
|
|
|
|
def_list: def_elem { $$ = list_make1($1); }
|
|
| def_list ',' def_elem { $$ = lappend($1, $3); }
|
|
;
|
|
|
|
def_elem: ColLabel '=' def_arg
|
|
{
|
|
$$ = makeDefElem($1, (Node *)$3);
|
|
}
|
|
| ColLabel
|
|
{
|
|
$$ = makeDefElem($1, NULL);
|
|
}
|
|
;
|
|
|
|
/* Note: any simple identifier will be returned as a type name! */
|
|
def_arg: func_type { $$ = (Node *)$1; }
|
|
| qual_all_Op { $$ = (Node *)$1; }
|
|
| NumericOnly { $$ = (Node *)$1; }
|
|
| Sconst { $$ = (Node *)makeString($1); }
|
|
;
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* QUERIES :
|
|
* CREATE OPERATOR CLASS ...
|
|
* DROP OPERATOR CLASS ...
|
|
*
|
|
*****************************************************************************/
|
|
|
|
CreateOpClassStmt:
|
|
CREATE OPERATOR CLASS any_name opt_default FOR TYPE_P Typename
|
|
USING access_method AS opclass_item_list
|
|
{
|
|
CreateOpClassStmt *n = makeNode(CreateOpClassStmt);
|
|
n->opclassname = $4;
|
|
n->isDefault = $5;
|
|
n->datatype = $8;
|
|
n->amname = $10;
|
|
n->items = $12;
|
|
$$ = (Node *) n;
|
|
}
|
|
;
|
|
|
|
opclass_item_list:
|
|
opclass_item { $$ = list_make1($1); }
|
|
| opclass_item_list ',' opclass_item { $$ = lappend($1, $3); }
|
|
;
|
|
|
|
opclass_item:
|
|
OPERATOR Iconst any_operator opt_recheck
|
|
{
|
|
CreateOpClassItem *n = makeNode(CreateOpClassItem);
|
|
n->itemtype = OPCLASS_ITEM_OPERATOR;
|
|
n->name = $3;
|
|
n->args = NIL;
|
|
n->number = $2;
|
|
n->recheck = $4;
|
|
$$ = (Node *) n;
|
|
}
|
|
| OPERATOR Iconst any_operator '(' oper_argtypes ')' opt_recheck
|
|
{
|
|
CreateOpClassItem *n = makeNode(CreateOpClassItem);
|
|
n->itemtype = OPCLASS_ITEM_OPERATOR;
|
|
n->name = $3;
|
|
n->args = $5;
|
|
n->number = $2;
|
|
n->recheck = $7;
|
|
$$ = (Node *) n;
|
|
}
|
|
| FUNCTION Iconst func_name func_args
|
|
{
|
|
CreateOpClassItem *n = makeNode(CreateOpClassItem);
|
|
n->itemtype = OPCLASS_ITEM_FUNCTION;
|
|
n->name = $3;
|
|
n->args = extractArgTypes($4);
|
|
n->number = $2;
|
|
$$ = (Node *) n;
|
|
}
|
|
| STORAGE Typename
|
|
{
|
|
CreateOpClassItem *n = makeNode(CreateOpClassItem);
|
|
n->itemtype = OPCLASS_ITEM_STORAGETYPE;
|
|
n->storedtype = $2;
|
|
$$ = (Node *) n;
|
|
}
|
|
;
|
|
|
|
opt_default: DEFAULT { $$ = TRUE; }
|
|
| /*EMPTY*/ { $$ = FALSE; }
|
|
;
|
|
|
|
opt_recheck: RECHECK { $$ = TRUE; }
|
|
| /*EMPTY*/ { $$ = FALSE; }
|
|
;
|
|
|
|
|
|
DropOpClassStmt:
|
|
DROP OPERATOR CLASS any_name USING access_method opt_drop_behavior
|
|
{
|
|
RemoveOpClassStmt *n = makeNode(RemoveOpClassStmt);
|
|
n->opclassname = $4;
|
|
n->amname = $6;
|
|
n->behavior = $7;
|
|
$$ = (Node *) n;
|
|
}
|
|
;
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* QUERY:
|
|
*
|
|
* DROP OWNED BY username [, username ...] [ RESTRICT | CASCADE ]
|
|
* REASSIGN OWNED BY username [, username ...] TO username
|
|
*
|
|
*****************************************************************************/
|
|
DropOwnedStmt:
|
|
DROP OWNED BY name_list opt_drop_behavior
|
|
{
|
|
DropOwnedStmt *n = makeNode(DropOwnedStmt);
|
|
n->roles = $4;
|
|
n->behavior = $5;
|
|
$$ = (Node *)n;
|
|
}
|
|
;
|
|
|
|
ReassignOwnedStmt:
|
|
REASSIGN OWNED BY name_list TO name
|
|
{
|
|
ReassignOwnedStmt *n = makeNode(ReassignOwnedStmt);
|
|
n->roles = $4;
|
|
n->newrole = $6;
|
|
$$ = (Node *)n;
|
|
}
|
|
;
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* QUERY:
|
|
*
|
|
* DROP itemtype [ IF EXISTS ] itemname [, itemname ...]
|
|
* [ RESTRICT | CASCADE ]
|
|
*
|
|
*****************************************************************************/
|
|
|
|
DropStmt: DROP drop_type IF_P EXISTS any_name_list opt_drop_behavior
|
|
{
|
|
DropStmt *n = makeNode(DropStmt);
|
|
n->removeType = $2;
|
|
n->missing_ok = TRUE;
|
|
n->objects = $5;
|
|
n->behavior = $6;
|
|
$$ = (Node *)n;
|
|
}
|
|
| DROP drop_type any_name_list opt_drop_behavior
|
|
{
|
|
DropStmt *n = makeNode(DropStmt);
|
|
n->removeType = $2;
|
|
n->missing_ok = FALSE;
|
|
n->objects = $3;
|
|
n->behavior = $4;
|
|
$$ = (Node *)n;
|
|
}
|
|
;
|
|
|
|
|
|
drop_type: TABLE { $$ = OBJECT_TABLE; }
|
|
| SEQUENCE { $$ = OBJECT_SEQUENCE; }
|
|
| VIEW { $$ = OBJECT_VIEW; }
|
|
| INDEX { $$ = OBJECT_INDEX; }
|
|
| TYPE_P { $$ = OBJECT_TYPE; }
|
|
| DOMAIN_P { $$ = OBJECT_DOMAIN; }
|
|
| CONVERSION_P { $$ = OBJECT_CONVERSION; }
|
|
| SCHEMA { $$ = OBJECT_SCHEMA; }
|
|
;
|
|
|
|
any_name_list:
|
|
any_name { $$ = list_make1($1); }
|
|
| any_name_list ',' any_name { $$ = lappend($1, $3); }
|
|
;
|
|
|
|
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)); }
|
|
;
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* QUERY:
|
|
* truncate table relname1, relname2, ...
|
|
*
|
|
*****************************************************************************/
|
|
|
|
TruncateStmt:
|
|
TRUNCATE opt_table qualified_name_list
|
|
{
|
|
TruncateStmt *n = makeNode(TruncateStmt);
|
|
n->relations = $3;
|
|
$$ = (Node *)n;
|
|
}
|
|
;
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* The COMMENT ON statement can take different forms based upon the type of
|
|
* the object associated with the comment. The form of the statement is:
|
|
*
|
|
* COMMENT ON [ [ DATABASE | DOMAIN | INDEX | SEQUENCE | TABLE | TYPE | VIEW |
|
|
* CONVERSION | LANGUAGE | OPERATOR CLASS | LARGE OBJECT |
|
|
* CAST ] <objname> |
|
|
* AGGREGATE <aggname> (<aggtype>) |
|
|
* FUNCTION <funcname> (arg1, arg2, ...) |
|
|
* OPERATOR <op> (leftoperand_typ, rightoperand_typ) |
|
|
* TRIGGER <triggername> ON <relname> |
|
|
* RULE <rulename> ON <relname> ]
|
|
* IS 'text'
|
|
*
|
|
*****************************************************************************/
|
|
|
|
CommentStmt:
|
|
COMMENT ON comment_type any_name IS comment_text
|
|
{
|
|
CommentStmt *n = makeNode(CommentStmt);
|
|
n->objtype = $3;
|
|
n->objname = $4;
|
|
n->objargs = NIL;
|
|
n->comment = $6;
|
|
$$ = (Node *) n;
|
|
}
|
|
| COMMENT ON AGGREGATE func_name '(' aggr_argtype ')'
|
|
IS comment_text
|
|
{
|
|
CommentStmt *n = makeNode(CommentStmt);
|
|
n->objtype = OBJECT_AGGREGATE;
|
|
n->objname = $4;
|
|
n->objargs = list_make1($6);
|
|
n->comment = $9;
|
|
$$ = (Node *) n;
|
|
}
|
|
| COMMENT ON FUNCTION func_name func_args IS comment_text
|
|
{
|
|
CommentStmt *n = makeNode(CommentStmt);
|
|
n->objtype = OBJECT_FUNCTION;
|
|
n->objname = $4;
|
|
n->objargs = extractArgTypes($5);
|
|
n->comment = $7;
|
|
$$ = (Node *) n;
|
|
}
|
|
| COMMENT ON OPERATOR any_operator '(' oper_argtypes ')'
|
|
IS comment_text
|
|
{
|
|
CommentStmt *n = makeNode(CommentStmt);
|
|
n->objtype = OBJECT_OPERATOR;
|
|
n->objname = $4;
|
|
n->objargs = $6;
|
|
n->comment = $9;
|
|
$$ = (Node *) n;
|
|
}
|
|
| COMMENT ON CONSTRAINT name ON any_name IS comment_text
|
|
{
|
|
CommentStmt *n = makeNode(CommentStmt);
|
|
n->objtype = OBJECT_CONSTRAINT;
|
|
n->objname = lappend($6, makeString($4));
|
|
n->objargs = NIL;
|
|
n->comment = $8;
|
|
$$ = (Node *) n;
|
|
}
|
|
| COMMENT ON RULE name ON any_name IS comment_text
|
|
{
|
|
CommentStmt *n = makeNode(CommentStmt);
|
|
n->objtype = OBJECT_RULE;
|
|
n->objname = lappend($6, makeString($4));
|
|
n->objargs = NIL;
|
|
n->comment = $8;
|
|
$$ = (Node *) n;
|
|
}
|
|
| COMMENT ON RULE name IS comment_text
|
|
{
|
|
/* Obsolete syntax supported for awhile for compatibility */
|
|
CommentStmt *n = makeNode(CommentStmt);
|
|
n->objtype = OBJECT_RULE;
|
|
n->objname = list_make1(makeString($4));
|
|
n->objargs = NIL;
|
|
n->comment = $6;
|
|
$$ = (Node *) n;
|
|
}
|
|
| COMMENT ON TRIGGER name ON any_name IS comment_text
|
|
{
|
|
CommentStmt *n = makeNode(CommentStmt);
|
|
n->objtype = OBJECT_TRIGGER;
|
|
n->objname = lappend($6, makeString($4));
|
|
n->objargs = NIL;
|
|
n->comment = $8;
|
|
$$ = (Node *) n;
|
|
}
|
|
| COMMENT ON OPERATOR CLASS any_name USING access_method IS comment_text
|
|
{
|
|
CommentStmt *n = makeNode(CommentStmt);
|
|
n->objtype = OBJECT_OPCLASS;
|
|
n->objname = $5;
|
|
n->objargs = list_make1(makeString($7));
|
|
n->comment = $9;
|
|
$$ = (Node *) n;
|
|
}
|
|
| COMMENT ON LARGE_P OBJECT_P NumericOnly IS comment_text
|
|
{
|
|
CommentStmt *n = makeNode(CommentStmt);
|
|
n->objtype = OBJECT_LARGEOBJECT;
|
|
n->objname = list_make1($5);
|
|
n->objargs = NIL;
|
|
n->comment = $7;
|
|
$$ = (Node *) n;
|
|
}
|
|
| COMMENT ON CAST '(' Typename AS Typename ')' IS comment_text
|
|
{
|
|
CommentStmt *n = makeNode(CommentStmt);
|
|
n->objtype = OBJECT_CAST;
|
|
n->objname = list_make1($5);
|
|
n->objargs = list_make1($7);
|
|
n->comment = $10;
|
|
$$ = (Node *) n;
|
|
}
|
|
| COMMENT ON opt_procedural LANGUAGE any_name IS comment_text
|
|
{
|
|
CommentStmt *n = makeNode(CommentStmt);
|
|
n->objtype = OBJECT_LANGUAGE;
|
|
n->objname = $5;
|
|
n->objargs = NIL;
|
|
n->comment = $7;
|
|
$$ = (Node *) n;
|
|
}
|
|
;
|
|
|
|
comment_type:
|
|
COLUMN { $$ = OBJECT_COLUMN; }
|
|
| DATABASE { $$ = OBJECT_DATABASE; }
|
|
| SCHEMA { $$ = OBJECT_SCHEMA; }
|
|
| INDEX { $$ = OBJECT_INDEX; }
|
|
| SEQUENCE { $$ = OBJECT_SEQUENCE; }
|
|
| TABLE { $$ = OBJECT_TABLE; }
|
|
| DOMAIN_P { $$ = OBJECT_TYPE; }
|
|
| TYPE_P { $$ = OBJECT_TYPE; }
|
|
| VIEW { $$ = OBJECT_VIEW; }
|
|
| CONVERSION_P { $$ = OBJECT_CONVERSION; }
|
|
;
|
|
|
|
comment_text:
|
|
Sconst { $$ = $1; }
|
|
| NULL_P { $$ = NULL; }
|
|
;
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* QUERY:
|
|
* fetch/move
|
|
*
|
|
*****************************************************************************/
|
|
|
|
FetchStmt: FETCH fetch_direction from_in name
|
|
{
|
|
FetchStmt *n = (FetchStmt *) $2;
|
|
n->portalname = $4;
|
|
n->ismove = FALSE;
|
|
$$ = (Node *)n;
|
|
}
|
|
| FETCH name
|
|
{
|
|
FetchStmt *n = makeNode(FetchStmt);
|
|
n->direction = FETCH_FORWARD;
|
|
n->howMany = 1;
|
|
n->portalname = $2;
|
|
n->ismove = FALSE;
|
|
$$ = (Node *)n;
|
|
}
|
|
| MOVE fetch_direction from_in name
|
|
{
|
|
FetchStmt *n = (FetchStmt *) $2;
|
|
n->portalname = $4;
|
|
n->ismove = TRUE;
|
|
$$ = (Node *)n;
|
|
}
|
|
| MOVE name
|
|
{
|
|
FetchStmt *n = makeNode(FetchStmt);
|
|
n->direction = FETCH_FORWARD;
|
|
n->howMany = 1;
|
|
n->portalname = $2;
|
|
n->ismove = TRUE;
|
|
$$ = (Node *)n;
|
|
}
|
|
;
|
|
|
|
fetch_direction:
|
|
/*EMPTY*/
|
|
{
|
|
FetchStmt *n = makeNode(FetchStmt);
|
|
n->direction = FETCH_FORWARD;
|
|
n->howMany = 1;
|
|
$$ = (Node *)n;
|
|
}
|
|
| NEXT
|
|
{
|
|
FetchStmt *n = makeNode(FetchStmt);
|
|
n->direction = FETCH_FORWARD;
|
|
n->howMany = 1;
|
|
$$ = (Node *)n;
|
|
}
|
|
| PRIOR
|
|
{
|
|
FetchStmt *n = makeNode(FetchStmt);
|
|
n->direction = FETCH_BACKWARD;
|
|
n->howMany = 1;
|
|
$$ = (Node *)n;
|
|
}
|
|
| FIRST_P
|
|
{
|
|
FetchStmt *n = makeNode(FetchStmt);
|
|
n->direction = FETCH_ABSOLUTE;
|
|
n->howMany = 1;
|
|
$$ = (Node *)n;
|
|
}
|
|
| LAST_P
|
|
{
|
|
FetchStmt *n = makeNode(FetchStmt);
|
|
n->direction = FETCH_ABSOLUTE;
|
|
n->howMany = -1;
|
|
$$ = (Node *)n;
|
|
}
|
|
| ABSOLUTE_P SignedIconst
|
|
{
|
|
FetchStmt *n = makeNode(FetchStmt);
|
|
n->direction = FETCH_ABSOLUTE;
|
|
n->howMany = $2;
|
|
$$ = (Node *)n;
|
|
}
|
|
| RELATIVE_P SignedIconst
|
|
{
|
|
FetchStmt *n = makeNode(FetchStmt);
|
|
n->direction = FETCH_RELATIVE;
|
|
n->howMany = $2;
|
|
$$ = (Node *)n;
|
|
}
|
|
| SignedIconst
|
|
{
|
|
FetchStmt *n = makeNode(FetchStmt);
|
|
n->direction = FETCH_FORWARD;
|
|
n->howMany = $1;
|
|
$$ = (Node *)n;
|
|
}
|
|
| ALL
|
|
{
|
|
FetchStmt *n = makeNode(FetchStmt);
|
|
n->direction = FETCH_FORWARD;
|
|
n->howMany = FETCH_ALL;
|
|
$$ = (Node *)n;
|
|
}
|
|
| FORWARD
|
|
{
|
|
FetchStmt *n = makeNode(FetchStmt);
|
|
n->direction = FETCH_FORWARD;
|
|
n->howMany = 1;
|
|
$$ = (Node *)n;
|
|
}
|
|
| FORWARD SignedIconst
|
|
{
|
|
FetchStmt *n = makeNode(FetchStmt);
|
|
n->direction = FETCH_FORWARD;
|
|
n->howMany = $2;
|
|
$$ = (Node *)n;
|
|
}
|
|
| FORWARD ALL
|
|
{
|
|
FetchStmt *n = makeNode(FetchStmt);
|
|
n->direction = FETCH_FORWARD;
|
|
n->howMany = FETCH_ALL;
|
|
$$ = (Node *)n;
|
|
}
|
|
| BACKWARD
|
|
{
|
|
FetchStmt *n = makeNode(FetchStmt);
|
|
n->direction = FETCH_BACKWARD;
|
|
n->howMany = 1;
|
|
$$ = (Node *)n;
|
|
}
|
|
| BACKWARD SignedIconst
|
|
{
|
|
FetchStmt *n = makeNode(FetchStmt);
|
|
n->direction = FETCH_BACKWARD;
|
|
n->howMany = $2;
|
|
$$ = (Node *)n;
|
|
}
|
|
| BACKWARD ALL
|
|
{
|
|
FetchStmt *n = makeNode(FetchStmt);
|
|
n->direction = FETCH_BACKWARD;
|
|
n->howMany = FETCH_ALL;
|
|
$$ = (Node *)n;
|
|
}
|
|
;
|
|
|
|
from_in: FROM {}
|
|
| IN_P {}
|
|
;
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* GRANT and REVOKE statements
|
|
*
|
|
*****************************************************************************/
|
|
|
|
GrantStmt: GRANT privileges ON privilege_target TO grantee_list
|
|
opt_grant_grant_option
|
|
{
|
|
GrantStmt *n = makeNode(GrantStmt);
|
|
n->is_grant = true;
|
|
n->privileges = $2;
|
|
n->objtype = ($4)->objtype;
|
|
n->objects = ($4)->objs;
|
|
n->grantees = $6;
|
|
n->grant_option = $7;
|
|
$$ = (Node*)n;
|
|
}
|
|
;
|
|
|
|
RevokeStmt:
|
|
REVOKE privileges ON privilege_target
|
|
FROM grantee_list opt_drop_behavior
|
|
{
|
|
GrantStmt *n = makeNode(GrantStmt);
|
|
n->is_grant = false;
|
|
n->grant_option = false;
|
|
n->privileges = $2;
|
|
n->objtype = ($4)->objtype;
|
|
n->objects = ($4)->objs;
|
|
n->grantees = $6;
|
|
n->behavior = $7;
|
|
$$ = (Node *)n;
|
|
}
|
|
| REVOKE GRANT OPTION FOR privileges ON privilege_target
|
|
FROM grantee_list opt_drop_behavior
|
|
{
|
|
GrantStmt *n = makeNode(GrantStmt);
|
|
n->is_grant = false;
|
|
n->grant_option = true;
|
|
n->privileges = $5;
|
|
n->objtype = ($7)->objtype;
|
|
n->objects = ($7)->objs;
|
|
n->grantees = $9;
|
|
n->behavior = $10;
|
|
$$ = (Node *)n;
|
|
}
|
|
;
|
|
|
|
|
|
/*
|
|
* A privilege list is represented as a list of strings; the validity of
|
|
* the privilege names gets checked at execution. This is a bit annoying
|
|
* but we have little choice because of the syntactic conflict with lists
|
|
* of role names in GRANT/REVOKE. What's more, we have to call out in
|
|
* the "privilege" production any reserved keywords that need to be usable
|
|
* as privilege names.
|
|
*/
|
|
|
|
/* either ALL [PRIVILEGES] or a list of individual privileges */
|
|
privileges: privilege_list
|
|
{ $$ = $1; }
|
|
| ALL
|
|
{ $$ = NIL; }
|
|
| ALL PRIVILEGES
|
|
{ $$ = NIL; }
|
|
;
|
|
|
|
privilege_list: privilege
|
|
{ $$ = list_make1(makeString($1)); }
|
|
| privilege_list ',' privilege
|
|
{ $$ = lappend($1, makeString($3)); }
|
|
;
|
|
|
|
privilege: SELECT { $$ = pstrdup($1); }
|
|
| REFERENCES { $$ = pstrdup($1); }
|
|
| CREATE { $$ = pstrdup($1); }
|
|
| ColId { $$ = $1; }
|
|
;
|
|
|
|
|
|
/* Don't bother trying to fold the first two rules into one using
|
|
* opt_table. You're going to get conflicts.
|
|
*/
|
|
privilege_target:
|
|
qualified_name_list
|
|
{
|
|
PrivTarget *n = makeNode(PrivTarget);
|
|
n->objtype = ACL_OBJECT_RELATION;
|
|
n->objs = $1;
|
|
$$ = n;
|
|
}
|
|
| TABLE qualified_name_list
|
|
{
|
|
PrivTarget *n = makeNode(PrivTarget);
|
|
n->objtype = ACL_OBJECT_RELATION;
|
|
n->objs = $2;
|
|
$$ = n;
|
|
}
|
|
| SEQUENCE qualified_name_list
|
|
{
|
|
PrivTarget *n = makeNode(PrivTarget);
|
|
n->objtype = ACL_OBJECT_SEQUENCE;
|
|
n->objs = $2;
|
|
$$ = n;
|
|
}
|
|
| FUNCTION function_with_argtypes_list
|
|
{
|
|
PrivTarget *n = makeNode(PrivTarget);
|
|
n->objtype = ACL_OBJECT_FUNCTION;
|
|
n->objs = $2;
|
|
$$ = n;
|
|
}
|
|
| DATABASE name_list
|
|
{
|
|
PrivTarget *n = makeNode(PrivTarget);
|
|
n->objtype = ACL_OBJECT_DATABASE;
|
|
n->objs = $2;
|
|
$$ = n;
|
|
}
|
|
| LANGUAGE name_list
|
|
{
|
|
PrivTarget *n = makeNode(PrivTarget);
|
|
n->objtype = ACL_OBJECT_LANGUAGE;
|
|
n->objs = $2;
|
|
$$ = n;
|
|
}
|
|
| SCHEMA name_list
|
|
{
|
|
PrivTarget *n = makeNode(PrivTarget);
|
|
n->objtype = ACL_OBJECT_NAMESPACE;
|
|
n->objs = $2;
|
|
$$ = n;
|
|
}
|
|
| TABLESPACE name_list
|
|
{
|
|
PrivTarget *n = makeNode(PrivTarget);
|
|
n->objtype = ACL_OBJECT_TABLESPACE;
|
|
n->objs = $2;
|
|
$$ = n;
|
|
}
|
|
;
|
|
|
|
|
|
grantee_list:
|
|
grantee { $$ = list_make1($1); }
|
|
| grantee_list ',' grantee { $$ = lappend($1, $3); }
|
|
;
|
|
|
|
grantee: RoleId
|
|
{
|
|
PrivGrantee *n = makeNode(PrivGrantee);
|
|
/* This hack lets us avoid reserving PUBLIC as a keyword*/
|
|
if (strcmp($1, "public") == 0)
|
|
n->rolname = NULL;
|
|
else
|
|
n->rolname = $1;
|
|
$$ = (Node *)n;
|
|
}
|
|
| GROUP_P RoleId
|
|
{
|
|
PrivGrantee *n = makeNode(PrivGrantee);
|
|
/* Treat GROUP PUBLIC as a synonym for PUBLIC */
|
|
if (strcmp($2, "public") == 0)
|
|
n->rolname = NULL;
|
|
else
|
|
n->rolname = $2;
|
|
$$ = (Node *)n;
|
|
}
|
|
;
|
|
|
|
|
|
opt_grant_grant_option:
|
|
WITH GRANT OPTION { $$ = TRUE; }
|
|
| /*EMPTY*/ { $$ = FALSE; }
|
|
;
|
|
|
|
function_with_argtypes_list:
|
|
function_with_argtypes { $$ = list_make1($1); }
|
|
| function_with_argtypes_list ',' function_with_argtypes
|
|
{ $$ = lappend($1, $3); }
|
|
;
|
|
|
|
function_with_argtypes:
|
|
func_name func_args
|
|
{
|
|
FuncWithArgs *n = makeNode(FuncWithArgs);
|
|
n->funcname = $1;
|
|
n->funcargs = extractArgTypes($2);
|
|
$$ = n;
|
|
}
|
|
;
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* GRANT and REVOKE ROLE statements
|
|
*
|
|
*****************************************************************************/
|
|
|
|
GrantRoleStmt:
|
|
GRANT privilege_list TO name_list opt_grant_admin_option opt_granted_by
|
|
{
|
|
GrantRoleStmt *n = makeNode(GrantRoleStmt);
|
|
n->is_grant = true;
|
|
n->granted_roles = $2;
|
|
n->grantee_roles = $4;
|
|
n->admin_opt = $5;
|
|
n->grantor = $6;
|
|
$$ = (Node*)n;
|
|
}
|
|
;
|
|
|
|
RevokeRoleStmt:
|
|
REVOKE privilege_list FROM name_list opt_granted_by opt_drop_behavior
|
|
{
|
|
GrantRoleStmt *n = makeNode(GrantRoleStmt);
|
|
n->is_grant = false;
|
|
n->admin_opt = false;
|
|
n->granted_roles = $2;
|
|
n->grantee_roles = $4;
|
|
n->behavior = $6;
|
|
$$ = (Node*)n;
|
|
}
|
|
| REVOKE ADMIN OPTION FOR privilege_list FROM name_list opt_granted_by opt_drop_behavior
|
|
{
|
|
GrantRoleStmt *n = makeNode(GrantRoleStmt);
|
|
n->is_grant = false;
|
|
n->admin_opt = true;
|
|
n->granted_roles = $5;
|
|
n->grantee_roles = $7;
|
|
n->behavior = $9;
|
|
$$ = (Node*)n;
|
|
}
|
|
;
|
|
|
|
opt_grant_admin_option: WITH ADMIN OPTION { $$ = TRUE; }
|
|
| /*EMPTY*/ { $$ = FALSE; }
|
|
;
|
|
|
|
opt_granted_by: GRANTED BY RoleId { $$ = $3; }
|
|
| /*EMPTY*/ { $$ = NULL; }
|
|
;
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* QUERY:
|
|
* create index <indexname> on <relname>
|
|
* [ using <access> ] "(" ( <col> [ using <opclass> ] )+ ")"
|
|
* [ tablespace <tablespacename> ] [ where <predicate> ]
|
|
*
|
|
* Note: we cannot put TABLESPACE clause after WHERE clause unless we are
|
|
* willing to make TABLESPACE a fully reserved word.
|
|
*****************************************************************************/
|
|
|
|
IndexStmt: CREATE index_opt_unique INDEX index_name ON qualified_name
|
|
access_method_clause '(' index_params ')' OptTableSpace where_clause
|
|
{
|
|
IndexStmt *n = makeNode(IndexStmt);
|
|
n->unique = $2;
|
|
n->idxname = $4;
|
|
n->relation = $6;
|
|
n->accessMethod = $7;
|
|
n->indexParams = $9;
|
|
n->tableSpace = $11;
|
|
n->whereClause = $12;
|
|
$$ = (Node *)n;
|
|
}
|
|
;
|
|
|
|
index_opt_unique:
|
|
UNIQUE { $$ = TRUE; }
|
|
| /*EMPTY*/ { $$ = FALSE; }
|
|
;
|
|
|
|
access_method_clause:
|
|
USING access_method { $$ = $2; }
|
|
| /*EMPTY*/ { $$ = DEFAULT_INDEX_TYPE; }
|
|
;
|
|
|
|
index_params: index_elem { $$ = list_make1($1); }
|
|
| index_params ',' index_elem { $$ = lappend($1, $3); }
|
|
;
|
|
|
|
/*
|
|
* Index attributes can be either simple column references, or arbitrary
|
|
* expressions in parens. For backwards-compatibility reasons, we allow
|
|
* an expression that's just a function call to be written without parens.
|
|
*/
|
|
index_elem: ColId opt_class
|
|
{
|
|
$$ = makeNode(IndexElem);
|
|
$$->name = $1;
|
|
$$->expr = NULL;
|
|
$$->opclass = $2;
|
|
}
|
|
| func_expr opt_class
|
|
{
|
|
$$ = makeNode(IndexElem);
|
|
$$->name = NULL;
|
|
$$->expr = $1;
|
|
$$->opclass = $2;
|
|
}
|
|
| '(' a_expr ')' opt_class
|
|
{
|
|
$$ = makeNode(IndexElem);
|
|
$$->name = NULL;
|
|
$$->expr = $2;
|
|
$$->opclass = $4;
|
|
}
|
|
;
|
|
|
|
opt_class: any_name { $$ = $1; }
|
|
| USING any_name { $$ = $2; }
|
|
| /*EMPTY*/ { $$ = NIL; }
|
|
;
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* QUERY:
|
|
* create [or replace] function <fname>
|
|
* [(<type-1> { , <type-n>})]
|
|
* returns <type-r>
|
|
* as <filename or code in language as appropriate>
|
|
* language <lang> [with parameters]
|
|
*
|
|
*****************************************************************************/
|
|
|
|
CreateFunctionStmt:
|
|
CREATE opt_or_replace FUNCTION func_name func_args
|
|
RETURNS func_return createfunc_opt_list opt_definition
|
|
{
|
|
CreateFunctionStmt *n = makeNode(CreateFunctionStmt);
|
|
n->replace = $2;
|
|
n->funcname = $4;
|
|
n->parameters = $5;
|
|
n->returnType = $7;
|
|
n->options = $8;
|
|
n->withClause = $9;
|
|
$$ = (Node *)n;
|
|
}
|
|
| CREATE opt_or_replace FUNCTION func_name func_args
|
|
createfunc_opt_list opt_definition
|
|
{
|
|
CreateFunctionStmt *n = makeNode(CreateFunctionStmt);
|
|
n->replace = $2;
|
|
n->funcname = $4;
|
|
n->parameters = $5;
|
|
n->returnType = NULL;
|
|
n->options = $6;
|
|
n->withClause = $7;
|
|
$$ = (Node *)n;
|
|
}
|
|
;
|
|
|
|
opt_or_replace:
|
|
OR REPLACE { $$ = TRUE; }
|
|
| /*EMPTY*/ { $$ = FALSE; }
|
|
;
|
|
|
|
func_args: '(' func_args_list ')' { $$ = $2; }
|
|
| '(' ')' { $$ = NIL; }
|
|
;
|
|
|
|
func_args_list:
|
|
func_arg { $$ = list_make1($1); }
|
|
| func_args_list ',' func_arg { $$ = lappend($1, $3); }
|
|
;
|
|
|
|
/*
|
|
* The style with arg_class first is SQL99 standard, but Oracle puts
|
|
* param_name first; accept both since it's likely people will try both
|
|
* anyway. Don't bother trying to save productions by letting arg_class
|
|
* have an empty alternative ... you'll get shift/reduce conflicts.
|
|
*
|
|
* We can catch over-specified arguments here if we want to,
|
|
* but for now better to silently swallow typmod, etc.
|
|
* - thomas 2000-03-22
|
|
*/
|
|
func_arg:
|
|
arg_class param_name func_type
|
|
{
|
|
FunctionParameter *n = makeNode(FunctionParameter);
|
|
n->name = $2;
|
|
n->argType = $3;
|
|
n->mode = $1;
|
|
$$ = n;
|
|
}
|
|
| param_name arg_class func_type
|
|
{
|
|
FunctionParameter *n = makeNode(FunctionParameter);
|
|
n->name = $1;
|
|
n->argType = $3;
|
|
n->mode = $2;
|
|
$$ = n;
|
|
}
|
|
| param_name func_type
|
|
{
|
|
FunctionParameter *n = makeNode(FunctionParameter);
|
|
n->name = $1;
|
|
n->argType = $2;
|
|
n->mode = FUNC_PARAM_IN;
|
|
$$ = n;
|
|
}
|
|
| arg_class func_type
|
|
{
|
|
FunctionParameter *n = makeNode(FunctionParameter);
|
|
n->name = NULL;
|
|
n->argType = $2;
|
|
n->mode = $1;
|
|
$$ = n;
|
|
}
|
|
| func_type
|
|
{
|
|
FunctionParameter *n = makeNode(FunctionParameter);
|
|
n->name = NULL;
|
|
n->argType = $1;
|
|
n->mode = FUNC_PARAM_IN;
|
|
$$ = n;
|
|
}
|
|
;
|
|
|
|
/* INOUT is SQL99 standard, IN OUT is for Oracle compatibility */
|
|
arg_class: IN_P { $$ = FUNC_PARAM_IN; }
|
|
| OUT_P { $$ = FUNC_PARAM_OUT; }
|
|
| INOUT { $$ = FUNC_PARAM_INOUT; }
|
|
| IN_P OUT_P { $$ = FUNC_PARAM_INOUT; }
|
|
;
|
|
|
|
/*
|
|
* Ideally param_name should be ColId, but that causes too many conflicts.
|
|
*/
|
|
param_name: function_name
|
|
;
|
|
|
|
func_return:
|
|
func_type
|
|
{
|
|
/* We can catch over-specified results here if we want to,
|
|
* but for now better to silently swallow typmod, etc.
|
|
* - thomas 2000-03-22
|
|
*/
|
|
$$ = $1;
|
|
}
|
|
;
|
|
|
|
/*
|
|
* We would like to make the %TYPE productions here be ColId attrs etc,
|
|
* but that causes reduce/reduce conflicts. type_name is next best choice.
|
|
*/
|
|
func_type: Typename { $$ = $1; }
|
|
| type_name attrs '%' TYPE_P
|
|
{
|
|
$$ = makeNode(TypeName);
|
|
$$->names = lcons(makeString($1), $2);
|
|
$$->pct_type = true;
|
|
$$->typmod = -1;
|
|
}
|
|
| SETOF type_name attrs '%' TYPE_P
|
|
{
|
|
$$ = makeNode(TypeName);
|
|
$$->names = lcons(makeString($2), $3);
|
|
$$->pct_type = true;
|
|
$$->typmod = -1;
|
|
$$->setof = TRUE;
|
|
}
|
|
;
|
|
|
|
|
|
createfunc_opt_list:
|
|
/* Must be at least one to prevent conflict */
|
|
createfunc_opt_item { $$ = list_make1($1); }
|
|
| createfunc_opt_list createfunc_opt_item { $$ = lappend($1, $2); }
|
|
;
|
|
|
|
/*
|
|
* Options common to both CREATE FUNCTION and ALTER FUNCTION
|
|
*/
|
|
common_func_opt_item:
|
|
CALLED ON NULL_P INPUT_P
|
|
{
|
|
$$ = makeDefElem("strict", (Node *)makeInteger(FALSE));
|
|
}
|
|
| RETURNS NULL_P ON NULL_P INPUT_P
|
|
{
|
|
$$ = makeDefElem("strict", (Node *)makeInteger(TRUE));
|
|
}
|
|
| STRICT_P
|
|
{
|
|
$$ = makeDefElem("strict", (Node *)makeInteger(TRUE));
|
|
}
|
|
| IMMUTABLE
|
|
{
|
|
$$ = makeDefElem("volatility", (Node *)makeString("immutable"));
|
|
}
|
|
| STABLE
|
|
{
|
|
$$ = makeDefElem("volatility", (Node *)makeString("stable"));
|
|
}
|
|
| VOLATILE
|
|
{
|
|
$$ = makeDefElem("volatility", (Node *)makeString("volatile"));
|
|
}
|
|
| EXTERNAL SECURITY DEFINER
|
|
{
|
|
$$ = makeDefElem("security", (Node *)makeInteger(TRUE));
|
|
}
|
|
| EXTERNAL SECURITY INVOKER
|
|
{
|
|
$$ = makeDefElem("security", (Node *)makeInteger(FALSE));
|
|
}
|
|
| SECURITY DEFINER
|
|
{
|
|
$$ = makeDefElem("security", (Node *)makeInteger(TRUE));
|
|
}
|
|
| SECURITY INVOKER
|
|
{
|
|
$$ = makeDefElem("security", (Node *)makeInteger(FALSE));
|
|
}
|
|
;
|
|
|
|
createfunc_opt_item:
|
|
AS func_as
|
|
{
|
|
$$ = makeDefElem("as", (Node *)$2);
|
|
}
|
|
| LANGUAGE ColId_or_Sconst
|
|
{
|
|
$$ = makeDefElem("language", (Node *)makeString($2));
|
|
}
|
|
| common_func_opt_item
|
|
{
|
|
$$ = $1;
|
|
}
|
|
;
|
|
|
|
func_as: Sconst { $$ = list_make1(makeString($1)); }
|
|
| Sconst ',' Sconst
|
|
{
|
|
$$ = list_make2(makeString($1), makeString($3));
|
|
}
|
|
;
|
|
|
|
opt_definition:
|
|
WITH definition { $$ = $2; }
|
|
| /*EMPTY*/ { $$ = NIL; }
|
|
;
|
|
|
|
/*****************************************************************************
|
|
* ALTER FUNCTION
|
|
*
|
|
* RENAME and OWNER subcommands are already provided by the generic
|
|
* ALTER infrastructure, here we just specify alterations that can
|
|
* only be applied to functions.
|
|
*
|
|
*****************************************************************************/
|
|
AlterFunctionStmt:
|
|
ALTER FUNCTION function_with_argtypes alterfunc_opt_list opt_restrict
|
|
{
|
|
AlterFunctionStmt *n = makeNode(AlterFunctionStmt);
|
|
n->func = $3;
|
|
n->actions = $4;
|
|
$$ = (Node *) n;
|
|
}
|
|
;
|
|
|
|
alterfunc_opt_list:
|
|
/* At least one option must be specified */
|
|
common_func_opt_item { $$ = list_make1($1); }
|
|
| alterfunc_opt_list common_func_opt_item { $$ = lappend($1, $2); }
|
|
;
|
|
|
|
/* Ignored, merely for SQL compliance */
|
|
opt_restrict:
|
|
RESTRICT
|
|
| /* EMPTY */
|
|
;
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* QUERY:
|
|
*
|
|
* DROP FUNCTION funcname (arg1, arg2, ...) [ RESTRICT | CASCADE ]
|
|
* DROP AGGREGATE aggname (aggtype) [ RESTRICT | CASCADE ]
|
|
* DROP OPERATOR opname (leftoperand_typ, rightoperand_typ) [ RESTRICT | CASCADE ]
|
|
*
|
|
*****************************************************************************/
|
|
|
|
RemoveFuncStmt:
|
|
DROP FUNCTION func_name func_args opt_drop_behavior
|
|
{
|
|
RemoveFuncStmt *n = makeNode(RemoveFuncStmt);
|
|
n->funcname = $3;
|
|
n->args = extractArgTypes($4);
|
|
n->behavior = $5;
|
|
$$ = (Node *)n;
|
|
}
|
|
;
|
|
|
|
RemoveAggrStmt:
|
|
DROP AGGREGATE func_name '(' aggr_argtype ')' opt_drop_behavior
|
|
{
|
|
RemoveAggrStmt *n = makeNode(RemoveAggrStmt);
|
|
n->aggname = $3;
|
|
n->aggtype = $5;
|
|
n->behavior = $7;
|
|
$$ = (Node *)n;
|
|
}
|
|
;
|
|
|
|
aggr_argtype:
|
|
Typename { $$ = $1; }
|
|
| '*' { $$ = NULL; }
|
|
;
|
|
|
|
RemoveOperStmt:
|
|
DROP OPERATOR any_operator '(' oper_argtypes ')' opt_drop_behavior
|
|
{
|
|
RemoveOperStmt *n = makeNode(RemoveOperStmt);
|
|
n->opname = $3;
|
|
n->args = $5;
|
|
n->behavior = $7;
|
|
$$ = (Node *)n;
|
|
}
|
|
;
|
|
|
|
oper_argtypes:
|
|
Typename
|
|
{
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_SYNTAX_ERROR),
|
|
errmsg("missing argument"),
|
|
errhint("Use NONE to denote the missing argument of a unary operator.")));
|
|
}
|
|
| Typename ',' Typename
|
|
{ $$ = list_make2($1, $3); }
|
|
| NONE ',' Typename /* left unary */
|
|
{ $$ = list_make2(NULL, $3); }
|
|
| Typename ',' NONE /* right unary */
|
|
{ $$ = list_make2($1, NULL); }
|
|
;
|
|
|
|
any_operator:
|
|
all_Op
|
|
{ $$ = list_make1(makeString($1)); }
|
|
| ColId '.' any_operator
|
|
{ $$ = lcons(makeString($1), $3); }
|
|
;
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* CREATE CAST / DROP CAST
|
|
*
|
|
*****************************************************************************/
|
|
|
|
CreateCastStmt: CREATE CAST '(' Typename AS Typename ')'
|
|
WITH FUNCTION function_with_argtypes cast_context
|
|
{
|
|
CreateCastStmt *n = makeNode(CreateCastStmt);
|
|
n->sourcetype = $4;
|
|
n->targettype = $6;
|
|
n->func = $10;
|
|
n->context = (CoercionContext) $11;
|
|
$$ = (Node *)n;
|
|
}
|
|
| CREATE CAST '(' Typename AS Typename ')'
|
|
WITHOUT FUNCTION cast_context
|
|
{
|
|
CreateCastStmt *n = makeNode(CreateCastStmt);
|
|
n->sourcetype = $4;
|
|
n->targettype = $6;
|
|
n->func = NULL;
|
|
n->context = (CoercionContext) $10;
|
|
$$ = (Node *)n;
|
|
}
|
|
;
|
|
|
|
cast_context: AS IMPLICIT_P { $$ = COERCION_IMPLICIT; }
|
|
| AS ASSIGNMENT { $$ = COERCION_ASSIGNMENT; }
|
|
| /*EMPTY*/ { $$ = COERCION_EXPLICIT; }
|
|
;
|
|
|
|
|
|
DropCastStmt: DROP CAST '(' Typename AS Typename ')' opt_drop_behavior
|
|
{
|
|
DropCastStmt *n = makeNode(DropCastStmt);
|
|
n->sourcetype = $4;
|
|
n->targettype = $6;
|
|
n->behavior = $8;
|
|
$$ = (Node *)n;
|
|
}
|
|
;
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* QUERY:
|
|
*
|
|
* REINDEX type <name> [FORCE]
|
|
*
|
|
* FORCE no longer does anything, but we accept it for backwards compatibility
|
|
*****************************************************************************/
|
|
|
|
ReindexStmt:
|
|
REINDEX reindex_type qualified_name opt_force
|
|
{
|
|
ReindexStmt *n = makeNode(ReindexStmt);
|
|
n->kind = $2;
|
|
n->relation = $3;
|
|
n->name = NULL;
|
|
$$ = (Node *)n;
|
|
}
|
|
| REINDEX SYSTEM_P name opt_force
|
|
{
|
|
ReindexStmt *n = makeNode(ReindexStmt);
|
|
n->kind = OBJECT_DATABASE;
|
|
n->name = $3;
|
|
n->relation = NULL;
|
|
n->do_system = true;
|
|
n->do_user = false;
|
|
$$ = (Node *)n;
|
|
}
|
|
| REINDEX DATABASE name opt_force
|
|
{
|
|
ReindexStmt *n = makeNode(ReindexStmt);
|
|
n->kind = OBJECT_DATABASE;
|
|
n->name = $3;
|
|
n->relation = NULL;
|
|
n->do_system = true;
|
|
n->do_user = true;
|
|
$$ = (Node *)n;
|
|
}
|
|
;
|
|
|
|
reindex_type:
|
|
INDEX { $$ = OBJECT_INDEX; }
|
|
| TABLE { $$ = OBJECT_TABLE; }
|
|
;
|
|
|
|
opt_force: FORCE { $$ = TRUE; }
|
|
| /* EMPTY */ { $$ = FALSE; }
|
|
;
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* ALTER THING name RENAME TO newname
|
|
*
|
|
*****************************************************************************/
|
|
|
|
RenameStmt: ALTER AGGREGATE func_name '(' aggr_argtype ')' RENAME TO name
|
|
{
|
|
RenameStmt *n = makeNode(RenameStmt);
|
|
n->renameType = OBJECT_AGGREGATE;
|
|
n->object = $3;
|
|
n->objarg = list_make1($5);
|
|
n->newname = $9;
|
|
$$ = (Node *)n;
|
|
}
|
|
| ALTER CONVERSION_P any_name RENAME TO name
|
|
{
|
|
RenameStmt *n = makeNode(RenameStmt);
|
|
n->renameType = OBJECT_CONVERSION;
|
|
n->object = $3;
|
|
n->newname = $6;
|
|
$$ = (Node *)n;
|
|
}
|
|
| ALTER DATABASE database_name RENAME TO database_name
|
|
{
|
|
RenameStmt *n = makeNode(RenameStmt);
|
|
n->renameType = OBJECT_DATABASE;
|
|
n->subname = $3;
|
|
n->newname = $6;
|
|
$$ = (Node *)n;
|
|
}
|
|
| ALTER FUNCTION func_name func_args RENAME TO name
|
|
{
|
|
RenameStmt *n = makeNode(RenameStmt);
|
|
n->renameType = OBJECT_FUNCTION;
|
|
n->object = $3;
|
|
n->objarg = extractArgTypes($4);
|
|
n->newname = $7;
|
|
$$ = (Node *)n;
|
|
}
|
|
| ALTER GROUP_P RoleId RENAME TO RoleId
|
|
{
|
|
RenameStmt *n = makeNode(RenameStmt);
|
|
n->renameType = OBJECT_ROLE;
|
|
n->subname = $3;
|
|
n->newname = $6;
|
|
$$ = (Node *)n;
|
|
}
|
|
| ALTER LANGUAGE name RENAME TO name
|
|
{
|
|
RenameStmt *n = makeNode(RenameStmt);
|
|
n->renameType = OBJECT_LANGUAGE;
|
|
n->subname = $3;
|
|
n->newname = $6;
|
|
$$ = (Node *)n;
|
|
}
|
|
| ALTER OPERATOR CLASS any_name USING access_method RENAME TO name
|
|
{
|
|
RenameStmt *n = makeNode(RenameStmt);
|
|
n->renameType = OBJECT_OPCLASS;
|
|
n->object = $4;
|
|
n->subname = $6;
|
|
n->newname = $9;
|
|
$$ = (Node *)n;
|
|
}
|
|
| ALTER SCHEMA name RENAME TO name
|
|
{
|
|
RenameStmt *n = makeNode(RenameStmt);
|
|
n->renameType = OBJECT_SCHEMA;
|
|
n->subname = $3;
|
|
n->newname = $6;
|
|
$$ = (Node *)n;
|
|
}
|
|
| ALTER TABLE relation_expr RENAME TO name
|
|
{
|
|
RenameStmt *n = makeNode(RenameStmt);
|
|
n->renameType = OBJECT_TABLE;
|
|
n->relation = $3;
|
|
n->subname = NULL;
|
|
n->newname = $6;
|
|
$$ = (Node *)n;
|
|
}
|
|
| ALTER INDEX relation_expr RENAME TO name
|
|
{
|
|
RenameStmt *n = makeNode(RenameStmt);
|
|
n->renameType = OBJECT_INDEX;
|
|
n->relation = $3;
|
|
n->subname = NULL;
|
|
n->newname = $6;
|
|
$$ = (Node *)n;
|
|
}
|
|
| ALTER TABLE relation_expr RENAME opt_column name TO name
|
|
{
|
|
RenameStmt *n = makeNode(RenameStmt);
|
|
n->renameType = OBJECT_COLUMN;
|
|
n->relation = $3;
|
|
n->subname = $6;
|
|
n->newname = $8;
|
|
$$ = (Node *)n;
|
|
}
|
|
| ALTER TRIGGER name ON relation_expr RENAME TO name
|
|
{
|
|
RenameStmt *n = makeNode(RenameStmt);
|
|
n->renameType = OBJECT_TRIGGER;
|
|
n->relation = $5;
|
|
n->subname = $3;
|
|
n->newname = $8;
|
|
$$ = (Node *)n;
|
|
}
|
|
| ALTER ROLE RoleId RENAME TO RoleId
|
|
{
|
|
RenameStmt *n = makeNode(RenameStmt);
|
|
n->renameType = OBJECT_ROLE;
|
|
n->subname = $3;
|
|
n->newname = $6;
|
|
$$ = (Node *)n;
|
|
}
|
|
| ALTER USER RoleId RENAME TO RoleId
|
|
{
|
|
RenameStmt *n = makeNode(RenameStmt);
|
|
n->renameType = OBJECT_ROLE;
|
|
n->subname = $3;
|
|
n->newname = $6;
|
|
$$ = (Node *)n;
|
|
}
|
|
| ALTER TABLESPACE name RENAME TO name
|
|
{
|
|
RenameStmt *n = makeNode(RenameStmt);
|
|
n->renameType = OBJECT_TABLESPACE;
|
|
n->subname = $3;
|
|
n->newname = $6;
|
|
$$ = (Node *)n;
|
|
}
|
|
;
|
|
|
|
opt_column: COLUMN { $$ = COLUMN; }
|
|
| /*EMPTY*/ { $$ = 0; }
|
|
;
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* ALTER THING name SET SCHEMA name
|
|
*
|
|
*****************************************************************************/
|
|
|
|
AlterObjectSchemaStmt:
|
|
ALTER AGGREGATE func_name '(' aggr_argtype ')' SET SCHEMA name
|
|
{
|
|
AlterObjectSchemaStmt *n = makeNode(AlterObjectSchemaStmt);
|
|
n->objectType = OBJECT_AGGREGATE;
|
|
n->object = $3;
|
|
n->objarg = list_make1($5);
|
|
n->newschema = $9;
|
|
$$ = (Node *)n;
|
|
}
|
|
| ALTER DOMAIN_P any_name SET SCHEMA name
|
|
{
|
|
AlterObjectSchemaStmt *n = makeNode(AlterObjectSchemaStmt);
|
|
n->objectType = OBJECT_DOMAIN;
|
|
n->object = $3;
|
|
n->newschema = $6;
|
|
$$ = (Node *)n;
|
|
}
|
|
| ALTER FUNCTION func_name func_args SET SCHEMA name
|
|
{
|
|
AlterObjectSchemaStmt *n = makeNode(AlterObjectSchemaStmt);
|
|
n->objectType = OBJECT_FUNCTION;
|
|
n->object = $3;
|
|
n->objarg = extractArgTypes($4);
|
|
n->newschema = $7;
|
|
$$ = (Node *)n;
|
|
}
|
|
| ALTER SEQUENCE relation_expr SET SCHEMA name
|
|
{
|
|
AlterObjectSchemaStmt *n = makeNode(AlterObjectSchemaStmt);
|
|
n->objectType = OBJECT_SEQUENCE;
|
|
n->relation = $3;
|
|
n->newschema = $6;
|
|
$$ = (Node *)n;
|
|
}
|
|
| ALTER TABLE relation_expr SET SCHEMA name
|
|
{
|
|
AlterObjectSchemaStmt *n = makeNode(AlterObjectSchemaStmt);
|
|
n->objectType = OBJECT_TABLE;
|
|
n->relation = $3;
|
|
n->newschema = $6;
|
|
$$ = (Node *)n;
|
|
}
|
|
| ALTER TYPE_P any_name SET SCHEMA name
|
|
{
|
|
AlterObjectSchemaStmt *n = makeNode(AlterObjectSchemaStmt);
|
|
n->objectType = OBJECT_TYPE;
|
|
n->object = $3;
|
|
n->newschema = $6;
|
|
$$ = (Node *)n;
|
|
}
|
|
;
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* ALTER THING name OWNER TO newname
|
|
*
|
|
*****************************************************************************/
|
|
|
|
AlterOwnerStmt: ALTER AGGREGATE func_name '(' aggr_argtype ')' OWNER TO RoleId
|
|
{
|
|
AlterOwnerStmt *n = makeNode(AlterOwnerStmt);
|
|
n->objectType = OBJECT_AGGREGATE;
|
|
n->object = $3;
|
|
n->objarg = list_make1($5);
|
|
n->newowner = $9;
|
|
$$ = (Node *)n;
|
|
}
|
|
| ALTER CONVERSION_P any_name OWNER TO RoleId
|
|
{
|
|
AlterOwnerStmt *n = makeNode(AlterOwnerStmt);
|
|
n->objectType = OBJECT_CONVERSION;
|
|
n->object = $3;
|
|
n->newowner = $6;
|
|
$$ = (Node *)n;
|
|
}
|
|
| ALTER DATABASE database_name OWNER TO RoleId
|
|
{
|
|
AlterOwnerStmt *n = makeNode(AlterOwnerStmt);
|
|
n->objectType = OBJECT_DATABASE;
|
|
n->object = list_make1($3);
|
|
n->newowner = $6;
|
|
$$ = (Node *)n;
|
|
}
|
|
| ALTER DOMAIN_P any_name OWNER TO RoleId
|
|
{
|
|
AlterOwnerStmt *n = makeNode(AlterOwnerStmt);
|
|
n->objectType = OBJECT_DOMAIN;
|
|
n->object = $3;
|
|
n->newowner = $6;
|
|
$$ = (Node *)n;
|
|
}
|
|
| ALTER FUNCTION func_name func_args OWNER TO RoleId
|
|
{
|
|
AlterOwnerStmt *n = makeNode(AlterOwnerStmt);
|
|
n->objectType = OBJECT_FUNCTION;
|
|
n->object = $3;
|
|
n->objarg = extractArgTypes($4);
|
|
n->newowner = $7;
|
|
$$ = (Node *)n;
|
|
}
|
|
| ALTER OPERATOR any_operator '(' oper_argtypes ')' OWNER TO RoleId
|
|
{
|
|
AlterOwnerStmt *n = makeNode(AlterOwnerStmt);
|
|
n->objectType = OBJECT_OPERATOR;
|
|
n->object = $3;
|
|
n->objarg = $5;
|
|
n->newowner = $9;
|
|
$$ = (Node *)n;
|
|
}
|
|
| ALTER OPERATOR CLASS any_name USING access_method OWNER TO RoleId
|
|
{
|
|
AlterOwnerStmt *n = makeNode(AlterOwnerStmt);
|
|
n->objectType = OBJECT_OPCLASS;
|
|
n->object = $4;
|
|
n->addname = $6;
|
|
n->newowner = $9;
|
|
$$ = (Node *)n;
|
|
}
|
|
| ALTER SCHEMA name OWNER TO RoleId
|
|
{
|
|
AlterOwnerStmt *n = makeNode(AlterOwnerStmt);
|
|
n->objectType = OBJECT_SCHEMA;
|
|
n->object = list_make1($3);
|
|
n->newowner = $6;
|
|
$$ = (Node *)n;
|
|
}
|
|
| ALTER TYPE_P any_name OWNER TO RoleId
|
|
{
|
|
AlterOwnerStmt *n = makeNode(AlterOwnerStmt);
|
|
n->objectType = OBJECT_TYPE;
|
|
n->object = $3;
|
|
n->newowner = $6;
|
|
$$ = (Node *)n;
|
|
}
|
|
| ALTER TABLESPACE name OWNER TO RoleId
|
|
{
|
|
AlterOwnerStmt *n = makeNode(AlterOwnerStmt);
|
|
n->objectType = OBJECT_TABLESPACE;
|
|
n->object = list_make1($3);
|
|
n->newowner = $6;
|
|
$$ = (Node *)n;
|
|
}
|
|
;
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* QUERY: Define Rewrite Rule
|
|
*
|
|
*****************************************************************************/
|
|
|
|
RuleStmt: CREATE opt_or_replace RULE name AS
|
|
{ QueryIsRule=TRUE; }
|
|
ON event TO qualified_name where_clause
|
|
DO opt_instead RuleActionList
|
|
{
|
|
RuleStmt *n = makeNode(RuleStmt);
|
|
n->replace = $2;
|
|
n->relation = $10;
|
|
n->rulename = $4;
|
|
n->whereClause = $11;
|
|
n->event = $8;
|
|
n->instead = $13;
|
|
n->actions = $14;
|
|
$$ = (Node *)n;
|
|
QueryIsRule=FALSE;
|
|
}
|
|
;
|
|
|
|
RuleActionList:
|
|
NOTHING { $$ = NIL; }
|
|
| RuleActionStmt { $$ = list_make1($1); }
|
|
| '(' RuleActionMulti ')' { $$ = $2; }
|
|
;
|
|
|
|
/* the thrashing around here is to discard "empty" statements... */
|
|
RuleActionMulti:
|
|
RuleActionMulti ';' RuleActionStmtOrEmpty
|
|
{ if ($3 != NULL)
|
|
$$ = lappend($1, $3);
|
|
else
|
|
$$ = $1;
|
|
}
|
|
| RuleActionStmtOrEmpty
|
|
{ if ($1 != NULL)
|
|
$$ = list_make1($1);
|
|
else
|
|
$$ = NIL;
|
|
}
|
|
;
|
|
|
|
RuleActionStmt:
|
|
SelectStmt
|
|
| InsertStmt
|
|
| UpdateStmt
|
|
| DeleteStmt
|
|
| NotifyStmt
|
|
;
|
|
|
|
RuleActionStmtOrEmpty:
|
|
RuleActionStmt { $$ = $1; }
|
|
| /*EMPTY*/ { $$ = NULL; }
|
|
;
|
|
|
|
/* change me to select, update, etc. some day */
|
|
event: SELECT { $$ = CMD_SELECT; }
|
|
| UPDATE { $$ = CMD_UPDATE; }
|
|
| DELETE_P { $$ = CMD_DELETE; }
|
|
| INSERT { $$ = CMD_INSERT; }
|
|
;
|
|
|
|
opt_instead:
|
|
INSTEAD { $$ = TRUE; }
|
|
| ALSO { $$ = FALSE; }
|
|
| /*EMPTY*/ { $$ = FALSE; }
|
|
;
|
|
|
|
|
|
DropRuleStmt:
|
|
DROP RULE name ON qualified_name opt_drop_behavior
|
|
{
|
|
DropPropertyStmt *n = makeNode(DropPropertyStmt);
|
|
n->relation = $5;
|
|
n->property = $3;
|
|
n->behavior = $6;
|
|
n->removeType = OBJECT_RULE;
|
|
$$ = (Node *) n;
|
|
}
|
|
;
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* QUERY:
|
|
* NOTIFY <qualified_name> can appear both in rule bodies and
|
|
* as a query-level command
|
|
*
|
|
*****************************************************************************/
|
|
|
|
NotifyStmt: NOTIFY qualified_name
|
|
{
|
|
NotifyStmt *n = makeNode(NotifyStmt);
|
|
n->relation = $2;
|
|
$$ = (Node *)n;
|
|
}
|
|
;
|
|
|
|
ListenStmt: LISTEN qualified_name
|
|
{
|
|
ListenStmt *n = makeNode(ListenStmt);
|
|
n->relation = $2;
|
|
$$ = (Node *)n;
|
|
}
|
|
;
|
|
|
|
UnlistenStmt:
|
|
UNLISTEN qualified_name
|
|
{
|
|
UnlistenStmt *n = makeNode(UnlistenStmt);
|
|
n->relation = $2;
|
|
$$ = (Node *)n;
|
|
}
|
|
| UNLISTEN '*'
|
|
{
|
|
UnlistenStmt *n = makeNode(UnlistenStmt);
|
|
n->relation = makeNode(RangeVar);
|
|
n->relation->relname = "*";
|
|
n->relation->schemaname = NULL;
|
|
$$ = (Node *)n;
|
|
}
|
|
;
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* Transactions:
|
|
*
|
|
* BEGIN / COMMIT / ROLLBACK
|
|
* (also older versions END / ABORT)
|
|
*
|
|
*****************************************************************************/
|
|
|
|
TransactionStmt:
|
|
ABORT_P opt_transaction
|
|
{
|
|
TransactionStmt *n = makeNode(TransactionStmt);
|
|
n->kind = TRANS_STMT_ROLLBACK;
|
|
n->options = NIL;
|
|
$$ = (Node *)n;
|
|
}
|
|
| BEGIN_P opt_transaction transaction_mode_list_or_empty
|
|
{
|
|
TransactionStmt *n = makeNode(TransactionStmt);
|
|
n->kind = TRANS_STMT_BEGIN;
|
|
n->options = $3;
|
|
$$ = (Node *)n;
|
|
}
|
|
| START TRANSACTION transaction_mode_list_or_empty
|
|
{
|
|
TransactionStmt *n = makeNode(TransactionStmt);
|
|
n->kind = TRANS_STMT_START;
|
|
n->options = $3;
|
|
$$ = (Node *)n;
|
|
}
|
|
| COMMIT opt_transaction
|
|
{
|
|
TransactionStmt *n = makeNode(TransactionStmt);
|
|
n->kind = TRANS_STMT_COMMIT;
|
|
n->options = NIL;
|
|
$$ = (Node *)n;
|
|
}
|
|
| END_P opt_transaction
|
|
{
|
|
TransactionStmt *n = makeNode(TransactionStmt);
|
|
n->kind = TRANS_STMT_COMMIT;
|
|
n->options = NIL;
|
|
$$ = (Node *)n;
|
|
}
|
|
| ROLLBACK opt_transaction
|
|
{
|
|
TransactionStmt *n = makeNode(TransactionStmt);
|
|
n->kind = TRANS_STMT_ROLLBACK;
|
|
n->options = NIL;
|
|
$$ = (Node *)n;
|
|
}
|
|
| SAVEPOINT ColId
|
|
{
|
|
TransactionStmt *n = makeNode(TransactionStmt);
|
|
n->kind = TRANS_STMT_SAVEPOINT;
|
|
n->options = list_make1(makeDefElem("savepoint_name",
|
|
(Node *)makeString($2)));
|
|
$$ = (Node *)n;
|
|
}
|
|
| RELEASE SAVEPOINT ColId
|
|
{
|
|
TransactionStmt *n = makeNode(TransactionStmt);
|
|
n->kind = TRANS_STMT_RELEASE;
|
|
n->options = list_make1(makeDefElem("savepoint_name",
|
|
(Node *)makeString($3)));
|
|
$$ = (Node *)n;
|
|
}
|
|
| RELEASE ColId
|
|
{
|
|
TransactionStmt *n = makeNode(TransactionStmt);
|
|
n->kind = TRANS_STMT_RELEASE;
|
|
n->options = list_make1(makeDefElem("savepoint_name",
|
|
(Node *)makeString($2)));
|
|
$$ = (Node *)n;
|
|
}
|
|
| ROLLBACK opt_transaction TO SAVEPOINT ColId
|
|
{
|
|
TransactionStmt *n = makeNode(TransactionStmt);
|
|
n->kind = TRANS_STMT_ROLLBACK_TO;
|
|
n->options = list_make1(makeDefElem("savepoint_name",
|
|
(Node *)makeString($5)));
|
|
$$ = (Node *)n;
|
|
}
|
|
| ROLLBACK opt_transaction TO ColId
|
|
{
|
|
TransactionStmt *n = makeNode(TransactionStmt);
|
|
n->kind = TRANS_STMT_ROLLBACK_TO;
|
|
n->options = list_make1(makeDefElem("savepoint_name",
|
|
(Node *)makeString($4)));
|
|
$$ = (Node *)n;
|
|
}
|
|
| PREPARE TRANSACTION Sconst
|
|
{
|
|
TransactionStmt *n = makeNode(TransactionStmt);
|
|
n->kind = TRANS_STMT_PREPARE;
|
|
n->gid = $3;
|
|
$$ = (Node *)n;
|
|
}
|
|
| COMMIT PREPARED Sconst
|
|
{
|
|
TransactionStmt *n = makeNode(TransactionStmt);
|
|
n->kind = TRANS_STMT_COMMIT_PREPARED;
|
|
n->gid = $3;
|
|
$$ = (Node *)n;
|
|
}
|
|
| ROLLBACK PREPARED Sconst
|
|
{
|
|
TransactionStmt *n = makeNode(TransactionStmt);
|
|
n->kind = TRANS_STMT_ROLLBACK_PREPARED;
|
|
n->gid = $3;
|
|
$$ = (Node *)n;
|
|
}
|
|
;
|
|
|
|
opt_transaction: WORK {}
|
|
| TRANSACTION {}
|
|
| /*EMPTY*/ {}
|
|
;
|
|
|
|
transaction_mode_item:
|
|
ISOLATION LEVEL iso_level
|
|
{ $$ = makeDefElem("transaction_isolation",
|
|
makeStringConst($3, NULL)); }
|
|
| READ ONLY
|
|
{ $$ = makeDefElem("transaction_read_only",
|
|
makeIntConst(TRUE)); }
|
|
| READ WRITE
|
|
{ $$ = makeDefElem("transaction_read_only",
|
|
makeIntConst(FALSE)); }
|
|
;
|
|
|
|
/* Syntax with commas is SQL-spec, without commas is Postgres historical */
|
|
transaction_mode_list:
|
|
transaction_mode_item
|
|
{ $$ = list_make1($1); }
|
|
| transaction_mode_list ',' transaction_mode_item
|
|
{ $$ = lappend($1, $3); }
|
|
| transaction_mode_list transaction_mode_item
|
|
{ $$ = lappend($1, $2); }
|
|
;
|
|
|
|
transaction_mode_list_or_empty:
|
|
transaction_mode_list
|
|
| /* EMPTY */
|
|
{ $$ = NIL; }
|
|
;
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* QUERY:
|
|
* CREATE [ OR REPLACE ] [ TEMP ] VIEW <viewname> '('target-list ')' AS <query>
|
|
*
|
|
*****************************************************************************/
|
|
|
|
ViewStmt: CREATE OptTemp VIEW qualified_name opt_column_list
|
|
AS SelectStmt
|
|
{
|
|
ViewStmt *n = makeNode(ViewStmt);
|
|
n->replace = false;
|
|
n->view = $4;
|
|
n->view->istemp = $2;
|
|
n->aliases = $5;
|
|
n->query = (Query *) $7;
|
|
$$ = (Node *) n;
|
|
}
|
|
| CREATE OR REPLACE OptTemp VIEW qualified_name opt_column_list
|
|
AS SelectStmt
|
|
{
|
|
ViewStmt *n = makeNode(ViewStmt);
|
|
n->replace = true;
|
|
n->view = $6;
|
|
n->view->istemp = $4;
|
|
n->aliases = $7;
|
|
n->query = (Query *) $9;
|
|
$$ = (Node *) n;
|
|
}
|
|
;
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* QUERY:
|
|
* load "filename"
|
|
*
|
|
*****************************************************************************/
|
|
|
|
LoadStmt: LOAD file_name
|
|
{
|
|
LoadStmt *n = makeNode(LoadStmt);
|
|
n->filename = $2;
|
|
$$ = (Node *)n;
|
|
}
|
|
;
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* CREATE DATABASE
|
|
*
|
|
*****************************************************************************/
|
|
|
|
CreatedbStmt:
|
|
CREATE DATABASE database_name opt_with createdb_opt_list
|
|
{
|
|
CreatedbStmt *n = makeNode(CreatedbStmt);
|
|
n->dbname = $3;
|
|
n->options = $5;
|
|
$$ = (Node *)n;
|
|
}
|
|
;
|
|
|
|
createdb_opt_list:
|
|
createdb_opt_list createdb_opt_item { $$ = lappend($1, $2); }
|
|
| /* EMPTY */ { $$ = NIL; }
|
|
;
|
|
|
|
createdb_opt_item:
|
|
TABLESPACE opt_equal name
|
|
{
|
|
$$ = makeDefElem("tablespace", (Node *)makeString($3));
|
|
}
|
|
| TABLESPACE opt_equal DEFAULT
|
|
{
|
|
$$ = makeDefElem("tablespace", NULL);
|
|
}
|
|
| LOCATION opt_equal Sconst
|
|
{
|
|
$$ = makeDefElem("location", (Node *)makeString($3));
|
|
}
|
|
| LOCATION opt_equal DEFAULT
|
|
{
|
|
$$ = makeDefElem("location", NULL);
|
|
}
|
|
| TEMPLATE opt_equal name
|
|
{
|
|
$$ = makeDefElem("template", (Node *)makeString($3));
|
|
}
|
|
| TEMPLATE opt_equal DEFAULT
|
|
{
|
|
$$ = makeDefElem("template", NULL);
|
|
}
|
|
| ENCODING opt_equal Sconst
|
|
{
|
|
$$ = makeDefElem("encoding", (Node *)makeString($3));
|
|
}
|
|
| ENCODING opt_equal Iconst
|
|
{
|
|
$$ = makeDefElem("encoding", (Node *)makeInteger($3));
|
|
}
|
|
| ENCODING opt_equal DEFAULT
|
|
{
|
|
$$ = makeDefElem("encoding", NULL);
|
|
}
|
|
| CONNECTION LIMIT opt_equal SignedIconst
|
|
{
|
|
$$ = makeDefElem("connectionlimit", (Node *)makeInteger($4));
|
|
}
|
|
| OWNER opt_equal name
|
|
{
|
|
$$ = makeDefElem("owner", (Node *)makeString($3));
|
|
}
|
|
| OWNER opt_equal DEFAULT
|
|
{
|
|
$$ = makeDefElem("owner", NULL);
|
|
}
|
|
;
|
|
|
|
/*
|
|
* Though the equals sign doesn't match other WITH options, pg_dump uses
|
|
* equals for backward compatibility, and it doesn't seem worth removing it.
|
|
*/
|
|
opt_equal: '=' {}
|
|
| /*EMPTY*/ {}
|
|
;
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* ALTER DATABASE
|
|
*
|
|
*****************************************************************************/
|
|
|
|
AlterDatabaseStmt:
|
|
ALTER DATABASE database_name opt_with alterdb_opt_list
|
|
{
|
|
AlterDatabaseStmt *n = makeNode(AlterDatabaseStmt);
|
|
n->dbname = $3;
|
|
n->options = $5;
|
|
$$ = (Node *)n;
|
|
}
|
|
;
|
|
|
|
AlterDatabaseSetStmt:
|
|
ALTER DATABASE database_name SET set_rest
|
|
{
|
|
AlterDatabaseSetStmt *n = makeNode(AlterDatabaseSetStmt);
|
|
n->dbname = $3;
|
|
n->variable = $5->name;
|
|
n->value = $5->args;
|
|
$$ = (Node *)n;
|
|
}
|
|
| ALTER DATABASE database_name VariableResetStmt
|
|
{
|
|
AlterDatabaseSetStmt *n = makeNode(AlterDatabaseSetStmt);
|
|
n->dbname = $3;
|
|
n->variable = ((VariableResetStmt *)$4)->name;
|
|
n->value = NIL;
|
|
$$ = (Node *)n;
|
|
}
|
|
;
|
|
|
|
|
|
alterdb_opt_list:
|
|
alterdb_opt_list alterdb_opt_item { $$ = lappend($1, $2); }
|
|
| /* EMPTY */ { $$ = NIL; }
|
|
;
|
|
|
|
alterdb_opt_item:
|
|
CONNECTION LIMIT opt_equal SignedIconst
|
|
{
|
|
$$ = makeDefElem("connectionlimit", (Node *)makeInteger($4));
|
|
}
|
|
;
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* DROP DATABASE [ IF EXISTS ]
|
|
*
|
|
* This is implicitly CASCADE, no need for drop behavior
|
|
*****************************************************************************/
|
|
|
|
DropdbStmt: DROP DATABASE database_name
|
|
{
|
|
DropdbStmt *n = makeNode(DropdbStmt);
|
|
n->dbname = $3;
|
|
n->missing_ok = FALSE;
|
|
$$ = (Node *)n;
|
|
}
|
|
| DROP DATABASE IF_P EXISTS database_name
|
|
{
|
|
DropdbStmt *n = makeNode(DropdbStmt);
|
|
n->dbname = $5;
|
|
n->missing_ok = TRUE;
|
|
$$ = (Node *)n;
|
|
}
|
|
;
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* Manipulate a domain
|
|
*
|
|
*****************************************************************************/
|
|
|
|
CreateDomainStmt:
|
|
CREATE DOMAIN_P any_name opt_as Typename ColQualList
|
|
{
|
|
CreateDomainStmt *n = makeNode(CreateDomainStmt);
|
|
n->domainname = $3;
|
|
n->typename = $5;
|
|
n->constraints = $6;
|
|
$$ = (Node *)n;
|
|
}
|
|
;
|
|
|
|
AlterDomainStmt:
|
|
/* ALTER DOMAIN <domain> {SET DEFAULT <expr>|DROP DEFAULT} */
|
|
ALTER DOMAIN_P any_name alter_column_default
|
|
{
|
|
AlterDomainStmt *n = makeNode(AlterDomainStmt);
|
|
n->subtype = 'T';
|
|
n->typename = $3;
|
|
n->def = $4;
|
|
$$ = (Node *)n;
|
|
}
|
|
/* ALTER DOMAIN <domain> DROP NOT NULL */
|
|
| ALTER DOMAIN_P any_name DROP NOT NULL_P
|
|
{
|
|
AlterDomainStmt *n = makeNode(AlterDomainStmt);
|
|
n->subtype = 'N';
|
|
n->typename = $3;
|
|
$$ = (Node *)n;
|
|
}
|
|
/* ALTER DOMAIN <domain> SET NOT NULL */
|
|
| ALTER DOMAIN_P any_name SET NOT NULL_P
|
|
{
|
|
AlterDomainStmt *n = makeNode(AlterDomainStmt);
|
|
n->subtype = 'O';
|
|
n->typename = $3;
|
|
$$ = (Node *)n;
|
|
}
|
|
/* ALTER DOMAIN <domain> ADD CONSTRAINT ... */
|
|
| ALTER DOMAIN_P any_name ADD_P TableConstraint
|
|
{
|
|
AlterDomainStmt *n = makeNode(AlterDomainStmt);
|
|
n->subtype = 'C';
|
|
n->typename = $3;
|
|
n->def = $5;
|
|
$$ = (Node *)n;
|
|
}
|
|
/* ALTER DOMAIN <domain> DROP CONSTRAINT <name> [RESTRICT|CASCADE] */
|
|
| ALTER DOMAIN_P any_name DROP CONSTRAINT name opt_drop_behavior
|
|
{
|
|
AlterDomainStmt *n = makeNode(AlterDomainStmt);
|
|
n->subtype = 'X';
|
|
n->typename = $3;
|
|
n->name = $6;
|
|
n->behavior = $7;
|
|
$$ = (Node *)n;
|
|
}
|
|
;
|
|
|
|
opt_as: AS {}
|
|
| /* EMPTY */ {}
|
|
;
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* Manipulate a conversion
|
|
*
|
|
* CREATE [DEFAULT] CONVERSION <conversion_name>
|
|
* FOR <encoding_name> TO <encoding_name> FROM <func_name>
|
|
*
|
|
*****************************************************************************/
|
|
|
|
CreateConversionStmt:
|
|
CREATE opt_default CONVERSION_P any_name FOR Sconst
|
|
TO Sconst FROM any_name
|
|
{
|
|
CreateConversionStmt *n = makeNode(CreateConversionStmt);
|
|
n->conversion_name = $4;
|
|
n->for_encoding_name = $6;
|
|
n->to_encoding_name = $8;
|
|
n->func_name = $10;
|
|
n->def = $2;
|
|
$$ = (Node *)n;
|
|
}
|
|
;
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* QUERY:
|
|
* cluster <index_name> on <qualified_name>
|
|
* cluster <qualified_name>
|
|
* cluster
|
|
*
|
|
*****************************************************************************/
|
|
|
|
ClusterStmt:
|
|
CLUSTER index_name ON qualified_name
|
|
{
|
|
ClusterStmt *n = makeNode(ClusterStmt);
|
|
n->relation = $4;
|
|
n->indexname = $2;
|
|
$$ = (Node*)n;
|
|
}
|
|
| CLUSTER qualified_name
|
|
{
|
|
ClusterStmt *n = makeNode(ClusterStmt);
|
|
n->relation = $2;
|
|
n->indexname = NULL;
|
|
$$ = (Node*)n;
|
|
}
|
|
| CLUSTER
|
|
{
|
|
ClusterStmt *n = makeNode(ClusterStmt);
|
|
n->relation = NULL;
|
|
n->indexname = NULL;
|
|
$$ = (Node*)n;
|
|
}
|
|
;
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* QUERY:
|
|
* vacuum
|
|
* analyze
|
|
*
|
|
*****************************************************************************/
|
|
|
|
VacuumStmt: VACUUM opt_full opt_freeze opt_verbose
|
|
{
|
|
VacuumStmt *n = makeNode(VacuumStmt);
|
|
n->vacuum = true;
|
|
n->analyze = false;
|
|
n->full = $2;
|
|
n->freeze = $3;
|
|
n->verbose = $4;
|
|
n->relation = NULL;
|
|
n->va_cols = NIL;
|
|
$$ = (Node *)n;
|
|
}
|
|
| VACUUM opt_full opt_freeze opt_verbose qualified_name
|
|
{
|
|
VacuumStmt *n = makeNode(VacuumStmt);
|
|
n->vacuum = true;
|
|
n->analyze = false;
|
|
n->full = $2;
|
|
n->freeze = $3;
|
|
n->verbose = $4;
|
|
n->relation = $5;
|
|
n->va_cols = NIL;
|
|
$$ = (Node *)n;
|
|
}
|
|
| VACUUM opt_full opt_freeze opt_verbose AnalyzeStmt
|
|
{
|
|
VacuumStmt *n = (VacuumStmt *) $5;
|
|
n->vacuum = true;
|
|
n->full = $2;
|
|
n->freeze = $3;
|
|
n->verbose |= $4;
|
|
$$ = (Node *)n;
|
|
}
|
|
;
|
|
|
|
AnalyzeStmt:
|
|
analyze_keyword opt_verbose
|
|
{
|
|
VacuumStmt *n = makeNode(VacuumStmt);
|
|
n->vacuum = false;
|
|
n->analyze = true;
|
|
n->full = false;
|
|
n->freeze = false;
|
|
n->verbose = $2;
|
|
n->relation = NULL;
|
|
n->va_cols = NIL;
|
|
$$ = (Node *)n;
|
|
}
|
|
| analyze_keyword opt_verbose qualified_name opt_name_list
|
|
{
|
|
VacuumStmt *n = makeNode(VacuumStmt);
|
|
n->vacuum = false;
|
|
n->analyze = true;
|
|
n->full = false;
|
|
n->freeze = false;
|
|
n->verbose = $2;
|
|
n->relation = $3;
|
|
n->va_cols = $4;
|
|
$$ = (Node *)n;
|
|
}
|
|
;
|
|
|
|
analyze_keyword:
|
|
ANALYZE {}
|
|
| ANALYSE /* British */ {}
|
|
;
|
|
|
|
opt_verbose:
|
|
VERBOSE { $$ = TRUE; }
|
|
| /*EMPTY*/ { $$ = FALSE; }
|
|
;
|
|
|
|
opt_full: FULL { $$ = TRUE; }
|
|
| /*EMPTY*/ { $$ = FALSE; }
|
|
;
|
|
|
|
opt_freeze: FREEZE { $$ = TRUE; }
|
|
| /*EMPTY*/ { $$ = FALSE; }
|
|
;
|
|
|
|
opt_name_list:
|
|
'(' name_list ')' { $$ = $2; }
|
|
| /*EMPTY*/ { $$ = NIL; }
|
|
;
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* QUERY:
|
|
* EXPLAIN [ANALYZE] [VERBOSE] query
|
|
*
|
|
*****************************************************************************/
|
|
|
|
ExplainStmt: EXPLAIN opt_analyze opt_verbose ExplainableStmt
|
|
{
|
|
ExplainStmt *n = makeNode(ExplainStmt);
|
|
n->analyze = $2;
|
|
n->verbose = $3;
|
|
n->query = (Query*)$4;
|
|
$$ = (Node *)n;
|
|
}
|
|
;
|
|
|
|
ExplainableStmt:
|
|
SelectStmt
|
|
| InsertStmt
|
|
| UpdateStmt
|
|
| DeleteStmt
|
|
| DeclareCursorStmt
|
|
| ExecuteStmt /* by default all are $$=$1 */
|
|
;
|
|
|
|
opt_analyze:
|
|
analyze_keyword { $$ = TRUE; }
|
|
| /* EMPTY */ { $$ = FALSE; }
|
|
;
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* QUERY:
|
|
* PREPARE <plan_name> [(args, ...)] AS <query>
|
|
*
|
|
*****************************************************************************/
|
|
|
|
PrepareStmt: PREPARE name prep_type_clause AS PreparableStmt
|
|
{
|
|
PrepareStmt *n = makeNode(PrepareStmt);
|
|
n->name = $2;
|
|
n->argtypes = $3;
|
|
n->query = (Query *) $5;
|
|
$$ = (Node *) n;
|
|
}
|
|
;
|
|
|
|
prep_type_clause: '(' prep_type_list ')' { $$ = $2; }
|
|
| /* EMPTY */ { $$ = NIL; }
|
|
;
|
|
|
|
prep_type_list: Typename { $$ = list_make1($1); }
|
|
| prep_type_list ',' Typename
|
|
{ $$ = lappend($1, $3); }
|
|
;
|
|
|
|
PreparableStmt:
|
|
SelectStmt
|
|
| InsertStmt
|
|
| UpdateStmt
|
|
| DeleteStmt /* by default all are $$=$1 */
|
|
;
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* EXECUTE <plan_name> [(params, ...)]
|
|
* CREATE TABLE <name> AS EXECUTE <plan_name> [(params, ...)]
|
|
*
|
|
*****************************************************************************/
|
|
|
|
ExecuteStmt: EXECUTE name execute_param_clause
|
|
{
|
|
ExecuteStmt *n = makeNode(ExecuteStmt);
|
|
n->name = $2;
|
|
n->params = $3;
|
|
n->into = NULL;
|
|
$$ = (Node *) n;
|
|
}
|
|
| CREATE OptTemp TABLE qualified_name OptCreateAs AS EXECUTE name execute_param_clause
|
|
{
|
|
ExecuteStmt *n = makeNode(ExecuteStmt);
|
|
n->name = $8;
|
|
n->params = $9;
|
|
$4->istemp = $2;
|
|
n->into = $4;
|
|
if ($5)
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
|
errmsg("column name list not allowed in CREATE TABLE / AS EXECUTE")));
|
|
/* ... because it's not implemented, but it could be */
|
|
$$ = (Node *) n;
|
|
}
|
|
;
|
|
|
|
execute_param_clause: '(' expr_list ')' { $$ = $2; }
|
|
| /* EMPTY */ { $$ = NIL; }
|
|
;
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* QUERY:
|
|
* DEALLOCATE [PREPARE] <plan_name>
|
|
*
|
|
*****************************************************************************/
|
|
|
|
DeallocateStmt: DEALLOCATE name
|
|
{
|
|
DeallocateStmt *n = makeNode(DeallocateStmt);
|
|
n->name = $2;
|
|
$$ = (Node *) n;
|
|
}
|
|
| DEALLOCATE PREPARE name
|
|
{
|
|
DeallocateStmt *n = makeNode(DeallocateStmt);
|
|
n->name = $3;
|
|
$$ = (Node *) n;
|
|
}
|
|
;
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* QUERY:
|
|
* INSERT STATEMENTS
|
|
*
|
|
*****************************************************************************/
|
|
|
|
InsertStmt:
|
|
INSERT INTO qualified_name insert_rest
|
|
{
|
|
$4->relation = $3;
|
|
$$ = (Node *) $4;
|
|
}
|
|
;
|
|
|
|
insert_rest:
|
|
VALUES '(' insert_target_list ')'
|
|
{
|
|
$$ = makeNode(InsertStmt);
|
|
$$->cols = NIL;
|
|
$$->targetList = $3;
|
|
$$->selectStmt = NULL;
|
|
}
|
|
| DEFAULT VALUES
|
|
{
|
|
$$ = makeNode(InsertStmt);
|
|
$$->cols = NIL;
|
|
$$->targetList = NIL;
|
|
$$->selectStmt = NULL;
|
|
}
|
|
| SelectStmt
|
|
{
|
|
$$ = makeNode(InsertStmt);
|
|
$$->cols = NIL;
|
|
$$->targetList = NIL;
|
|
$$->selectStmt = $1;
|
|
}
|
|
| '(' insert_column_list ')' VALUES '(' insert_target_list ')'
|
|
{
|
|
$$ = makeNode(InsertStmt);
|
|
$$->cols = $2;
|
|
$$->targetList = $6;
|
|
$$->selectStmt = NULL;
|
|
}
|
|
| '(' insert_column_list ')' SelectStmt
|
|
{
|
|
$$ = makeNode(InsertStmt);
|
|
$$->cols = $2;
|
|
$$->targetList = NIL;
|
|
$$->selectStmt = $4;
|
|
}
|
|
;
|
|
|
|
insert_column_list:
|
|
insert_column_item
|
|
{ $$ = list_make1($1); }
|
|
| insert_column_list ',' insert_column_item
|
|
{ $$ = lappend($1, $3); }
|
|
;
|
|
|
|
insert_column_item:
|
|
ColId opt_indirection
|
|
{
|
|
$$ = makeNode(ResTarget);
|
|
$$->name = $1;
|
|
$$->indirection = $2;
|
|
$$->val = NULL;
|
|
}
|
|
;
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* QUERY:
|
|
* DELETE STATEMENTS
|
|
*
|
|
*****************************************************************************/
|
|
|
|
DeleteStmt: DELETE_P FROM relation_expr_opt_alias
|
|
using_clause where_clause
|
|
{
|
|
DeleteStmt *n = makeNode(DeleteStmt);
|
|
n->relation = $3;
|
|
n->usingClause = $4;
|
|
n->whereClause = $5;
|
|
$$ = (Node *)n;
|
|
}
|
|
;
|
|
|
|
using_clause:
|
|
USING from_list { $$ = $2; }
|
|
| /*EMPTY*/ { $$ = NIL; }
|
|
;
|
|
|
|
LockStmt: LOCK_P opt_table qualified_name_list opt_lock opt_nowait
|
|
{
|
|
LockStmt *n = makeNode(LockStmt);
|
|
|
|
n->relations = $3;
|
|
n->mode = $4;
|
|
n->nowait = $5;
|
|
$$ = (Node *)n;
|
|
}
|
|
;
|
|
|
|
opt_lock: IN_P lock_type MODE { $$ = $2; }
|
|
| /*EMPTY*/ { $$ = AccessExclusiveLock; }
|
|
;
|
|
|
|
lock_type: ACCESS SHARE { $$ = AccessShareLock; }
|
|
| ROW SHARE { $$ = RowShareLock; }
|
|
| ROW EXCLUSIVE { $$ = RowExclusiveLock; }
|
|
| SHARE UPDATE EXCLUSIVE { $$ = ShareUpdateExclusiveLock; }
|
|
| SHARE { $$ = ShareLock; }
|
|
| SHARE ROW EXCLUSIVE { $$ = ShareRowExclusiveLock; }
|
|
| EXCLUSIVE { $$ = ExclusiveLock; }
|
|
| ACCESS EXCLUSIVE { $$ = AccessExclusiveLock; }
|
|
;
|
|
|
|
opt_nowait: NOWAIT { $$ = TRUE; }
|
|
| /*EMPTY*/ { $$ = FALSE; }
|
|
;
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* QUERY:
|
|
* UpdateStmt (UPDATE)
|
|
*
|
|
*****************************************************************************/
|
|
|
|
UpdateStmt: UPDATE relation_expr_opt_alias
|
|
SET update_target_list
|
|
from_clause
|
|
where_clause
|
|
{
|
|
UpdateStmt *n = makeNode(UpdateStmt);
|
|
n->relation = $2;
|
|
n->targetList = $4;
|
|
n->fromClause = $5;
|
|
n->whereClause = $6;
|
|
$$ = (Node *)n;
|
|
}
|
|
;
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* QUERY:
|
|
* CURSOR STATEMENTS
|
|
*
|
|
*****************************************************************************/
|
|
DeclareCursorStmt: DECLARE name cursor_options CURSOR opt_hold FOR SelectStmt
|
|
{
|
|
DeclareCursorStmt *n = makeNode(DeclareCursorStmt);
|
|
n->portalname = $2;
|
|
n->options = $3;
|
|
n->query = $7;
|
|
if ($5)
|
|
n->options |= CURSOR_OPT_HOLD;
|
|
$$ = (Node *)n;
|
|
}
|
|
;
|
|
|
|
cursor_options: /*EMPTY*/ { $$ = 0; }
|
|
| cursor_options NO SCROLL { $$ = $1 | CURSOR_OPT_NO_SCROLL; }
|
|
| cursor_options SCROLL { $$ = $1 | CURSOR_OPT_SCROLL; }
|
|
| cursor_options BINARY { $$ = $1 | CURSOR_OPT_BINARY; }
|
|
| cursor_options INSENSITIVE { $$ = $1 | CURSOR_OPT_INSENSITIVE; }
|
|
;
|
|
|
|
opt_hold: /* EMPTY */ { $$ = FALSE; }
|
|
| WITH HOLD { $$ = TRUE; }
|
|
| WITHOUT HOLD { $$ = FALSE; }
|
|
;
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* QUERY:
|
|
* SELECT STATEMENTS
|
|
*
|
|
*****************************************************************************/
|
|
|
|
/* A complete SELECT statement looks like this.
|
|
*
|
|
* The rule returns either a single SelectStmt 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 yacc 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 '(' SelectStmt ')',
|
|
* 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 SelectStmt 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; }
|
|
;
|
|
|
|
/*
|
|
* FOR UPDATE/SHARE 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 FOR UPDATE/SHARE
|
|
* 2002-08-28 bjm
|
|
*/
|
|
select_no_parens:
|
|
simple_select { $$ = $1; }
|
|
| select_clause sort_clause
|
|
{
|
|
insertSelectOptions((SelectStmt *) $1, $2, NULL,
|
|
NULL, NULL);
|
|
$$ = $1;
|
|
}
|
|
| select_clause opt_sort_clause for_locking_clause opt_select_limit
|
|
{
|
|
insertSelectOptions((SelectStmt *) $1, $2, $3,
|
|
list_nth($4, 0), list_nth($4, 1));
|
|
$$ = $1;
|
|
}
|
|
| select_clause opt_sort_clause select_limit opt_for_locking_clause
|
|
{
|
|
insertSelectOptions((SelectStmt *) $1, $2, $4,
|
|
list_nth($3, 0), list_nth($3, 1));
|
|
$$ = $1;
|
|
}
|
|
;
|
|
|
|
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 --- SQL92 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 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 sort and
|
|
* limit clauses inside parentheses.
|
|
*
|
|
* NOTE: only the leftmost component SelectStmt should have INTO.
|
|
* However, this is not checked by the grammar; parse analysis must check it.
|
|
*/
|
|
simple_select:
|
|
SELECT opt_distinct target_list
|
|
into_clause from_clause where_clause
|
|
group_clause having_clause
|
|
{
|
|
SelectStmt *n = makeNode(SelectStmt);
|
|
n->distinctClause = $2;
|
|
n->targetList = $3;
|
|
n->into = $4;
|
|
n->intoColNames = NIL;
|
|
n->intoHasOids = DEFAULT_OIDS;
|
|
n->fromClause = $5;
|
|
n->whereClause = $6;
|
|
n->groupClause = $7;
|
|
n->havingClause = $8;
|
|
$$ = (Node *)n;
|
|
}
|
|
| select_clause UNION opt_all select_clause
|
|
{
|
|
$$ = makeSetOp(SETOP_UNION, $3, $1, $4);
|
|
}
|
|
| select_clause INTERSECT opt_all select_clause
|
|
{
|
|
$$ = makeSetOp(SETOP_INTERSECT, $3, $1, $4);
|
|
}
|
|
| select_clause EXCEPT opt_all select_clause
|
|
{
|
|
$$ = makeSetOp(SETOP_EXCEPT, $3, $1, $4);
|
|
}
|
|
;
|
|
|
|
into_clause:
|
|
INTO OptTempTableName { $$ = $2; }
|
|
| /*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;
|
|
$$->istemp = true;
|
|
}
|
|
| TEMP opt_table qualified_name
|
|
{
|
|
$$ = $3;
|
|
$$->istemp = true;
|
|
}
|
|
| LOCAL TEMPORARY opt_table qualified_name
|
|
{
|
|
$$ = $4;
|
|
$$->istemp = true;
|
|
}
|
|
| LOCAL TEMP opt_table qualified_name
|
|
{
|
|
$$ = $4;
|
|
$$->istemp = true;
|
|
}
|
|
| GLOBAL TEMPORARY opt_table qualified_name
|
|
{
|
|
$$ = $4;
|
|
$$->istemp = true;
|
|
}
|
|
| GLOBAL TEMP opt_table qualified_name
|
|
{
|
|
$$ = $4;
|
|
$$->istemp = true;
|
|
}
|
|
| TABLE qualified_name
|
|
{
|
|
$$ = $2;
|
|
$$->istemp = false;
|
|
}
|
|
| qualified_name
|
|
{
|
|
$$ = $1;
|
|
$$->istemp = false;
|
|
}
|
|
;
|
|
|
|
opt_table: TABLE {}
|
|
| /*EMPTY*/ {}
|
|
;
|
|
|
|
opt_all: ALL { $$ = TRUE; }
|
|
| DISTINCT { $$ = FALSE; }
|
|
| /*EMPTY*/ { $$ = FALSE; }
|
|
;
|
|
|
|
/* We use (NIL) as a placeholder to indicate that all target expressions
|
|
* should be placed in the DISTINCT list during parsetree analysis.
|
|
*/
|
|
opt_distinct:
|
|
DISTINCT { $$ = list_make1(NIL); }
|
|
| DISTINCT ON '(' expr_list ')' { $$ = $4; }
|
|
| ALL { $$ = NIL; }
|
|
| /*EMPTY*/ { $$ = NIL; }
|
|
;
|
|
|
|
opt_sort_clause:
|
|
sort_clause { $$ = $1;}
|
|
| /*EMPTY*/ { $$ = NIL; }
|
|
;
|
|
|
|
sort_clause:
|
|
ORDER BY sortby_list { $$ = $3; }
|
|
;
|
|
|
|
sortby_list:
|
|
sortby { $$ = list_make1($1); }
|
|
| sortby_list ',' sortby { $$ = lappend($1, $3); }
|
|
;
|
|
|
|
sortby: a_expr USING qual_all_Op
|
|
{
|
|
$$ = makeNode(SortBy);
|
|
$$->node = $1;
|
|
$$->sortby_kind = SORTBY_USING;
|
|
$$->useOp = $3;
|
|
}
|
|
| a_expr ASC
|
|
{
|
|
$$ = makeNode(SortBy);
|
|
$$->node = $1;
|
|
$$->sortby_kind = SORTBY_ASC;
|
|
$$->useOp = NIL;
|
|
}
|
|
| a_expr DESC
|
|
{
|
|
$$ = makeNode(SortBy);
|
|
$$->node = $1;
|
|
$$->sortby_kind = SORTBY_DESC;
|
|
$$->useOp = NIL;
|
|
}
|
|
| a_expr
|
|
{
|
|
$$ = makeNode(SortBy);
|
|
$$->node = $1;
|
|
$$->sortby_kind = SORTBY_ASC; /* default */
|
|
$$->useOp = NIL;
|
|
}
|
|
;
|
|
|
|
|
|
select_limit:
|
|
LIMIT select_limit_value OFFSET select_offset_value
|
|
{ $$ = list_make2($4, $2); }
|
|
| OFFSET select_offset_value LIMIT select_limit_value
|
|
{ $$ = list_make2($2, $4); }
|
|
| LIMIT select_limit_value
|
|
{ $$ = list_make2(NULL, $2); }
|
|
| OFFSET select_offset_value
|
|
{ $$ = list_make2($2, NULL); }
|
|
| LIMIT select_limit_value ',' select_offset_value
|
|
{
|
|
/* Disabled because it was too confusing, bjm 2002-02-18 */
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_SYNTAX_ERROR),
|
|
errmsg("LIMIT #,# syntax is not supported"),
|
|
errhint("Use separate LIMIT and OFFSET clauses.")));
|
|
}
|
|
;
|
|
|
|
opt_select_limit:
|
|
select_limit { $$ = $1; }
|
|
| /* EMPTY */
|
|
{ $$ = list_make2(NULL,NULL); }
|
|
;
|
|
|
|
select_limit_value:
|
|
a_expr { $$ = $1; }
|
|
| ALL
|
|
{
|
|
/* LIMIT ALL is represented as a NULL constant */
|
|
A_Const *n = makeNode(A_Const);
|
|
n->val.type = T_Null;
|
|
$$ = (Node *)n;
|
|
}
|
|
;
|
|
|
|
select_offset_value:
|
|
a_expr { $$ = $1; }
|
|
;
|
|
|
|
group_clause:
|
|
GROUP_P BY expr_list { $$ = $3; }
|
|
| /*EMPTY*/ { $$ = NIL; }
|
|
;
|
|
|
|
having_clause:
|
|
HAVING a_expr { $$ = $2; }
|
|
| /*EMPTY*/ { $$ = NULL; }
|
|
;
|
|
|
|
for_locking_clause:
|
|
FOR UPDATE locked_rels_list opt_nowait
|
|
{
|
|
LockingClause *n = makeNode(LockingClause);
|
|
n->lockedRels = $3;
|
|
n->forUpdate = TRUE;
|
|
n->nowait = $4;
|
|
$$ = (Node *) n;
|
|
}
|
|
| FOR SHARE locked_rels_list opt_nowait
|
|
{
|
|
LockingClause *n = makeNode(LockingClause);
|
|
n->lockedRels = $3;
|
|
n->forUpdate = FALSE;
|
|
n->nowait = $4;
|
|
$$ = (Node *) n;
|
|
}
|
|
| FOR READ ONLY { $$ = NULL; }
|
|
;
|
|
|
|
opt_for_locking_clause:
|
|
for_locking_clause { $$ = $1; }
|
|
| /* EMPTY */ { $$ = NULL; }
|
|
;
|
|
|
|
locked_rels_list:
|
|
OF name_list { $$ = $2; }
|
|
| /* EMPTY */ { $$ = NIL; }
|
|
;
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* 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 { $$ = $2; }
|
|
| /*EMPTY*/ { $$ = NIL; }
|
|
;
|
|
|
|
from_list:
|
|
table_ref { $$ = list_make1($1); }
|
|
| from_list ',' table_ref { $$ = lappend($1, $3); }
|
|
;
|
|
|
|
/*
|
|
* table_ref is where an alias clause can be attached. Note we cannot make
|
|
* alias_clause have an empty production because that causes parse conflicts
|
|
* between table_ref := '(' joined_table ')' alias_clause
|
|
* and joined_table := '(' joined_table ')'. So, we must have the
|
|
* redundant-looking productions here instead.
|
|
*/
|
|
table_ref: relation_expr
|
|
{
|
|
$$ = (Node *) $1;
|
|
}
|
|
| relation_expr alias_clause
|
|
{
|
|
$1->alias = $2;
|
|
$$ = (Node *) $1;
|
|
}
|
|
| func_table
|
|
{
|
|
RangeFunction *n = makeNode(RangeFunction);
|
|
n->funccallnode = $1;
|
|
n->coldeflist = NIL;
|
|
$$ = (Node *) n;
|
|
}
|
|
| func_table alias_clause
|
|
{
|
|
RangeFunction *n = makeNode(RangeFunction);
|
|
n->funccallnode = $1;
|
|
n->alias = $2;
|
|
n->coldeflist = NIL;
|
|
$$ = (Node *) n;
|
|
}
|
|
| func_table AS '(' TableFuncElementList ')'
|
|
{
|
|
RangeFunction *n = makeNode(RangeFunction);
|
|
n->funccallnode = $1;
|
|
n->coldeflist = $4;
|
|
$$ = (Node *) n;
|
|
}
|
|
| func_table AS ColId '(' TableFuncElementList ')'
|
|
{
|
|
RangeFunction *n = makeNode(RangeFunction);
|
|
Alias *a = makeNode(Alias);
|
|
n->funccallnode = $1;
|
|
a->aliasname = $3;
|
|
n->alias = a;
|
|
n->coldeflist = $5;
|
|
$$ = (Node *) n;
|
|
}
|
|
| func_table ColId '(' TableFuncElementList ')'
|
|
{
|
|
RangeFunction *n = makeNode(RangeFunction);
|
|
Alias *a = makeNode(Alias);
|
|
n->funccallnode = $1;
|
|
a->aliasname = $2;
|
|
n->alias = a;
|
|
n->coldeflist = $4;
|
|
$$ = (Node *) n;
|
|
}
|
|
| select_with_parens
|
|
{
|
|
/*
|
|
* The SQL spec does not permit a subselect
|
|
* (<derived_table>) without an alias clause,
|
|
* so we don't either. This avoids the problem
|
|
* of needing to invent a unique refname for it.
|
|
* That could be surmounted if there's sufficient
|
|
* popular demand, but for now let's just implement
|
|
* the spec and see if anyone complains.
|
|
* However, it does seem like a good idea to emit
|
|
* an error message that's better than "syntax error".
|
|
*/
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_SYNTAX_ERROR),
|
|
errmsg("subquery in FROM must have an alias"),
|
|
errhint("For example, FROM (SELECT ...) [AS] foo.")));
|
|
$$ = NULL;
|
|
}
|
|
| select_with_parens alias_clause
|
|
{
|
|
RangeSubselect *n = makeNode(RangeSubselect);
|
|
n->subquery = $1;
|
|
n->alias = $2;
|
|
$$ = (Node *) n;
|
|
}
|
|
| joined_table
|
|
{
|
|
$$ = (Node *) $1;
|
|
}
|
|
| '(' joined_table ')' alias_clause
|
|
{
|
|
$2->alias = $4;
|
|
$$ = (Node *) $2;
|
|
}
|
|
;
|
|
|
|
|
|
/*
|
|
* It may seem silly to separate joined_table from table_ref, but there is
|
|
* method in SQL92'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.
|
|
*/
|
|
|
|
joined_table:
|
|
'(' joined_table ')'
|
|
{
|
|
$$ = $2;
|
|
}
|
|
| table_ref CROSS JOIN table_ref
|
|
{
|
|
/* CROSS JOIN is same as unqualified inner join */
|
|
JoinExpr *n = makeNode(JoinExpr);
|
|
n->jointype = JOIN_INNER;
|
|
n->isNatural = FALSE;
|
|
n->larg = $1;
|
|
n->rarg = $4;
|
|
n->using = NIL;
|
|
n->quals = NULL;
|
|
$$ = n;
|
|
}
|
|
| table_ref UNIONJOIN table_ref
|
|
{
|
|
/* UNION JOIN is made into 1 token to avoid shift/reduce
|
|
* conflict against regular UNION keyword.
|
|
*/
|
|
JoinExpr *n = makeNode(JoinExpr);
|
|
n->jointype = JOIN_UNION;
|
|
n->isNatural = FALSE;
|
|
n->larg = $1;
|
|
n->rarg = $3;
|
|
n->using = NIL;
|
|
n->quals = NULL;
|
|
$$ = n;
|
|
}
|
|
| table_ref join_type JOIN table_ref join_qual
|
|
{
|
|
JoinExpr *n = makeNode(JoinExpr);
|
|
n->jointype = $2;
|
|
n->isNatural = FALSE;
|
|
n->larg = $1;
|
|
n->rarg = $4;
|
|
if ($5 != NULL && IsA($5, List))
|
|
n->using = (List *) $5; /* USING clause */
|
|
else
|
|
n->quals = $5; /* ON clause */
|
|
$$ = n;
|
|
}
|
|
| table_ref JOIN table_ref join_qual
|
|
{
|
|
/* letting join_type reduce to empty doesn't work */
|
|
JoinExpr *n = makeNode(JoinExpr);
|
|
n->jointype = JOIN_INNER;
|
|
n->isNatural = FALSE;
|
|
n->larg = $1;
|
|
n->rarg = $3;
|
|
if ($4 != NULL && IsA($4, List))
|
|
n->using = (List *) $4; /* USING clause */
|
|
else
|
|
n->quals = $4; /* ON clause */
|
|
$$ = n;
|
|
}
|
|
| table_ref NATURAL join_type JOIN table_ref
|
|
{
|
|
JoinExpr *n = makeNode(JoinExpr);
|
|
n->jointype = $3;
|
|
n->isNatural = TRUE;
|
|
n->larg = $1;
|
|
n->rarg = $5;
|
|
n->using = NIL; /* figure out which columns later... */
|
|
n->quals = NULL; /* fill later */
|
|
$$ = n;
|
|
}
|
|
| table_ref NATURAL JOIN table_ref
|
|
{
|
|
/* letting join_type reduce to empty doesn't work */
|
|
JoinExpr *n = makeNode(JoinExpr);
|
|
n->jointype = JOIN_INNER;
|
|
n->isNatural = TRUE;
|
|
n->larg = $1;
|
|
n->rarg = $4;
|
|
n->using = NIL; /* figure out which columns later... */
|
|
n->quals = NULL; /* fill later */
|
|
$$ = n;
|
|
}
|
|
;
|
|
|
|
alias_clause:
|
|
AS ColId '(' name_list ')'
|
|
{
|
|
$$ = makeNode(Alias);
|
|
$$->aliasname = $2;
|
|
$$->colnames = $4;
|
|
}
|
|
| AS ColId
|
|
{
|
|
$$ = makeNode(Alias);
|
|
$$->aliasname = $2;
|
|
}
|
|
| ColId '(' name_list ')'
|
|
{
|
|
$$ = makeNode(Alias);
|
|
$$->aliasname = $1;
|
|
$$->colnames = $3;
|
|
}
|
|
| ColId
|
|
{
|
|
$$ = makeNode(Alias);
|
|
$$->aliasname = $1;
|
|
}
|
|
;
|
|
|
|
join_type: FULL join_outer { $$ = JOIN_FULL; }
|
|
| LEFT join_outer { $$ = JOIN_LEFT; }
|
|
| RIGHT join_outer { $$ = JOIN_RIGHT; }
|
|
| INNER_P { $$ = 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 List node, while an ON-expr will not be a List.
|
|
*/
|
|
|
|
join_qual: USING '(' name_list ')' { $$ = (Node *) $3; }
|
|
| ON a_expr { $$ = $2; }
|
|
;
|
|
|
|
|
|
relation_expr:
|
|
qualified_name
|
|
{
|
|
/* default inheritance */
|
|
$$ = $1;
|
|
$$->inhOpt = INH_DEFAULT;
|
|
$$->alias = NULL;
|
|
}
|
|
| qualified_name '*'
|
|
{
|
|
/* inheritance query */
|
|
$$ = $1;
|
|
$$->inhOpt = INH_YES;
|
|
$$->alias = NULL;
|
|
}
|
|
| ONLY qualified_name
|
|
{
|
|
/* no inheritance */
|
|
$$ = $2;
|
|
$$->inhOpt = INH_NO;
|
|
$$->alias = NULL;
|
|
}
|
|
| ONLY '(' qualified_name ')'
|
|
{
|
|
/* no inheritance, SQL99-style syntax */
|
|
$$ = $3;
|
|
$$->inhOpt = INH_NO;
|
|
$$->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
|
|
* relation_expr_opt_alias 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.
|
|
*/
|
|
relation_expr_opt_alias: relation_expr %prec UMINUS
|
|
{
|
|
$$ = $1;
|
|
}
|
|
| relation_expr ColId
|
|
{
|
|
Alias *alias = makeNode(Alias);
|
|
alias->aliasname = $2;
|
|
$1->alias = alias;
|
|
$$ = $1;
|
|
}
|
|
| relation_expr AS ColId
|
|
{
|
|
Alias *alias = makeNode(Alias);
|
|
alias->aliasname = $3;
|
|
$1->alias = alias;
|
|
$$ = $1;
|
|
}
|
|
;
|
|
|
|
|
|
func_table: func_expr { $$ = $1; }
|
|
;
|
|
|
|
|
|
where_clause:
|
|
WHERE a_expr { $$ = $2; }
|
|
| /*EMPTY*/ { $$ = NULL; }
|
|
;
|
|
|
|
|
|
TableFuncElementList:
|
|
TableFuncElement
|
|
{
|
|
$$ = list_make1($1);
|
|
}
|
|
| TableFuncElementList ',' TableFuncElement
|
|
{
|
|
$$ = lappend($1, $3);
|
|
}
|
|
;
|
|
|
|
TableFuncElement: ColId Typename
|
|
{
|
|
ColumnDef *n = makeNode(ColumnDef);
|
|
n->colname = $1;
|
|
n->typename = $2;
|
|
n->constraints = NIL;
|
|
n->is_local = true;
|
|
$$ = (Node *)n;
|
|
}
|
|
;
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* Type syntax
|
|
* SQL92 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
|
|
*
|
|
*****************************************************************************/
|
|
|
|
Typename: SimpleTypename opt_array_bounds
|
|
{
|
|
$$ = $1;
|
|
$$->arrayBounds = $2;
|
|
}
|
|
| SETOF SimpleTypename opt_array_bounds
|
|
{
|
|
$$ = $2;
|
|
$$->arrayBounds = $3;
|
|
$$->setof = TRUE;
|
|
}
|
|
| SimpleTypename ARRAY '[' Iconst ']'
|
|
{
|
|
/* SQL99's redundant syntax */
|
|
$$ = $1;
|
|
$$->arrayBounds = list_make1(makeInteger($4));
|
|
}
|
|
| SETOF SimpleTypename ARRAY '[' Iconst ']'
|
|
{
|
|
/* SQL99's redundant syntax */
|
|
$$ = $2;
|
|
$$->arrayBounds = list_make1(makeInteger($5));
|
|
$$->setof = TRUE;
|
|
}
|
|
;
|
|
|
|
opt_array_bounds:
|
|
opt_array_bounds '[' ']'
|
|
{ $$ = lappend($1, makeInteger(-1)); }
|
|
| opt_array_bounds '[' Iconst ']'
|
|
{ $$ = lappend($1, makeInteger($3)); }
|
|
| /*EMPTY*/
|
|
{ $$ = NIL; }
|
|
;
|
|
|
|
/*
|
|
* XXX ideally, the production for a qualified typename should be ColId attrs
|
|
* (there's no obvious reason why the first name should need to be restricted)
|
|
* and should be an alternative of GenericType (so that it can be used to
|
|
* specify a type for a literal in AExprConst). However doing either causes
|
|
* reduce/reduce conflicts that I haven't been able to find a workaround
|
|
* for. FIXME later.
|
|
*/
|
|
SimpleTypename:
|
|
GenericType { $$ = $1; }
|
|
| Numeric { $$ = $1; }
|
|
| Bit { $$ = $1; }
|
|
| Character { $$ = $1; }
|
|
| ConstDatetime { $$ = $1; }
|
|
| ConstInterval opt_interval
|
|
{
|
|
$$ = $1;
|
|
if ($2 != INTERVAL_FULL_RANGE)
|
|
$$->typmod = INTERVAL_TYPMOD(INTERVAL_FULL_PRECISION, $2);
|
|
}
|
|
| ConstInterval '(' Iconst ')' opt_interval
|
|
{
|
|
$$ = $1;
|
|
if ($3 < 0)
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
|
errmsg("INTERVAL(%d) precision must not be negative",
|
|
$3)));
|
|
if ($3 > MAX_INTERVAL_PRECISION)
|
|
{
|
|
ereport(WARNING,
|
|
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
|
errmsg("INTERVAL(%d) precision reduced to maximum allowed, %d",
|
|
$3, MAX_INTERVAL_PRECISION)));
|
|
$3 = MAX_INTERVAL_PRECISION;
|
|
}
|
|
$$->typmod = INTERVAL_TYPMOD($3, $5);
|
|
}
|
|
| type_name attrs
|
|
{
|
|
$$ = makeNode(TypeName);
|
|
$$->names = lcons(makeString($1), $2);
|
|
$$->typmod = -1;
|
|
}
|
|
;
|
|
|
|
/* 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 accomodate the postfix
|
|
* options (e.g. INTERVAL '1' YEAR).
|
|
*/
|
|
ConstTypename:
|
|
GenericType { $$ = $1; }
|
|
| Numeric { $$ = $1; }
|
|
| ConstBit { $$ = $1; }
|
|
| ConstCharacter { $$ = $1; }
|
|
| ConstDatetime { $$ = $1; }
|
|
;
|
|
|
|
GenericType:
|
|
type_name
|
|
{
|
|
$$ = makeTypeName($1);
|
|
}
|
|
;
|
|
|
|
/* SQL92 numeric data types
|
|
* Check FLOAT() precision limits assuming IEEE floating types.
|
|
* - thomas 1997-09-18
|
|
* Provide real DECIMAL() and NUMERIC() implementations now - Jan 1998-12-30
|
|
*/
|
|
Numeric: INT_P
|
|
{
|
|
$$ = SystemTypeName("int4");
|
|
}
|
|
| INTEGER
|
|
{
|
|
$$ = SystemTypeName("int4");
|
|
}
|
|
| SMALLINT
|
|
{
|
|
$$ = SystemTypeName("int2");
|
|
}
|
|
| BIGINT
|
|
{
|
|
$$ = SystemTypeName("int8");
|
|
}
|
|
| REAL
|
|
{
|
|
$$ = SystemTypeName("float4");
|
|
}
|
|
| FLOAT_P opt_float
|
|
{
|
|
$$ = $2;
|
|
}
|
|
| DOUBLE_P PRECISION
|
|
{
|
|
$$ = SystemTypeName("float8");
|
|
}
|
|
| DECIMAL_P opt_decimal
|
|
{
|
|
$$ = SystemTypeName("numeric");
|
|
$$->typmod = $2;
|
|
}
|
|
| DEC opt_decimal
|
|
{
|
|
$$ = SystemTypeName("numeric");
|
|
$$->typmod = $2;
|
|
}
|
|
| NUMERIC opt_numeric
|
|
{
|
|
$$ = SystemTypeName("numeric");
|
|
$$->typmod = $2;
|
|
}
|
|
| BOOLEAN_P
|
|
{
|
|
$$ = SystemTypeName("bool");
|
|
}
|
|
;
|
|
|
|
opt_float: '(' Iconst ')'
|
|
{
|
|
if ($2 < 1)
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
|
errmsg("precision for type float must be at least 1 bit")));
|
|
else if ($2 <= 24)
|
|
$$ = SystemTypeName("float4");
|
|
else if ($2 <= 53)
|
|
$$ = SystemTypeName("float8");
|
|
else
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
|
errmsg("precision for type float must be less than 54 bits")));
|
|
}
|
|
| /*EMPTY*/
|
|
{
|
|
$$ = SystemTypeName("float8");
|
|
}
|
|
;
|
|
|
|
opt_numeric:
|
|
'(' Iconst ',' Iconst ')'
|
|
{
|
|
if ($2 < 1 || $2 > NUMERIC_MAX_PRECISION)
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
|
errmsg("NUMERIC precision %d must be between 1 and %d",
|
|
$2, NUMERIC_MAX_PRECISION)));
|
|
if ($4 < 0 || $4 > $2)
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
|
errmsg("NUMERIC scale %d must be between 0 and precision %d",
|
|
$4, $2)));
|
|
|
|
$$ = (($2 << 16) | $4) + VARHDRSZ;
|
|
}
|
|
| '(' Iconst ')'
|
|
{
|
|
if ($2 < 1 || $2 > NUMERIC_MAX_PRECISION)
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
|
errmsg("NUMERIC precision %d must be between 1 and %d",
|
|
$2, NUMERIC_MAX_PRECISION)));
|
|
|
|
$$ = ($2 << 16) + VARHDRSZ;
|
|
}
|
|
| /*EMPTY*/
|
|
{
|
|
/* Insert "-1" meaning "no limit" */
|
|
$$ = -1;
|
|
}
|
|
;
|
|
|
|
opt_decimal:
|
|
'(' Iconst ',' Iconst ')'
|
|
{
|
|
if ($2 < 1 || $2 > NUMERIC_MAX_PRECISION)
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
|
errmsg("DECIMAL precision %d must be between 1 and %d",
|
|
$2, NUMERIC_MAX_PRECISION)));
|
|
if ($4 < 0 || $4 > $2)
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
|
errmsg("DECIMAL scale %d must be between 0 and precision %d",
|
|
$4, $2)));
|
|
|
|
$$ = (($2 << 16) | $4) + VARHDRSZ;
|
|
}
|
|
| '(' Iconst ')'
|
|
{
|
|
if ($2 < 1 || $2 > NUMERIC_MAX_PRECISION)
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
|
errmsg("DECIMAL precision %d must be between 1 and %d",
|
|
$2, NUMERIC_MAX_PRECISION)));
|
|
|
|
$$ = ($2 << 16) + VARHDRSZ;
|
|
}
|
|
| /*EMPTY*/
|
|
{
|
|
/* Insert "-1" meaning "no limit" */
|
|
$$ = -1;
|
|
}
|
|
;
|
|
|
|
|
|
/*
|
|
* SQL92 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;
|
|
$$->typmod = -1;
|
|
}
|
|
;
|
|
|
|
BitWithLength:
|
|
BIT opt_varying '(' Iconst ')'
|
|
{
|
|
char *typname;
|
|
|
|
typname = $2 ? "varbit" : "bit";
|
|
$$ = SystemTypeName(typname);
|
|
if ($4 < 1)
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
|
errmsg("length for type %s must be at least 1",
|
|
typname)));
|
|
else if ($4 > (MaxAttrSize * BITS_PER_BYTE))
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
|
errmsg("length for type %s cannot exceed %d",
|
|
typname, MaxAttrSize * BITS_PER_BYTE)));
|
|
$$->typmod = $4;
|
|
}
|
|
;
|
|
|
|
BitWithoutLength:
|
|
BIT opt_varying
|
|
{
|
|
/* bit defaults to bit(1), varbit to no limit */
|
|
if ($2)
|
|
{
|
|
$$ = SystemTypeName("varbit");
|
|
$$->typmod = -1;
|
|
}
|
|
else
|
|
{
|
|
$$ = SystemTypeName("bit");
|
|
$$->typmod = 1;
|
|
}
|
|
}
|
|
;
|
|
|
|
|
|
/*
|
|
* SQL92 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;
|
|
$$->typmod = -1;
|
|
}
|
|
;
|
|
|
|
CharacterWithLength: character '(' Iconst ')' opt_charset
|
|
{
|
|
if (($5 != NULL) && (strcmp($5, "sql_text") != 0))
|
|
{
|
|
char *type;
|
|
|
|
type = palloc(strlen($1) + 1 + strlen($5) + 1);
|
|
strcpy(type, $1);
|
|
strcat(type, "_");
|
|
strcat(type, $5);
|
|
$1 = type;
|
|
}
|
|
|
|
$$ = SystemTypeName($1);
|
|
|
|
if ($3 < 1)
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
|
errmsg("length for type %s must be at least 1",
|
|
$1)));
|
|
else if ($3 > MaxAttrSize)
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
|
errmsg("length for type %s cannot exceed %d",
|
|
$1, MaxAttrSize)));
|
|
|
|
/* we actually implement these like a varlen, so
|
|
* the first 4 bytes is the length. (the difference
|
|
* between these and "text" is that we blank-pad and
|
|
* truncate where necessary)
|
|
*/
|
|
$$->typmod = VARHDRSZ + $3;
|
|
}
|
|
;
|
|
|
|
CharacterWithoutLength: character opt_charset
|
|
{
|
|
if (($2 != NULL) && (strcmp($2, "sql_text") != 0))
|
|
{
|
|
char *type;
|
|
|
|
type = palloc(strlen($1) + 1 + strlen($2) + 1);
|
|
strcpy(type, $1);
|
|
strcat(type, "_");
|
|
strcat(type, $2);
|
|
$1 = type;
|
|
}
|
|
|
|
$$ = SystemTypeName($1);
|
|
|
|
/* char defaults to char(1), varchar to no limit */
|
|
if (strcmp($1, "bpchar") == 0)
|
|
$$->typmod = VARHDRSZ + 1;
|
|
else
|
|
$$->typmod = -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; }
|
|
;
|
|
|
|
opt_charset:
|
|
CHARACTER SET ColId { $$ = $3; }
|
|
| /*EMPTY*/ { $$ = NULL; }
|
|
;
|
|
|
|
ConstDatetime:
|
|
TIMESTAMP '(' Iconst ')' opt_timezone
|
|
{
|
|
if ($5)
|
|
$$ = SystemTypeName("timestamptz");
|
|
else
|
|
$$ = SystemTypeName("timestamp");
|
|
/* XXX the timezone field seems to be unused
|
|
* - thomas 2001-09-06
|
|
*/
|
|
$$->timezone = $5;
|
|
if ($3 < 0)
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
|
errmsg("TIMESTAMP(%d)%s precision must not be negative",
|
|
$3, ($5 ? " WITH TIME ZONE": ""))));
|
|
if ($3 > MAX_TIMESTAMP_PRECISION)
|
|
{
|
|
ereport(WARNING,
|
|
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
|
errmsg("TIMESTAMP(%d)%s precision reduced to maximum allowed, %d",
|
|
$3, ($5 ? " WITH TIME ZONE": ""),
|
|
MAX_TIMESTAMP_PRECISION)));
|
|
$3 = MAX_TIMESTAMP_PRECISION;
|
|
}
|
|
$$->typmod = $3;
|
|
}
|
|
| TIMESTAMP opt_timezone
|
|
{
|
|
if ($2)
|
|
$$ = SystemTypeName("timestamptz");
|
|
else
|
|
$$ = SystemTypeName("timestamp");
|
|
/* XXX the timezone field seems to be unused
|
|
* - thomas 2001-09-06
|
|
*/
|
|
$$->timezone = $2;
|
|
}
|
|
| TIME '(' Iconst ')' opt_timezone
|
|
{
|
|
if ($5)
|
|
$$ = SystemTypeName("timetz");
|
|
else
|
|
$$ = SystemTypeName("time");
|
|
if ($3 < 0)
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
|
errmsg("TIME(%d)%s precision must not be negative",
|
|
$3, ($5 ? " WITH TIME ZONE": ""))));
|
|
if ($3 > MAX_TIME_PRECISION)
|
|
{
|
|
ereport(WARNING,
|
|
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
|
errmsg("TIME(%d)%s precision reduced to maximum allowed, %d",
|
|
$3, ($5 ? " WITH TIME ZONE": ""),
|
|
MAX_TIME_PRECISION)));
|
|
$3 = MAX_TIME_PRECISION;
|
|
}
|
|
$$->typmod = $3;
|
|
}
|
|
| TIME opt_timezone
|
|
{
|
|
if ($2)
|
|
$$ = SystemTypeName("timetz");
|
|
else
|
|
$$ = SystemTypeName("time");
|
|
}
|
|
;
|
|
|
|
ConstInterval:
|
|
INTERVAL { $$ = SystemTypeName("interval"); }
|
|
;
|
|
|
|
opt_timezone:
|
|
WITH TIME ZONE { $$ = TRUE; }
|
|
| WITHOUT TIME ZONE { $$ = FALSE; }
|
|
| /*EMPTY*/ { $$ = FALSE; }
|
|
;
|
|
|
|
opt_interval:
|
|
YEAR_P { $$ = INTERVAL_MASK(YEAR); }
|
|
| MONTH_P { $$ = INTERVAL_MASK(MONTH); }
|
|
| DAY_P { $$ = INTERVAL_MASK(DAY); }
|
|
| HOUR_P { $$ = INTERVAL_MASK(HOUR); }
|
|
| MINUTE_P { $$ = INTERVAL_MASK(MINUTE); }
|
|
| SECOND_P { $$ = INTERVAL_MASK(SECOND); }
|
|
| YEAR_P TO MONTH_P
|
|
{ $$ = INTERVAL_MASK(YEAR) | INTERVAL_MASK(MONTH); }
|
|
| DAY_P TO HOUR_P
|
|
{ $$ = INTERVAL_MASK(DAY) | INTERVAL_MASK(HOUR); }
|
|
| DAY_P TO MINUTE_P
|
|
{ $$ = INTERVAL_MASK(DAY) | INTERVAL_MASK(HOUR)
|
|
| INTERVAL_MASK(MINUTE); }
|
|
| DAY_P TO SECOND_P
|
|
{ $$ = INTERVAL_MASK(DAY) | INTERVAL_MASK(HOUR)
|
|
| INTERVAL_MASK(MINUTE) | INTERVAL_MASK(SECOND); }
|
|
| HOUR_P TO MINUTE_P
|
|
{ $$ = INTERVAL_MASK(HOUR) | INTERVAL_MASK(MINUTE); }
|
|
| HOUR_P TO SECOND_P
|
|
{ $$ = INTERVAL_MASK(HOUR) | INTERVAL_MASK(MINUTE)
|
|
| INTERVAL_MASK(SECOND); }
|
|
| MINUTE_P TO SECOND_P
|
|
{ $$ = INTERVAL_MASK(MINUTE) | INTERVAL_MASK(SECOND); }
|
|
| /*EMPTY*/ { $$ = INTERVAL_FULL_RANGE; }
|
|
;
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* 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.
|
|
*/
|
|
a_expr: c_expr { $$ = $1; }
|
|
| a_expr TYPECAST Typename
|
|
{ $$ = makeTypeCast($1, $3); }
|
|
| a_expr AT TIME ZONE a_expr
|
|
{
|
|
FuncCall *n = makeNode(FuncCall);
|
|
n->funcname = SystemFuncName("timezone");
|
|
n->args = list_make2($5, $1);
|
|
n->agg_star = FALSE;
|
|
n->agg_distinct = FALSE;
|
|
$$ = (Node *) n;
|
|
}
|
|
/*
|
|
* These operators must be called out explicitly in order to make use
|
|
* of yacc/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 above.
|
|
*/
|
|
| '+' a_expr %prec UMINUS
|
|
{ $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "+", NULL, $2); }
|
|
| '-' a_expr %prec UMINUS
|
|
{ $$ = doNegate($2); }
|
|
| a_expr '+' a_expr
|
|
{ $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "+", $1, $3); }
|
|
| a_expr '-' a_expr
|
|
{ $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "-", $1, $3); }
|
|
| a_expr '*' a_expr
|
|
{ $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "*", $1, $3); }
|
|
| a_expr '/' a_expr
|
|
{ $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "/", $1, $3); }
|
|
| a_expr '%' a_expr
|
|
{ $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "%", $1, $3); }
|
|
| a_expr '^' a_expr
|
|
{ $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "^", $1, $3); }
|
|
| a_expr '<' a_expr
|
|
{ $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "<", $1, $3); }
|
|
| a_expr '>' a_expr
|
|
{ $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, ">", $1, $3); }
|
|
| a_expr '=' a_expr
|
|
{ $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "=", $1, $3); }
|
|
|
|
| a_expr qual_Op a_expr %prec Op
|
|
{ $$ = (Node *) makeA_Expr(AEXPR_OP, $2, $1, $3); }
|
|
| qual_Op a_expr %prec Op
|
|
{ $$ = (Node *) makeA_Expr(AEXPR_OP, $1, NULL, $2); }
|
|
| a_expr qual_Op %prec POSTFIXOP
|
|
{ $$ = (Node *) makeA_Expr(AEXPR_OP, $2, $1, NULL); }
|
|
|
|
| a_expr AND a_expr
|
|
{ $$ = (Node *) makeA_Expr(AEXPR_AND, NIL, $1, $3); }
|
|
| a_expr OR a_expr
|
|
{ $$ = (Node *) makeA_Expr(AEXPR_OR, NIL, $1, $3); }
|
|
| NOT a_expr
|
|
{ $$ = (Node *) makeA_Expr(AEXPR_NOT, NIL, NULL, $2); }
|
|
|
|
| a_expr LIKE a_expr
|
|
{ $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "~~", $1, $3); }
|
|
| a_expr LIKE a_expr ESCAPE a_expr
|
|
{
|
|
FuncCall *n = makeNode(FuncCall);
|
|
n->funcname = SystemFuncName("like_escape");
|
|
n->args = list_make2($3, $5);
|
|
n->agg_star = FALSE;
|
|
n->agg_distinct = FALSE;
|
|
$$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "~~", $1, (Node *) n);
|
|
}
|
|
| a_expr NOT LIKE a_expr
|
|
{ $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "!~~", $1, $4); }
|
|
| a_expr NOT LIKE a_expr ESCAPE a_expr
|
|
{
|
|
FuncCall *n = makeNode(FuncCall);
|
|
n->funcname = SystemFuncName("like_escape");
|
|
n->args = list_make2($4, $6);
|
|
n->agg_star = FALSE;
|
|
n->agg_distinct = FALSE;
|
|
$$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "!~~", $1, (Node *) n);
|
|
}
|
|
| a_expr ILIKE a_expr
|
|
{ $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "~~*", $1, $3); }
|
|
| a_expr ILIKE a_expr ESCAPE a_expr
|
|
{
|
|
FuncCall *n = makeNode(FuncCall);
|
|
n->funcname = SystemFuncName("like_escape");
|
|
n->args = list_make2($3, $5);
|
|
n->agg_star = FALSE;
|
|
n->agg_distinct = FALSE;
|
|
$$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "~~*", $1, (Node *) n);
|
|
}
|
|
| a_expr NOT ILIKE a_expr
|
|
{ $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "!~~*", $1, $4); }
|
|
| a_expr NOT ILIKE a_expr ESCAPE a_expr
|
|
{
|
|
FuncCall *n = makeNode(FuncCall);
|
|
n->funcname = SystemFuncName("like_escape");
|
|
n->args = list_make2($4, $6);
|
|
n->agg_star = FALSE;
|
|
n->agg_distinct = FALSE;
|
|
$$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "!~~*", $1, (Node *) n);
|
|
}
|
|
|
|
| a_expr SIMILAR TO a_expr %prec SIMILAR
|
|
{
|
|
A_Const *c = makeNode(A_Const);
|
|
FuncCall *n = makeNode(FuncCall);
|
|
c->val.type = T_Null;
|
|
n->funcname = SystemFuncName("similar_escape");
|
|
n->args = list_make2($4, (Node *) c);
|
|
n->agg_star = FALSE;
|
|
n->agg_distinct = FALSE;
|
|
$$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "~", $1, (Node *) n);
|
|
}
|
|
| a_expr SIMILAR TO a_expr ESCAPE a_expr
|
|
{
|
|
FuncCall *n = makeNode(FuncCall);
|
|
n->funcname = SystemFuncName("similar_escape");
|
|
n->args = list_make2($4, $6);
|
|
n->agg_star = FALSE;
|
|
n->agg_distinct = FALSE;
|
|
$$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "~", $1, (Node *) n);
|
|
}
|
|
| a_expr NOT SIMILAR TO a_expr %prec SIMILAR
|
|
{
|
|
A_Const *c = makeNode(A_Const);
|
|
FuncCall *n = makeNode(FuncCall);
|
|
c->val.type = T_Null;
|
|
n->funcname = SystemFuncName("similar_escape");
|
|
n->args = list_make2($5, (Node *) c);
|
|
n->agg_star = FALSE;
|
|
n->agg_distinct = FALSE;
|
|
$$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "!~", $1, (Node *) n);
|
|
}
|
|
| a_expr NOT SIMILAR TO a_expr ESCAPE a_expr
|
|
{
|
|
FuncCall *n = makeNode(FuncCall);
|
|
n->funcname = SystemFuncName("similar_escape");
|
|
n->args = list_make2($5, $7);
|
|
n->agg_star = FALSE;
|
|
n->agg_distinct = FALSE;
|
|
$$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "!~", $1, (Node *) n);
|
|
}
|
|
|
|
/* NullTest clause
|
|
* Define SQL92-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 ISNULL
|
|
{
|
|
if (IsA($1, RowExpr))
|
|
$$ = makeRowNullTest(IS_NULL, (RowExpr *) $1);
|
|
else
|
|
{
|
|
NullTest *n = makeNode(NullTest);
|
|
n->arg = (Expr *) $1;
|
|
n->nulltesttype = IS_NULL;
|
|
$$ = (Node *)n;
|
|
}
|
|
}
|
|
| a_expr IS NULL_P
|
|
{
|
|
if (IsA($1, RowExpr))
|
|
$$ = makeRowNullTest(IS_NULL, (RowExpr *) $1);
|
|
else
|
|
{
|
|
NullTest *n = makeNode(NullTest);
|
|
n->arg = (Expr *) $1;
|
|
n->nulltesttype = IS_NULL;
|
|
$$ = (Node *)n;
|
|
}
|
|
}
|
|
| a_expr NOTNULL
|
|
{
|
|
if (IsA($1, RowExpr))
|
|
$$ = makeRowNullTest(IS_NOT_NULL, (RowExpr *) $1);
|
|
else
|
|
{
|
|
NullTest *n = makeNode(NullTest);
|
|
n->arg = (Expr *) $1;
|
|
n->nulltesttype = IS_NOT_NULL;
|
|
$$ = (Node *)n;
|
|
}
|
|
}
|
|
| a_expr IS NOT NULL_P
|
|
{
|
|
if (IsA($1, RowExpr))
|
|
$$ = makeRowNullTest(IS_NOT_NULL, (RowExpr *) $1);
|
|
else
|
|
{
|
|
NullTest *n = makeNode(NullTest);
|
|
n->arg = (Expr *) $1;
|
|
n->nulltesttype = IS_NOT_NULL;
|
|
$$ = (Node *)n;
|
|
}
|
|
}
|
|
| row OVERLAPS row
|
|
{
|
|
$$ = (Node *)makeOverlaps($1, $3);
|
|
}
|
|
| a_expr IS TRUE_P
|
|
{
|
|
BooleanTest *b = makeNode(BooleanTest);
|
|
b->arg = (Expr *) $1;
|
|
b->booltesttype = IS_TRUE;
|
|
$$ = (Node *)b;
|
|
}
|
|
| a_expr IS NOT TRUE_P
|
|
{
|
|
BooleanTest *b = makeNode(BooleanTest);
|
|
b->arg = (Expr *) $1;
|
|
b->booltesttype = IS_NOT_TRUE;
|
|
$$ = (Node *)b;
|
|
}
|
|
| a_expr IS FALSE_P
|
|
{
|
|
BooleanTest *b = makeNode(BooleanTest);
|
|
b->arg = (Expr *) $1;
|
|
b->booltesttype = IS_FALSE;
|
|
$$ = (Node *)b;
|
|
}
|
|
| a_expr IS NOT FALSE_P
|
|
{
|
|
BooleanTest *b = makeNode(BooleanTest);
|
|
b->arg = (Expr *) $1;
|
|
b->booltesttype = IS_NOT_FALSE;
|
|
$$ = (Node *)b;
|
|
}
|
|
| a_expr IS UNKNOWN
|
|
{
|
|
BooleanTest *b = makeNode(BooleanTest);
|
|
b->arg = (Expr *) $1;
|
|
b->booltesttype = IS_UNKNOWN;
|
|
$$ = (Node *)b;
|
|
}
|
|
| a_expr IS NOT UNKNOWN
|
|
{
|
|
BooleanTest *b = makeNode(BooleanTest);
|
|
b->arg = (Expr *) $1;
|
|
b->booltesttype = IS_NOT_UNKNOWN;
|
|
$$ = (Node *)b;
|
|
}
|
|
| a_expr IS DISTINCT FROM a_expr %prec IS
|
|
{
|
|
$$ = (Node *) makeSimpleA_Expr(AEXPR_DISTINCT, "=", $1, $5);
|
|
}
|
|
| a_expr IS NOT DISTINCT FROM a_expr %prec IS
|
|
{
|
|
$$ = (Node *) makeA_Expr(AEXPR_NOT, NIL, NULL,
|
|
(Node *) makeSimpleA_Expr(AEXPR_DISTINCT,
|
|
"=", $1, $6));
|
|
|
|
}
|
|
| a_expr IS OF '(' type_list ')' %prec IS
|
|
{
|
|
$$ = (Node *) makeSimpleA_Expr(AEXPR_OF, "=", $1, (Node *) $5);
|
|
}
|
|
| a_expr IS NOT OF '(' type_list ')' %prec IS
|
|
{
|
|
$$ = (Node *) makeSimpleA_Expr(AEXPR_OF, "<>", $1, (Node *) $6);
|
|
}
|
|
| a_expr BETWEEN opt_asymmetric b_expr AND b_expr %prec BETWEEN
|
|
{
|
|
$$ = (Node *) makeA_Expr(AEXPR_AND, NIL,
|
|
(Node *) makeSimpleA_Expr(AEXPR_OP, ">=", $1, $4),
|
|
(Node *) makeSimpleA_Expr(AEXPR_OP, "<=", $1, $6));
|
|
}
|
|
| a_expr NOT BETWEEN opt_asymmetric b_expr AND b_expr %prec BETWEEN
|
|
{
|
|
$$ = (Node *) makeA_Expr(AEXPR_OR, NIL,
|
|
(Node *) makeSimpleA_Expr(AEXPR_OP, "<", $1, $5),
|
|
(Node *) makeSimpleA_Expr(AEXPR_OP, ">", $1, $7));
|
|
}
|
|
| a_expr BETWEEN SYMMETRIC b_expr AND b_expr %prec BETWEEN
|
|
{
|
|
$$ = (Node *) makeA_Expr(AEXPR_OR, NIL,
|
|
(Node *) makeA_Expr(AEXPR_AND, NIL,
|
|
(Node *) makeSimpleA_Expr(AEXPR_OP, ">=", $1, $4),
|
|
(Node *) makeSimpleA_Expr(AEXPR_OP, "<=", $1, $6)),
|
|
(Node *) makeA_Expr(AEXPR_AND, NIL,
|
|
(Node *) makeSimpleA_Expr(AEXPR_OP, ">=", $1, $6),
|
|
(Node *) makeSimpleA_Expr(AEXPR_OP, "<=", $1, $4)));
|
|
}
|
|
| a_expr NOT BETWEEN SYMMETRIC b_expr AND b_expr %prec BETWEEN
|
|
{
|
|
$$ = (Node *) makeA_Expr(AEXPR_AND, NIL,
|
|
(Node *) makeA_Expr(AEXPR_OR, NIL,
|
|
(Node *) makeSimpleA_Expr(AEXPR_OP, "<", $1, $5),
|
|
(Node *) makeSimpleA_Expr(AEXPR_OP, ">", $1, $7)),
|
|
(Node *) makeA_Expr(AEXPR_OR, NIL,
|
|
(Node *) makeSimpleA_Expr(AEXPR_OP, "<", $1, $7),
|
|
(Node *) makeSimpleA_Expr(AEXPR_OP, ">", $1, $5)));
|
|
}
|
|
| a_expr IN_P in_expr
|
|
{
|
|
/* in_expr returns a SubLink or a list of a_exprs */
|
|
if (IsA($3, SubLink))
|
|
{
|
|
/* generate foo = ANY (subquery) */
|
|
SubLink *n = (SubLink *) $3;
|
|
n->subLinkType = ANY_SUBLINK;
|
|
n->testexpr = $1;
|
|
n->operName = list_make1(makeString("="));
|
|
$$ = (Node *)n;
|
|
}
|
|
else
|
|
{
|
|
/* generate scalar IN expression */
|
|
$$ = (Node *) makeSimpleA_Expr(AEXPR_IN, "=", $1, $3);
|
|
}
|
|
}
|
|
| a_expr NOT IN_P in_expr
|
|
{
|
|
/* in_expr returns a SubLink or a list of a_exprs */
|
|
if (IsA($4, SubLink))
|
|
{
|
|
/* generate NOT (foo = ANY (subquery)) */
|
|
/* Make an = ANY node */
|
|
SubLink *n = (SubLink *) $4;
|
|
n->subLinkType = ANY_SUBLINK;
|
|
n->testexpr = $1;
|
|
n->operName = list_make1(makeString("="));
|
|
/* Stick a NOT on top */
|
|
$$ = (Node *) makeA_Expr(AEXPR_NOT, NIL, NULL, (Node *) n);
|
|
}
|
|
else
|
|
{
|
|
/* generate scalar NOT IN expression */
|
|
$$ = (Node *) makeSimpleA_Expr(AEXPR_IN, "<>", $1, $4);
|
|
}
|
|
}
|
|
| a_expr subquery_Op sub_type select_with_parens %prec Op
|
|
{
|
|
SubLink *n = makeNode(SubLink);
|
|
n->subLinkType = $3;
|
|
n->testexpr = $1;
|
|
n->operName = $2;
|
|
n->subselect = $4;
|
|
$$ = (Node *)n;
|
|
}
|
|
| a_expr subquery_Op sub_type '(' a_expr ')' %prec Op
|
|
{
|
|
if ($3 == ANY_SUBLINK)
|
|
$$ = (Node *) makeA_Expr(AEXPR_OP_ANY, $2, $1, $5);
|
|
else
|
|
$$ = (Node *) makeA_Expr(AEXPR_OP_ALL, $2, $1, $5);
|
|
}
|
|
| UNIQUE select_with_parens
|
|
{
|
|
/* Not sure how to get rid of the parentheses
|
|
* but there are lots of shift/reduce errors without them.
|
|
*
|
|
* Should be able to implement this by plopping the entire
|
|
* select into a node, then transforming the target expressions
|
|
* from whatever they are into count(*), and testing the
|
|
* entire result equal to one.
|
|
* But, will probably implement a separate node in the executor.
|
|
*/
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
|
errmsg("UNIQUE predicate is not yet implemented")));
|
|
}
|
|
;
|
|
|
|
/*
|
|
* 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); }
|
|
| '+' b_expr %prec UMINUS
|
|
{ $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "+", NULL, $2); }
|
|
| '-' b_expr %prec UMINUS
|
|
{ $$ = doNegate($2); }
|
|
| b_expr '+' b_expr
|
|
{ $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "+", $1, $3); }
|
|
| b_expr '-' b_expr
|
|
{ $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "-", $1, $3); }
|
|
| b_expr '*' b_expr
|
|
{ $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "*", $1, $3); }
|
|
| b_expr '/' b_expr
|
|
{ $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "/", $1, $3); }
|
|
| b_expr '%' b_expr
|
|
{ $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "%", $1, $3); }
|
|
| b_expr '^' b_expr
|
|
{ $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "^", $1, $3); }
|
|
| b_expr '<' b_expr
|
|
{ $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "<", $1, $3); }
|
|
| b_expr '>' b_expr
|
|
{ $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, ">", $1, $3); }
|
|
| b_expr '=' b_expr
|
|
{ $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "=", $1, $3); }
|
|
| b_expr qual_Op b_expr %prec Op
|
|
{ $$ = (Node *) makeA_Expr(AEXPR_OP, $2, $1, $3); }
|
|
| qual_Op b_expr %prec Op
|
|
{ $$ = (Node *) makeA_Expr(AEXPR_OP, $1, NULL, $2); }
|
|
| b_expr qual_Op %prec POSTFIXOP
|
|
{ $$ = (Node *) makeA_Expr(AEXPR_OP, $2, $1, NULL); }
|
|
| b_expr IS DISTINCT FROM b_expr %prec IS
|
|
{
|
|
$$ = (Node *) makeSimpleA_Expr(AEXPR_DISTINCT, "=", $1, $5);
|
|
}
|
|
| b_expr IS NOT DISTINCT FROM b_expr %prec IS
|
|
{
|
|
$$ = (Node *) makeA_Expr(AEXPR_NOT, NIL,
|
|
NULL, (Node *) makeSimpleA_Expr(AEXPR_DISTINCT, "=", $1, $6));
|
|
}
|
|
| b_expr IS OF '(' type_list ')' %prec IS
|
|
{
|
|
$$ = (Node *) makeSimpleA_Expr(AEXPR_OF, "=", $1, (Node *) $5);
|
|
}
|
|
| b_expr IS NOT OF '(' type_list ')' %prec IS
|
|
{
|
|
$$ = (Node *) makeSimpleA_Expr(AEXPR_OF, "<>", $1, (Node *) $6);
|
|
}
|
|
;
|
|
|
|
/*
|
|
* 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: columnref { $$ = $1; }
|
|
| AexprConst { $$ = $1; }
|
|
| PARAM opt_indirection
|
|
{
|
|
ParamRef *p = makeNode(ParamRef);
|
|
p->number = $1;
|
|
if ($2)
|
|
{
|
|
A_Indirection *n = makeNode(A_Indirection);
|
|
n->arg = (Node *) p;
|
|
n->indirection = $2;
|
|
$$ = (Node *) n;
|
|
}
|
|
else
|
|
$$ = (Node *) p;
|
|
}
|
|
| '(' a_expr ')' opt_indirection
|
|
{
|
|
if ($4)
|
|
{
|
|
A_Indirection *n = makeNode(A_Indirection);
|
|
n->arg = $2;
|
|
n->indirection = $4;
|
|
$$ = (Node *)n;
|
|
}
|
|
else
|
|
$$ = $2;
|
|
}
|
|
| case_expr
|
|
{ $$ = $1; }
|
|
| func_expr
|
|
{ $$ = $1; }
|
|
| select_with_parens %prec UMINUS
|
|
{
|
|
SubLink *n = makeNode(SubLink);
|
|
n->subLinkType = EXPR_SUBLINK;
|
|
n->testexpr = NULL;
|
|
n->operName = NIL;
|
|
n->subselect = $1;
|
|
$$ = (Node *)n;
|
|
}
|
|
| EXISTS select_with_parens
|
|
{
|
|
SubLink *n = makeNode(SubLink);
|
|
n->subLinkType = EXISTS_SUBLINK;
|
|
n->testexpr = NULL;
|
|
n->operName = NIL;
|
|
n->subselect = $2;
|
|
$$ = (Node *)n;
|
|
}
|
|
| ARRAY select_with_parens
|
|
{
|
|
SubLink *n = makeNode(SubLink);
|
|
n->subLinkType = ARRAY_SUBLINK;
|
|
n->testexpr = NULL;
|
|
n->operName = NIL;
|
|
n->subselect = $2;
|
|
$$ = (Node *)n;
|
|
}
|
|
| ARRAY array_expr
|
|
{ $$ = $2; }
|
|
| row
|
|
{
|
|
RowExpr *r = makeNode(RowExpr);
|
|
r->args = $1;
|
|
r->row_typeid = InvalidOid; /* not analyzed yet */
|
|
$$ = (Node *)r;
|
|
}
|
|
;
|
|
|
|
/*
|
|
* func_expr is split out from c_expr just so that we have a classification
|
|
* 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 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_name '(' ')'
|
|
{
|
|
FuncCall *n = makeNode(FuncCall);
|
|
n->funcname = $1;
|
|
n->args = NIL;
|
|
n->agg_star = FALSE;
|
|
n->agg_distinct = FALSE;
|
|
$$ = (Node *)n;
|
|
}
|
|
| func_name '(' expr_list ')'
|
|
{
|
|
FuncCall *n = makeNode(FuncCall);
|
|
n->funcname = $1;
|
|
n->args = $3;
|
|
n->agg_star = FALSE;
|
|
n->agg_distinct = FALSE;
|
|
$$ = (Node *)n;
|
|
}
|
|
| func_name '(' ALL expr_list ')'
|
|
{
|
|
FuncCall *n = makeNode(FuncCall);
|
|
n->funcname = $1;
|
|
n->args = $4;
|
|
n->agg_star = FALSE;
|
|
n->agg_distinct = FALSE;
|
|
/* Ideally we'd mark the FuncCall node to indicate
|
|
* "must be an aggregate", but there's no provision
|
|
* for that in FuncCall at the moment.
|
|
*/
|
|
$$ = (Node *)n;
|
|
}
|
|
| func_name '(' DISTINCT expr_list ')'
|
|
{
|
|
FuncCall *n = makeNode(FuncCall);
|
|
n->funcname = $1;
|
|
n->args = $4;
|
|
n->agg_star = FALSE;
|
|
n->agg_distinct = TRUE;
|
|
$$ = (Node *)n;
|
|
}
|
|
| func_name '(' '*' ')'
|
|
{
|
|
/*
|
|
* For now, we transform AGGREGATE(*) into AGGREGATE(1).
|
|
*
|
|
* This does the right thing for COUNT(*) (in fact,
|
|
* any certainly-non-null expression would do for COUNT),
|
|
* and there are no other aggregates in SQL92 that accept
|
|
* '*' as parameter.
|
|
*
|
|
* The FuncCall node is also marked agg_star = true,
|
|
* so that later processing can detect what the argument
|
|
* really was.
|
|
*/
|
|
FuncCall *n = makeNode(FuncCall);
|
|
A_Const *star = makeNode(A_Const);
|
|
|
|
star->val.type = T_Integer;
|
|
star->val.val.ival = 1;
|
|
n->funcname = $1;
|
|
n->args = list_make1(star);
|
|
n->agg_star = TRUE;
|
|
n->agg_distinct = FALSE;
|
|
$$ = (Node *)n;
|
|
}
|
|
| CURRENT_DATE
|
|
{
|
|
/*
|
|
* Translate as "'now'::text::date".
|
|
*
|
|
* We cannot use "'now'::date" because coerce_type() will
|
|
* immediately reduce that to a constant representing
|
|
* today's date. We need to delay the conversion until
|
|
* runtime, else the wrong things will happen when
|
|
* CURRENT_DATE is used in a column default value or rule.
|
|
*
|
|
* This could be simplified if we had a way to generate
|
|
* an expression tree representing runtime application
|
|
* of type-input conversion functions. (As of PG 7.3
|
|
* that is actually possible, but not clear that we want
|
|
* to rely on it.)
|
|
*/
|
|
A_Const *s = makeNode(A_Const);
|
|
TypeName *d;
|
|
|
|
s->val.type = T_String;
|
|
s->val.val.str = "now";
|
|
s->typename = SystemTypeName("text");
|
|
|
|
d = SystemTypeName("date");
|
|
|
|
$$ = (Node *)makeTypeCast((Node *)s, d);
|
|
}
|
|
| CURRENT_TIME
|
|
{
|
|
/*
|
|
* Translate as "'now'::text::timetz".
|
|
* See comments for CURRENT_DATE.
|
|
*/
|
|
A_Const *s = makeNode(A_Const);
|
|
TypeName *d;
|
|
|
|
s->val.type = T_String;
|
|
s->val.val.str = "now";
|
|
s->typename = SystemTypeName("text");
|
|
|
|
d = SystemTypeName("timetz");
|
|
|
|
$$ = (Node *)makeTypeCast((Node *)s, d);
|
|
}
|
|
| CURRENT_TIME '(' Iconst ')'
|
|
{
|
|
/*
|
|
* Translate as "'now'::text::timetz(n)".
|
|
* See comments for CURRENT_DATE.
|
|
*/
|
|
A_Const *s = makeNode(A_Const);
|
|
TypeName *d;
|
|
|
|
s->val.type = T_String;
|
|
s->val.val.str = "now";
|
|
s->typename = SystemTypeName("text");
|
|
d = SystemTypeName("timetz");
|
|
if ($3 < 0)
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
|
errmsg("CURRENT_TIME(%d) precision must not be negative",
|
|
$3)));
|
|
if ($3 > MAX_TIME_PRECISION)
|
|
{
|
|
ereport(WARNING,
|
|
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
|
errmsg("CURRENT_TIME(%d) precision reduced to maximum allowed, %d",
|
|
$3, MAX_TIME_PRECISION)));
|
|
$3 = MAX_TIME_PRECISION;
|
|
}
|
|
d->typmod = $3;
|
|
|
|
$$ = (Node *)makeTypeCast((Node *)s, d);
|
|
}
|
|
| CURRENT_TIMESTAMP
|
|
{
|
|
/*
|
|
* Translate as "now()", since we have a function that
|
|
* does exactly what is needed.
|
|
*/
|
|
FuncCall *n = makeNode(FuncCall);
|
|
n->funcname = SystemFuncName("now");
|
|
n->args = NIL;
|
|
n->agg_star = FALSE;
|
|
n->agg_distinct = FALSE;
|
|
$$ = (Node *)n;
|
|
}
|
|
| CURRENT_TIMESTAMP '(' Iconst ')'
|
|
{
|
|
/*
|
|
* Translate as "'now'::text::timestamptz(n)".
|
|
* See comments for CURRENT_DATE.
|
|
*/
|
|
A_Const *s = makeNode(A_Const);
|
|
TypeName *d;
|
|
|
|
s->val.type = T_String;
|
|
s->val.val.str = "now";
|
|
s->typename = SystemTypeName("text");
|
|
|
|
d = SystemTypeName("timestamptz");
|
|
if ($3 < 0)
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
|
errmsg("CURRENT_TIMESTAMP(%d) precision must not be negative",
|
|
$3)));
|
|
if ($3 > MAX_TIMESTAMP_PRECISION)
|
|
{
|
|
ereport(WARNING,
|
|
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
|
errmsg("CURRENT_TIMESTAMP(%d) precision reduced to maximum allowed, %d",
|
|
$3, MAX_TIMESTAMP_PRECISION)));
|
|
$3 = MAX_TIMESTAMP_PRECISION;
|
|
}
|
|
d->typmod = $3;
|
|
|
|
$$ = (Node *)makeTypeCast((Node *)s, d);
|
|
}
|
|
| LOCALTIME
|
|
{
|
|
/*
|
|
* Translate as "'now'::text::time".
|
|
* See comments for CURRENT_DATE.
|
|
*/
|
|
A_Const *s = makeNode(A_Const);
|
|
TypeName *d;
|
|
|
|
s->val.type = T_String;
|
|
s->val.val.str = "now";
|
|
s->typename = SystemTypeName("text");
|
|
|
|
d = SystemTypeName("time");
|
|
|
|
$$ = (Node *)makeTypeCast((Node *)s, d);
|
|
}
|
|
| LOCALTIME '(' Iconst ')'
|
|
{
|
|
/*
|
|
* Translate as "'now'::text::time(n)".
|
|
* See comments for CURRENT_DATE.
|
|
*/
|
|
A_Const *s = makeNode(A_Const);
|
|
TypeName *d;
|
|
|
|
s->val.type = T_String;
|
|
s->val.val.str = "now";
|
|
s->typename = SystemTypeName("text");
|
|
d = SystemTypeName("time");
|
|
if ($3 < 0)
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
|
errmsg("LOCALTIME(%d) precision must not be negative",
|
|
$3)));
|
|
if ($3 > MAX_TIME_PRECISION)
|
|
{
|
|
ereport(WARNING,
|
|
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
|
errmsg("LOCALTIME(%d) precision reduced to maximum allowed, %d",
|
|
$3, MAX_TIME_PRECISION)));
|
|
$3 = MAX_TIME_PRECISION;
|
|
}
|
|
d->typmod = $3;
|
|
|
|
$$ = (Node *)makeTypeCast((Node *)s, d);
|
|
}
|
|
| LOCALTIMESTAMP
|
|
{
|
|
/*
|
|
* Translate as "'now'::text::timestamp".
|
|
* See comments for CURRENT_DATE.
|
|
*/
|
|
A_Const *s = makeNode(A_Const);
|
|
TypeName *d;
|
|
|
|
s->val.type = T_String;
|
|
s->val.val.str = "now";
|
|
s->typename = SystemTypeName("text");
|
|
|
|
d = SystemTypeName("timestamp");
|
|
|
|
$$ = (Node *)makeTypeCast((Node *)s, d);
|
|
}
|
|
| LOCALTIMESTAMP '(' Iconst ')'
|
|
{
|
|
/*
|
|
* Translate as "'now'::text::timestamp(n)".
|
|
* See comments for CURRENT_DATE.
|
|
*/
|
|
A_Const *s = makeNode(A_Const);
|
|
TypeName *d;
|
|
|
|
s->val.type = T_String;
|
|
s->val.val.str = "now";
|
|
s->typename = SystemTypeName("text");
|
|
|
|
d = SystemTypeName("timestamp");
|
|
if ($3 < 0)
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
|
errmsg("LOCALTIMESTAMP(%d) precision must not be negative",
|
|
$3)));
|
|
if ($3 > MAX_TIMESTAMP_PRECISION)
|
|
{
|
|
ereport(WARNING,
|
|
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
|
errmsg("LOCALTIMESTAMP(%d) precision reduced to maximum allowed, %d",
|
|
$3, MAX_TIMESTAMP_PRECISION)));
|
|
$3 = MAX_TIMESTAMP_PRECISION;
|
|
}
|
|
d->typmod = $3;
|
|
|
|
$$ = (Node *)makeTypeCast((Node *)s, d);
|
|
}
|
|
| CURRENT_ROLE
|
|
{
|
|
FuncCall *n = makeNode(FuncCall);
|
|
n->funcname = SystemFuncName("current_user");
|
|
n->args = NIL;
|
|
n->agg_star = FALSE;
|
|
n->agg_distinct = FALSE;
|
|
$$ = (Node *)n;
|
|
}
|
|
| CURRENT_USER
|
|
{
|
|
FuncCall *n = makeNode(FuncCall);
|
|
n->funcname = SystemFuncName("current_user");
|
|
n->args = NIL;
|
|
n->agg_star = FALSE;
|
|
n->agg_distinct = FALSE;
|
|
$$ = (Node *)n;
|
|
}
|
|
| SESSION_USER
|
|
{
|
|
FuncCall *n = makeNode(FuncCall);
|
|
n->funcname = SystemFuncName("session_user");
|
|
n->args = NIL;
|
|
n->agg_star = FALSE;
|
|
n->agg_distinct = FALSE;
|
|
$$ = (Node *)n;
|
|
}
|
|
| USER
|
|
{
|
|
FuncCall *n = makeNode(FuncCall);
|
|
n->funcname = SystemFuncName("current_user");
|
|
n->args = NIL;
|
|
n->agg_star = FALSE;
|
|
n->agg_distinct = FALSE;
|
|
$$ = (Node *)n;
|
|
}
|
|
| CAST '(' a_expr AS Typename ')'
|
|
{ $$ = makeTypeCast($3, $5); }
|
|
| EXTRACT '(' extract_list ')'
|
|
{
|
|
FuncCall *n = makeNode(FuncCall);
|
|
n->funcname = SystemFuncName("date_part");
|
|
n->args = $3;
|
|
n->agg_star = FALSE;
|
|
n->agg_distinct = FALSE;
|
|
$$ = (Node *)n;
|
|
}
|
|
| OVERLAY '(' overlay_list ')'
|
|
{
|
|
/* overlay(A PLACING B FROM C FOR D) is converted to
|
|
* substring(A, 1, C-1) || B || substring(A, C+1, C+D)
|
|
* overlay(A PLACING B FROM C) is converted to
|
|
* substring(A, 1, C-1) || B || substring(A, C+1, C+char_length(B))
|
|
*/
|
|
FuncCall *n = makeNode(FuncCall);
|
|
n->funcname = SystemFuncName("overlay");
|
|
n->args = $3;
|
|
n->agg_star = FALSE;
|
|
n->agg_distinct = FALSE;
|
|
$$ = (Node *)n;
|
|
}
|
|
| POSITION '(' position_list ')'
|
|
{
|
|
/* position(A in B) is converted to position(B, A) */
|
|
FuncCall *n = makeNode(FuncCall);
|
|
n->funcname = SystemFuncName("position");
|
|
n->args = $3;
|
|
n->agg_star = FALSE;
|
|
n->agg_distinct = FALSE;
|
|
$$ = (Node *)n;
|
|
}
|
|
| SUBSTRING '(' substr_list ')'
|
|
{
|
|
/* substring(A from B for C) is converted to
|
|
* substring(A, B, C) - thomas 2000-11-28
|
|
*/
|
|
FuncCall *n = makeNode(FuncCall);
|
|
n->funcname = SystemFuncName("substring");
|
|
n->args = $3;
|
|
n->agg_star = FALSE;
|
|
n->agg_distinct = FALSE;
|
|
$$ = (Node *)n;
|
|
}
|
|
| 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
|
|
* coersions than are handled by implicit casting.
|
|
*/
|
|
FuncCall *n = makeNode(FuncCall);
|
|
/* Convert SystemTypeName() to SystemFuncName() even though
|
|
* at the moment they result in the same thing.
|
|
*/
|
|
n->funcname = SystemFuncName(((Value *)llast($5->names))->val.str);
|
|
n->args = list_make1($3);
|
|
$$ = (Node *)n;
|
|
}
|
|
| TRIM '(' BOTH trim_list ')'
|
|
{
|
|
/* various trim expressions are defined in SQL92
|
|
* - thomas 1997-07-19
|
|
*/
|
|
FuncCall *n = makeNode(FuncCall);
|
|
n->funcname = SystemFuncName("btrim");
|
|
n->args = $4;
|
|
n->agg_star = FALSE;
|
|
n->agg_distinct = FALSE;
|
|
$$ = (Node *)n;
|
|
}
|
|
| TRIM '(' LEADING trim_list ')'
|
|
{
|
|
FuncCall *n = makeNode(FuncCall);
|
|
n->funcname = SystemFuncName("ltrim");
|
|
n->args = $4;
|
|
n->agg_star = FALSE;
|
|
n->agg_distinct = FALSE;
|
|
$$ = (Node *)n;
|
|
}
|
|
| TRIM '(' TRAILING trim_list ')'
|
|
{
|
|
FuncCall *n = makeNode(FuncCall);
|
|
n->funcname = SystemFuncName("rtrim");
|
|
n->args = $4;
|
|
n->agg_star = FALSE;
|
|
n->agg_distinct = FALSE;
|
|
$$ = (Node *)n;
|
|
}
|
|
| TRIM '(' trim_list ')'
|
|
{
|
|
FuncCall *n = makeNode(FuncCall);
|
|
n->funcname = SystemFuncName("btrim");
|
|
n->args = $3;
|
|
n->agg_star = FALSE;
|
|
n->agg_distinct = FALSE;
|
|
$$ = (Node *)n;
|
|
}
|
|
| CONVERT '(' a_expr USING any_name ')'
|
|
{
|
|
FuncCall *n = makeNode(FuncCall);
|
|
A_Const *c = makeNode(A_Const);
|
|
|
|
c->val.type = T_String;
|
|
c->val.val.str = NameListToQuotedString($5);
|
|
|
|
n->funcname = SystemFuncName("convert_using");
|
|
n->args = list_make2($3, c);
|
|
n->agg_star = FALSE;
|
|
n->agg_distinct = FALSE;
|
|
$$ = (Node *)n;
|
|
}
|
|
| CONVERT '(' expr_list ')'
|
|
{
|
|
FuncCall *n = makeNode(FuncCall);
|
|
n->funcname = SystemFuncName("convert");
|
|
n->args = $3;
|
|
n->agg_star = FALSE;
|
|
n->agg_distinct = FALSE;
|
|
$$ = (Node *)n;
|
|
}
|
|
| NULLIF '(' a_expr ',' a_expr ')'
|
|
{
|
|
$$ = (Node *) makeSimpleA_Expr(AEXPR_NULLIF, "=", $3, $5);
|
|
}
|
|
| COALESCE '(' expr_list ')'
|
|
{
|
|
CoalesceExpr *c = makeNode(CoalesceExpr);
|
|
c->args = $3;
|
|
$$ = (Node *)c;
|
|
}
|
|
| GREATEST '(' expr_list ')'
|
|
{
|
|
MinMaxExpr *v = makeNode(MinMaxExpr);
|
|
v->args = $3;
|
|
v->op = IS_GREATEST;
|
|
$$ = (Node *)v;
|
|
}
|
|
| LEAST '(' expr_list ')'
|
|
{
|
|
MinMaxExpr *v = makeNode(MinMaxExpr);
|
|
v->args = $3;
|
|
v->op = IS_LEAST;
|
|
$$ = (Node *)v;
|
|
}
|
|
;
|
|
|
|
/*
|
|
* 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.
|
|
*/
|
|
row: ROW '(' expr_list ')' { $$ = $3; }
|
|
| ROW '(' ')' { $$ = NIL; }
|
|
| '(' expr_list ',' a_expr ')' { $$ = lappend($2, $4); }
|
|
;
|
|
|
|
sub_type: ANY { $$ = ANY_SUBLINK; }
|
|
| SOME { $$ = ANY_SUBLINK; }
|
|
| ALL { $$ = ALL_SUBLINK; }
|
|
;
|
|
|
|
all_Op: Op { $$ = $1; }
|
|
| MathOp { $$ = $1; }
|
|
;
|
|
|
|
MathOp: '+' { $$ = "+"; }
|
|
| '-' { $$ = "-"; }
|
|
| '*' { $$ = "*"; }
|
|
| '/' { $$ = "/"; }
|
|
| '%' { $$ = "%"; }
|
|
| '^' { $$ = "^"; }
|
|
| '<' { $$ = "<"; }
|
|
| '>' { $$ = ">"; }
|
|
| '=' { $$ = "="; }
|
|
;
|
|
|
|
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 LIKE
|
|
{ $$ = list_make1(makeString("!~~")); }
|
|
| ILIKE
|
|
{ $$ = list_make1(makeString("~~*")); }
|
|
| NOT 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 SubLink structure which handles any/some/all stuff
|
|
* is not ready for such a thing.
|
|
*/
|
|
;
|
|
|
|
expr_list: a_expr
|
|
{
|
|
$$ = list_make1($1);
|
|
}
|
|
| expr_list ',' a_expr
|
|
{
|
|
$$ = lappend($1, $3);
|
|
}
|
|
;
|
|
|
|
extract_list:
|
|
extract_arg FROM a_expr
|
|
{
|
|
A_Const *n = makeNode(A_Const);
|
|
n->val.type = T_String;
|
|
n->val.val.str = $1;
|
|
$$ = list_make2((Node *) n, $3);
|
|
}
|
|
| /*EMPTY*/ { $$ = NIL; }
|
|
;
|
|
|
|
type_list: type_list ',' Typename
|
|
{
|
|
$$ = lappend($1, $3);
|
|
}
|
|
| Typename
|
|
{
|
|
$$ = list_make1($1);
|
|
}
|
|
;
|
|
|
|
array_expr_list: array_expr
|
|
{ $$ = list_make1($1); }
|
|
| array_expr_list ',' array_expr
|
|
{ $$ = lappend($1, $3); }
|
|
;
|
|
|
|
array_expr: '[' expr_list ']'
|
|
{
|
|
ArrayExpr *n = makeNode(ArrayExpr);
|
|
n->elements = $2;
|
|
$$ = (Node *)n;
|
|
}
|
|
| '[' array_expr_list ']'
|
|
{
|
|
ArrayExpr *n = makeNode(ArrayExpr);
|
|
n->elements = $2;
|
|
$$ = (Node *)n;
|
|
}
|
|
;
|
|
|
|
/* Allow delimited string SCONST in extract_arg as an SQL extension.
|
|
* - thomas 2001-04-12
|
|
*/
|
|
|
|
extract_arg:
|
|
IDENT { $$ = $1; }
|
|
| YEAR_P { $$ = "year"; }
|
|
| MONTH_P { $$ = "month"; }
|
|
| DAY_P { $$ = "day"; }
|
|
| HOUR_P { $$ = "hour"; }
|
|
| MINUTE_P { $$ = "minute"; }
|
|
| SECOND_P { $$ = "second"; }
|
|
| 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)
|
|
*/
|
|
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($3, $1); }
|
|
| /*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. This is a kluge to avoid surprising results
|
|
* when the argument is, say, int8. It'd be better if
|
|
* there were not an implicit cast from int8 to text ...
|
|
*/
|
|
A_Const *n = makeNode(A_Const);
|
|
n->val.type = T_Integer;
|
|
n->val.val.ival = 1;
|
|
$$ = list_make3($1, (Node *) n,
|
|
makeTypeCast($2, SystemTypeName("int4")));
|
|
}
|
|
| expr_list
|
|
{
|
|
$$ = $1;
|
|
}
|
|
| /*EMPTY*/
|
|
{ $$ = NIL; }
|
|
;
|
|
|
|
substr_from:
|
|
FROM a_expr { $$ = $2; }
|
|
;
|
|
|
|
substr_for: FOR a_expr { $$ = $2; }
|
|
;
|
|
|
|
trim_list: a_expr FROM expr_list { $$ = lappend($3, $1); }
|
|
| FROM expr_list { $$ = $2; }
|
|
| expr_list { $$ = $1; }
|
|
;
|
|
|
|
in_expr: select_with_parens
|
|
{
|
|
SubLink *n = makeNode(SubLink);
|
|
n->subselect = $1;
|
|
/* other fields will be filled later */
|
|
$$ = (Node *)n;
|
|
}
|
|
| '(' expr_list ')' { $$ = (Node *)$2; }
|
|
;
|
|
|
|
/*
|
|
* Define SQL92-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
|
|
{
|
|
CaseExpr *c = makeNode(CaseExpr);
|
|
c->casetype = InvalidOid; /* not analyzed yet */
|
|
c->arg = (Expr *) $2;
|
|
c->args = $3;
|
|
c->defresult = (Expr *) $4;
|
|
$$ = (Node *)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
|
|
{
|
|
CaseWhen *w = makeNode(CaseWhen);
|
|
w->expr = (Expr *) $2;
|
|
w->result = (Expr *) $4;
|
|
$$ = (Node *)w;
|
|
}
|
|
;
|
|
|
|
case_default:
|
|
ELSE a_expr { $$ = $2; }
|
|
| /*EMPTY*/ { $$ = NULL; }
|
|
;
|
|
|
|
case_arg: a_expr { $$ = $1; }
|
|
| /*EMPTY*/ { $$ = NULL; }
|
|
;
|
|
|
|
/*
|
|
* columnref starts with relation_name not ColId, so that OLD and NEW
|
|
* references can be accepted. Note that when there are more than two
|
|
* dotted names, the first name is not actually a relation name...
|
|
*/
|
|
columnref: relation_name
|
|
{
|
|
$$ = makeColumnRef($1, NIL);
|
|
}
|
|
| relation_name indirection
|
|
{
|
|
$$ = makeColumnRef($1, $2);
|
|
}
|
|
;
|
|
|
|
indirection_el:
|
|
'.' attr_name
|
|
{
|
|
$$ = (Node *) makeString($2);
|
|
}
|
|
| '.' '*'
|
|
{
|
|
$$ = (Node *) makeString("*");
|
|
}
|
|
| '[' a_expr ']'
|
|
{
|
|
A_Indices *ai = makeNode(A_Indices);
|
|
ai->lidx = NULL;
|
|
ai->uidx = $2;
|
|
$$ = (Node *) ai;
|
|
}
|
|
| '[' a_expr ':' a_expr ']'
|
|
{
|
|
A_Indices *ai = makeNode(A_Indices);
|
|
ai->lidx = $2;
|
|
ai->uidx = $4;
|
|
$$ = (Node *) ai;
|
|
}
|
|
;
|
|
|
|
indirection:
|
|
indirection_el { $$ = list_make1($1); }
|
|
| indirection indirection_el { $$ = lappend($1, $2); }
|
|
;
|
|
|
|
opt_indirection:
|
|
/*EMPTY*/ { $$ = NIL; }
|
|
| opt_indirection indirection_el { $$ = lappend($1, $2); }
|
|
;
|
|
|
|
opt_asymmetric: ASYMMETRIC
|
|
| /*EMPTY*/
|
|
;
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* target lists for SELECT, UPDATE, INSERT
|
|
*
|
|
*****************************************************************************/
|
|
|
|
target_list:
|
|
target_el { $$ = list_make1($1); }
|
|
| target_list ',' target_el { $$ = lappend($1, $3); }
|
|
;
|
|
|
|
/* AS is not optional because shift/red conflict with unary ops */
|
|
target_el: a_expr AS ColLabel
|
|
{
|
|
$$ = makeNode(ResTarget);
|
|
$$->name = $3;
|
|
$$->indirection = NIL;
|
|
$$->val = (Node *)$1;
|
|
}
|
|
| a_expr
|
|
{
|
|
$$ = makeNode(ResTarget);
|
|
$$->name = NULL;
|
|
$$->indirection = NIL;
|
|
$$->val = (Node *)$1;
|
|
}
|
|
| '*'
|
|
{
|
|
ColumnRef *n = makeNode(ColumnRef);
|
|
n->fields = list_make1(makeString("*"));
|
|
|
|
$$ = makeNode(ResTarget);
|
|
$$->name = NULL;
|
|
$$->indirection = NIL;
|
|
$$->val = (Node *)n;
|
|
}
|
|
;
|
|
|
|
update_target_list:
|
|
update_target_el { $$ = list_make1($1); }
|
|
| update_target_list ',' update_target_el { $$ = lappend($1,$3); }
|
|
;
|
|
|
|
update_target_el:
|
|
ColId opt_indirection '=' a_expr
|
|
{
|
|
$$ = makeNode(ResTarget);
|
|
$$->name = $1;
|
|
$$->indirection = $2;
|
|
$$->val = (Node *) $4;
|
|
}
|
|
| ColId opt_indirection '=' DEFAULT
|
|
{
|
|
$$ = makeNode(ResTarget);
|
|
$$->name = $1;
|
|
$$->indirection = $2;
|
|
$$->val = (Node *) makeNode(SetToDefault);
|
|
}
|
|
|
|
;
|
|
|
|
insert_target_list:
|
|
insert_target_el { $$ = list_make1($1); }
|
|
| insert_target_list ',' insert_target_el { $$ = lappend($1, $3); }
|
|
;
|
|
|
|
insert_target_el:
|
|
a_expr
|
|
{
|
|
$$ = makeNode(ResTarget);
|
|
$$->name = NULL;
|
|
$$->indirection = NIL;
|
|
$$->val = (Node *)$1;
|
|
}
|
|
| DEFAULT
|
|
{
|
|
$$ = makeNode(ResTarget);
|
|
$$->name = NULL;
|
|
$$->indirection = NIL;
|
|
$$->val = (Node *) makeNode(SetToDefault);
|
|
}
|
|
;
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* Names and constants
|
|
*
|
|
*****************************************************************************/
|
|
|
|
relation_name:
|
|
SpecialRuleRelation { $$ = $1; }
|
|
| ColId { $$ = $1; }
|
|
;
|
|
|
|
qualified_name_list:
|
|
qualified_name { $$ = list_make1($1); }
|
|
| qualified_name_list ',' qualified_name { $$ = lappend($1, $3); }
|
|
;
|
|
|
|
/*
|
|
* The production for a qualified relation name has to exactly match the
|
|
* production for a qualified func_name, because in a FROM clause we cannot
|
|
* tell which we are parsing until we see what comes after it ('(' for a
|
|
* func_name, something else for a relation). Therefore we allow 'indirection'
|
|
* which may contain subscripts, and reject that case in the C code.
|
|
*/
|
|
qualified_name:
|
|
relation_name
|
|
{
|
|
$$ = makeNode(RangeVar);
|
|
$$->catalogname = NULL;
|
|
$$->schemaname = NULL;
|
|
$$->relname = $1;
|
|
}
|
|
| relation_name indirection
|
|
{
|
|
check_qualified_name($2);
|
|
$$ = makeNode(RangeVar);
|
|
switch (list_length($2))
|
|
{
|
|
case 1:
|
|
$$->catalogname = NULL;
|
|
$$->schemaname = $1;
|
|
$$->relname = strVal(linitial($2));
|
|
break;
|
|
case 2:
|
|
$$->catalogname = $1;
|
|
$$->schemaname = strVal(linitial($2));
|
|
$$->relname = strVal(lsecond($2));
|
|
break;
|
|
default:
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_SYNTAX_ERROR),
|
|
errmsg("improper qualified name (too many dotted names): %s",
|
|
NameListToString(lcons(makeString($1), $2)))));
|
|
break;
|
|
}
|
|
}
|
|
;
|
|
|
|
name_list: name
|
|
{ $$ = list_make1(makeString($1)); }
|
|
| name_list ',' name
|
|
{ $$ = lappend($1, makeString($3)); }
|
|
;
|
|
|
|
|
|
name: ColId { $$ = $1; };
|
|
|
|
database_name:
|
|
ColId { $$ = $1; };
|
|
|
|
access_method:
|
|
ColId { $$ = $1; };
|
|
|
|
attr_name: ColLabel { $$ = $1; };
|
|
|
|
index_name: ColId { $$ = $1; };
|
|
|
|
file_name: Sconst { $$ = $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 ('(' 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
|
|
{ $$ = list_make1(makeString($1)); }
|
|
| relation_name indirection
|
|
{ $$ = check_func_name(lcons(makeString($1), $2)); }
|
|
;
|
|
|
|
|
|
/*
|
|
* Constants
|
|
*/
|
|
AexprConst: Iconst
|
|
{
|
|
A_Const *n = makeNode(A_Const);
|
|
n->val.type = T_Integer;
|
|
n->val.val.ival = $1;
|
|
$$ = (Node *)n;
|
|
}
|
|
| FCONST
|
|
{
|
|
A_Const *n = makeNode(A_Const);
|
|
n->val.type = T_Float;
|
|
n->val.val.str = $1;
|
|
$$ = (Node *)n;
|
|
}
|
|
| Sconst
|
|
{
|
|
A_Const *n = makeNode(A_Const);
|
|
n->val.type = T_String;
|
|
n->val.val.str = $1;
|
|
$$ = (Node *)n;
|
|
}
|
|
| BCONST
|
|
{
|
|
A_Const *n = makeNode(A_Const);
|
|
n->val.type = T_BitString;
|
|
n->val.val.str = $1;
|
|
$$ = (Node *)n;
|
|
}
|
|
| 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>.
|
|
*/
|
|
A_Const *n = makeNode(A_Const);
|
|
n->val.type = T_BitString;
|
|
n->val.val.str = $1;
|
|
$$ = (Node *)n;
|
|
}
|
|
| ConstTypename Sconst
|
|
{
|
|
A_Const *n = makeNode(A_Const);
|
|
n->typename = $1;
|
|
n->val.type = T_String;
|
|
n->val.val.str = $2;
|
|
$$ = (Node *)n;
|
|
}
|
|
| ConstInterval Sconst opt_interval
|
|
{
|
|
A_Const *n = makeNode(A_Const);
|
|
n->typename = $1;
|
|
n->val.type = T_String;
|
|
n->val.val.str = $2;
|
|
/* precision is not specified, but fields may be... */
|
|
if ($3 != INTERVAL_FULL_RANGE)
|
|
n->typename->typmod = INTERVAL_TYPMOD(INTERVAL_FULL_PRECISION, $3);
|
|
$$ = (Node *)n;
|
|
}
|
|
| ConstInterval '(' Iconst ')' Sconst opt_interval
|
|
{
|
|
A_Const *n = makeNode(A_Const);
|
|
n->typename = $1;
|
|
n->val.type = T_String;
|
|
n->val.val.str = $5;
|
|
/* precision specified, and fields may be... */
|
|
if ($3 < 0)
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
|
errmsg("INTERVAL(%d) precision must not be negative",
|
|
$3)));
|
|
if ($3 > MAX_INTERVAL_PRECISION)
|
|
{
|
|
ereport(WARNING,
|
|
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
|
errmsg("INTERVAL(%d) precision reduced to maximum allowed, %d",
|
|
$3, MAX_INTERVAL_PRECISION)));
|
|
$3 = MAX_INTERVAL_PRECISION;
|
|
}
|
|
n->typename->typmod = INTERVAL_TYPMOD($3, $6);
|
|
$$ = (Node *)n;
|
|
}
|
|
| TRUE_P
|
|
{
|
|
$$ = (Node *)makeBoolAConst(TRUE);
|
|
}
|
|
| FALSE_P
|
|
{
|
|
$$ = (Node *)makeBoolAConst(FALSE);
|
|
}
|
|
| NULL_P
|
|
{
|
|
A_Const *n = makeNode(A_Const);
|
|
n->val.type = T_Null;
|
|
$$ = (Node *)n;
|
|
}
|
|
;
|
|
|
|
Iconst: ICONST { $$ = $1; };
|
|
Sconst: SCONST { $$ = $1; };
|
|
RoleId: ColId { $$ = $1; };
|
|
|
|
SignedIconst: ICONST { $$ = $1; }
|
|
| '-' ICONST { $$ = - $2; }
|
|
;
|
|
|
|
/*
|
|
* 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.
|
|
*/
|
|
|
|
/* Column identifier --- names that can be column, table, etc names.
|
|
*/
|
|
ColId: IDENT { $$ = $1; }
|
|
| unreserved_keyword { $$ = pstrdup($1); }
|
|
| col_name_keyword { $$ = pstrdup($1); }
|
|
;
|
|
|
|
/* Type identifier --- names that can be type names.
|
|
*/
|
|
type_name: IDENT { $$ = $1; }
|
|
| unreserved_keyword { $$ = pstrdup($1); }
|
|
;
|
|
|
|
/* Function identifier --- names that can be function names.
|
|
*/
|
|
function_name:
|
|
IDENT { $$ = $1; }
|
|
| unreserved_keyword { $$ = pstrdup($1); }
|
|
| func_name_keyword { $$ = pstrdup($1); }
|
|
;
|
|
|
|
/* Column label --- allowed labels in "AS" clauses.
|
|
* This presently includes *all* Postgres keywords.
|
|
*/
|
|
ColLabel: IDENT { $$ = $1; }
|
|
| unreserved_keyword { $$ = pstrdup($1); }
|
|
| col_name_keyword { $$ = pstrdup($1); }
|
|
| func_name_keyword { $$ = pstrdup($1); }
|
|
| reserved_keyword { $$ = pstrdup($1); }
|
|
;
|
|
|
|
|
|
/*
|
|
* Keyword classification lists. Generally, every keyword present in
|
|
* the Postgres grammar should appear in exactly one of these lists.
|
|
*
|
|
* Put a new keyword into the first list that it can go into without causing
|
|
* shift or reduce conflicts. The earlier lists define "less reserved"
|
|
* categories of keywords.
|
|
*/
|
|
|
|
/* "Unreserved" keywords --- available for use as any kind of name.
|
|
*/
|
|
unreserved_keyword:
|
|
ABORT_P
|
|
| ABSOLUTE_P
|
|
| ACCESS
|
|
| ACTION
|
|
| ADD_P
|
|
| ADMIN
|
|
| AFTER
|
|
| AGGREGATE
|
|
| ALSO
|
|
| ALTER
|
|
| ASSERTION
|
|
| ASSIGNMENT
|
|
| AT
|
|
| BACKWARD
|
|
| BEFORE
|
|
| BEGIN_P
|
|
| BY
|
|
| CACHE
|
|
| CALLED
|
|
| CASCADE
|
|
| CHAIN
|
|
| CHARACTERISTICS
|
|
| CHECKPOINT
|
|
| CLASS
|
|
| CLOSE
|
|
| CLUSTER
|
|
| COMMENT
|
|
| COMMIT
|
|
| COMMITTED
|
|
| CONNECTION
|
|
| CONSTRAINTS
|
|
| CONVERSION_P
|
|
| COPY
|
|
| CREATEDB
|
|
| CREATEROLE
|
|
| CREATEUSER
|
|
| CSV
|
|
| CURSOR
|
|
| CYCLE
|
|
| DATABASE
|
|
| DAY_P
|
|
| DEALLOCATE
|
|
| DECLARE
|
|
| DEFAULTS
|
|
| DEFERRED
|
|
| DEFINER
|
|
| DELETE_P
|
|
| DELIMITER
|
|
| DELIMITERS
|
|
| DISABLE_P
|
|
| DOMAIN_P
|
|
| DOUBLE_P
|
|
| DROP
|
|
| EACH
|
|
| ENABLE_P
|
|
| ENCODING
|
|
| ENCRYPTED
|
|
| ESCAPE
|
|
| EXCLUDING
|
|
| EXCLUSIVE
|
|
| EXECUTE
|
|
| EXPLAIN
|
|
| EXTERNAL
|
|
| FETCH
|
|
| FIRST_P
|
|
| FORCE
|
|
| FORWARD
|
|
| FUNCTION
|
|
| GLOBAL
|
|
| GRANTED
|
|
| HANDLER
|
|
| HEADER_P
|
|
| HOLD
|
|
| HOUR_P
|
|
| IF_P
|
|
| IMMEDIATE
|
|
| IMMUTABLE
|
|
| IMPLICIT_P
|
|
| INCLUDING
|
|
| INCREMENT
|
|
| INDEX
|
|
| INHERIT
|
|
| INHERITS
|
|
| INPUT_P
|
|
| INSENSITIVE
|
|
| INSERT
|
|
| INSTEAD
|
|
| INVOKER
|
|
| ISOLATION
|
|
| KEY
|
|
| LANCOMPILER
|
|
| LANGUAGE
|
|
| LARGE_P
|
|
| LAST_P
|
|
| LEVEL
|
|
| LISTEN
|
|
| LOAD
|
|
| LOCAL
|
|
| LOCATION
|
|
| LOCK_P
|
|
| LOGIN_P
|
|
| MATCH
|
|
| MAXVALUE
|
|
| MINUTE_P
|
|
| MINVALUE
|
|
| MODE
|
|
| MONTH_P
|
|
| MOVE
|
|
| NAMES
|
|
| NEXT
|
|
| NO
|
|
| NOCREATEDB
|
|
| NOCREATEROLE
|
|
| NOCREATEUSER
|
|
| NOINHERIT
|
|
| NOLOGIN_P
|
|
| NOSUPERUSER
|
|
| NOTHING
|
|
| NOTIFY
|
|
| NOWAIT
|
|
| OBJECT_P
|
|
| OF
|
|
| OIDS
|
|
| OPERATOR
|
|
| OPTION
|
|
| OWNED
|
|
| OWNER
|
|
| PARTIAL
|
|
| PASSWORD
|
|
| PREPARE
|
|
| PREPARED
|
|
| PRESERVE
|
|
| PRIOR
|
|
| PRIVILEGES
|
|
| PROCEDURAL
|
|
| PROCEDURE
|
|
| QUOTE
|
|
| READ
|
|
| REASSIGN
|
|
| RECHECK
|
|
| REINDEX
|
|
| RELATIVE_P
|
|
| RELEASE
|
|
| RENAME
|
|
| REPEATABLE
|
|
| REPLACE
|
|
| RESET
|
|
| RESTART
|
|
| RESTRICT
|
|
| RETURNS
|
|
| REVOKE
|
|
| ROLE
|
|
| ROLLBACK
|
|
| ROWS
|
|
| RULE
|
|
| SAVEPOINT
|
|
| SCHEMA
|
|
| SCROLL
|
|
| SECOND_P
|
|
| SECURITY
|
|
| SEQUENCE
|
|
| SERIALIZABLE
|
|
| SESSION
|
|
| SET
|
|
| SHARE
|
|
| SHOW
|
|
| SIMPLE
|
|
| STABLE
|
|
| START
|
|
| STATEMENT
|
|
| STATISTICS
|
|
| STDIN
|
|
| STDOUT
|
|
| STORAGE
|
|
| SUPERUSER_P
|
|
| SYSID
|
|
| SYSTEM_P
|
|
| STRICT_P
|
|
| TABLESPACE
|
|
| TEMP
|
|
| TEMPLATE
|
|
| TEMPORARY
|
|
| TOAST
|
|
| TRANSACTION
|
|
| TRIGGER
|
|
| TRUNCATE
|
|
| TRUSTED
|
|
| TYPE_P
|
|
| UNCOMMITTED
|
|
| UNENCRYPTED
|
|
| UNKNOWN
|
|
| UNLISTEN
|
|
| UNTIL
|
|
| UPDATE
|
|
| VACUUM
|
|
| VALID
|
|
| VALIDATOR
|
|
| VALUES
|
|
| VARYING
|
|
| VIEW
|
|
| VOLATILE
|
|
| WITH
|
|
| WITHOUT
|
|
| WORK
|
|
| WRITE
|
|
| YEAR_P
|
|
| ZONE
|
|
;
|
|
|
|
/* Column identifier --- keywords that can be column, table, etc names.
|
|
*
|
|
* Many of these keywords will in fact be recognized as type or function
|
|
* names too; but they have special productions for the purpose, and so
|
|
* can't be treated as "generic" type or function names.
|
|
*
|
|
* The type names appearing here are not usable as function names
|
|
* because they can be followed by '(' in typename productions, which
|
|
* looks too much like a function call for an LR(1) parser.
|
|
*/
|
|
col_name_keyword:
|
|
BIGINT
|
|
| BIT
|
|
| BOOLEAN_P
|
|
| CHAR_P
|
|
| CHARACTER
|
|
| COALESCE
|
|
| CONVERT
|
|
| DEC
|
|
| DECIMAL_P
|
|
| EXISTS
|
|
| EXTRACT
|
|
| FLOAT_P
|
|
| GREATEST
|
|
| INOUT
|
|
| INT_P
|
|
| INTEGER
|
|
| INTERVAL
|
|
| LEAST
|
|
| NATIONAL
|
|
| NCHAR
|
|
| NONE
|
|
| NULLIF
|
|
| NUMERIC
|
|
| OUT_P
|
|
| OVERLAY
|
|
| POSITION
|
|
| PRECISION
|
|
| REAL
|
|
| ROW
|
|
| SETOF
|
|
| SMALLINT
|
|
| SUBSTRING
|
|
| TIME
|
|
| TIMESTAMP
|
|
| TREAT
|
|
| TRIM
|
|
| VARCHAR
|
|
;
|
|
|
|
/* Function identifier --- keywords that can be function names.
|
|
*
|
|
* Most of these are keywords that are used as operators in expressions;
|
|
* in general such keywords can't be column names because they would be
|
|
* ambiguous with variables, but they are unambiguous as function identifiers.
|
|
*
|
|
* Do not include POSITION, SUBSTRING, etc here since they have explicit
|
|
* productions in a_expr to support the goofy SQL9x argument syntax.
|
|
* - thomas 2000-11-28
|
|
*/
|
|
func_name_keyword:
|
|
AUTHORIZATION
|
|
| BETWEEN
|
|
| BINARY
|
|
| CROSS
|
|
| FREEZE
|
|
| FULL
|
|
| ILIKE
|
|
| INNER_P
|
|
| IS
|
|
| ISNULL
|
|
| JOIN
|
|
| LEFT
|
|
| LIKE
|
|
| NATURAL
|
|
| NOTNULL
|
|
| OUTER_P
|
|
| OVERLAPS
|
|
| RIGHT
|
|
| SIMILAR
|
|
| VERBOSE
|
|
;
|
|
|
|
/* Reserved keyword --- these keywords are usable only as a ColLabel.
|
|
*
|
|
* Keywords appear here if they could not be distinguished from variable,
|
|
* type, or function names in some contexts. Don't put things here unless
|
|
* forced to.
|
|
*/
|
|
reserved_keyword:
|
|
ALL
|
|
| ANALYSE
|
|
| ANALYZE
|
|
| AND
|
|
| ANY
|
|
| ARRAY
|
|
| AS
|
|
| ASC
|
|
| ASYMMETRIC
|
|
| BOTH
|
|
| CASE
|
|
| CAST
|
|
| CHECK
|
|
| COLLATE
|
|
| COLUMN
|
|
| CONSTRAINT
|
|
| CREATE
|
|
| CURRENT_DATE
|
|
| CURRENT_ROLE
|
|
| CURRENT_TIME
|
|
| CURRENT_TIMESTAMP
|
|
| CURRENT_USER
|
|
| DEFAULT
|
|
| DEFERRABLE
|
|
| DESC
|
|
| DISTINCT
|
|
| DO
|
|
| ELSE
|
|
| END_P
|
|
| EXCEPT
|
|
| FALSE_P
|
|
| FOR
|
|
| FOREIGN
|
|
| FROM
|
|
| GRANT
|
|
| GROUP_P
|
|
| HAVING
|
|
| IN_P
|
|
| INITIALLY
|
|
| INTERSECT
|
|
| INTO
|
|
| LEADING
|
|
| LIMIT
|
|
| LOCALTIME
|
|
| LOCALTIMESTAMP
|
|
| NEW
|
|
| NOT
|
|
| NULL_P
|
|
| OFF
|
|
| OFFSET
|
|
| OLD
|
|
| ON
|
|
| ONLY
|
|
| OR
|
|
| ORDER
|
|
| PLACING
|
|
| PRIMARY
|
|
| REFERENCES
|
|
| SELECT
|
|
| SESSION_USER
|
|
| SOME
|
|
| SYMMETRIC
|
|
| TABLE
|
|
| THEN
|
|
| TO
|
|
| TRAILING
|
|
| TRUE_P
|
|
| UNION
|
|
| UNIQUE
|
|
| USER
|
|
| USING
|
|
| WHEN
|
|
| WHERE
|
|
;
|
|
|
|
|
|
SpecialRuleRelation:
|
|
OLD
|
|
{
|
|
if (QueryIsRule)
|
|
$$ = "*OLD*";
|
|
else
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_SYNTAX_ERROR),
|
|
errmsg("OLD used in query that is not in a rule")));
|
|
}
|
|
| NEW
|
|
{
|
|
if (QueryIsRule)
|
|
$$ = "*NEW*";
|
|
else
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_SYNTAX_ERROR),
|
|
errmsg("NEW used in query that is not in a rule")));
|
|
}
|
|
;
|
|
|
|
%%
|
|
|
|
static Node *
|
|
makeColumnRef(char *relname, List *indirection)
|
|
{
|
|
/*
|
|
* Generate a ColumnRef node, with an A_Indirection node added if there
|
|
* is any subscripting in the specified indirection list. However,
|
|
* any field selection at the start of the indirection list must be
|
|
* transposed into the "fields" part of the ColumnRef node.
|
|
*/
|
|
ColumnRef *c = makeNode(ColumnRef);
|
|
int nfields = 0;
|
|
ListCell *l;
|
|
|
|
foreach(l, indirection)
|
|
{
|
|
if (IsA(lfirst(l), A_Indices))
|
|
{
|
|
A_Indirection *i = makeNode(A_Indirection);
|
|
|
|
if (nfields == 0)
|
|
{
|
|
/* easy case - all indirection goes to A_Indirection */
|
|
c->fields = list_make1(makeString(relname));
|
|
i->indirection = indirection;
|
|
}
|
|
else
|
|
{
|
|
/* got to split the list in two */
|
|
i->indirection = list_copy_tail(indirection, nfields);
|
|
indirection = list_truncate(indirection, nfields);
|
|
c->fields = lcons(makeString(relname), indirection);
|
|
}
|
|
i->arg = (Node *) c;
|
|
return (Node *) i;
|
|
}
|
|
nfields++;
|
|
}
|
|
/* No subscripting, so all indirection gets added to field list */
|
|
c->fields = lcons(makeString(relname), indirection);
|
|
return (Node *) c;
|
|
}
|
|
|
|
static Node *
|
|
makeTypeCast(Node *arg, TypeName *typename)
|
|
{
|
|
/*
|
|
* Simply generate a TypeCast node.
|
|
*
|
|
* Earlier we would determine whether an A_Const would
|
|
* be acceptable, however Domains require coerce_type()
|
|
* to process them -- applying constraints as required.
|
|
*/
|
|
TypeCast *n = makeNode(TypeCast);
|
|
n->arg = arg;
|
|
n->typename = typename;
|
|
return (Node *) n;
|
|
}
|
|
|
|
static Node *
|
|
makeStringConst(char *str, TypeName *typename)
|
|
{
|
|
A_Const *n = makeNode(A_Const);
|
|
|
|
n->val.type = T_String;
|
|
n->val.val.str = str;
|
|
n->typename = typename;
|
|
|
|
return (Node *)n;
|
|
}
|
|
|
|
static Node *
|
|
makeIntConst(int val)
|
|
{
|
|
A_Const *n = makeNode(A_Const);
|
|
n->val.type = T_Integer;
|
|
n->val.val.ival = val;
|
|
n->typename = SystemTypeName("int4");
|
|
|
|
return (Node *)n;
|
|
}
|
|
|
|
static Node *
|
|
makeFloatConst(char *str)
|
|
{
|
|
A_Const *n = makeNode(A_Const);
|
|
|
|
n->val.type = T_Float;
|
|
n->val.val.str = str;
|
|
n->typename = SystemTypeName("float8");
|
|
|
|
return (Node *)n;
|
|
}
|
|
|
|
static Node *
|
|
makeAConst(Value *v)
|
|
{
|
|
Node *n;
|
|
|
|
switch (v->type)
|
|
{
|
|
case T_Float:
|
|
n = makeFloatConst(v->val.str);
|
|
break;
|
|
|
|
case T_Integer:
|
|
n = makeIntConst(v->val.ival);
|
|
break;
|
|
|
|
case T_String:
|
|
default:
|
|
n = makeStringConst(v->val.str, NULL);
|
|
break;
|
|
}
|
|
|
|
return n;
|
|
}
|
|
|
|
/* makeDefElem()
|
|
* Create a DefElem node and set contents.
|
|
* Could be moved to nodes/makefuncs.c if this is useful elsewhere.
|
|
*/
|
|
static DefElem *
|
|
makeDefElem(char *name, Node *arg)
|
|
{
|
|
DefElem *f = makeNode(DefElem);
|
|
f->defname = name;
|
|
f->arg = arg;
|
|
return f;
|
|
}
|
|
|
|
/* makeBoolAConst()
|
|
* Create an A_Const node and initialize to a boolean constant.
|
|
*/
|
|
static A_Const *
|
|
makeBoolAConst(bool state)
|
|
{
|
|
A_Const *n = makeNode(A_Const);
|
|
n->val.type = T_String;
|
|
n->val.val.str = (state? "t": "f");
|
|
n->typename = SystemTypeName("bool");
|
|
return n;
|
|
}
|
|
|
|
/* makeRowNullTest()
|
|
* Generate separate operator nodes for a single row descriptor test.
|
|
*
|
|
* Eventually this should be eliminated in favor of making the NullTest
|
|
* node type capable of handling it directly.
|
|
*/
|
|
static Node *
|
|
makeRowNullTest(NullTestType test, RowExpr *row)
|
|
{
|
|
Node *result = NULL;
|
|
ListCell *arg;
|
|
|
|
foreach(arg, row->args)
|
|
{
|
|
NullTest *n;
|
|
|
|
n = makeNode(NullTest);
|
|
n->arg = (Expr *) lfirst(arg);
|
|
n->nulltesttype = test;
|
|
|
|
if (result == NULL)
|
|
result = (Node *) n;
|
|
else if (test == IS_NOT_NULL)
|
|
result = (Node *) makeA_Expr(AEXPR_OR, NIL, result, (Node *)n);
|
|
else
|
|
result = (Node *) makeA_Expr(AEXPR_AND, NIL, result, (Node *)n);
|
|
}
|
|
|
|
if (result == NULL)
|
|
{
|
|
/* zero-length rows? Generate constant TRUE or FALSE */
|
|
result = (Node *) makeBoolAConst(test == IS_NULL);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/* makeOverlaps()
|
|
* Create and populate a FuncCall node to support the OVERLAPS operator.
|
|
*/
|
|
static FuncCall *
|
|
makeOverlaps(List *largs, List *rargs)
|
|
{
|
|
FuncCall *n = makeNode(FuncCall);
|
|
n->funcname = SystemFuncName("overlaps");
|
|
if (list_length(largs) == 1)
|
|
largs = lappend(largs, largs);
|
|
else if (list_length(largs) != 2)
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_SYNTAX_ERROR),
|
|
errmsg("wrong number of parameters on left side of OVERLAPS expression")));
|
|
if (list_length(rargs) == 1)
|
|
rargs = lappend(rargs, rargs);
|
|
else if (list_length(rargs) != 2)
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_SYNTAX_ERROR),
|
|
errmsg("wrong number of parameters on right side of OVERLAPS expression")));
|
|
n->args = list_concat(largs, rargs);
|
|
n->agg_star = FALSE;
|
|
n->agg_distinct = FALSE;
|
|
return n;
|
|
}
|
|
|
|
/* check_qualified_name --- check the result of qualified_name production
|
|
*
|
|
* It's easiest to let the grammar production for qualified_name allow
|
|
* subscripts and '*', which we then must reject here.
|
|
*/
|
|
static void
|
|
check_qualified_name(List *names)
|
|
{
|
|
ListCell *i;
|
|
|
|
foreach(i, names)
|
|
{
|
|
if (!IsA(lfirst(i), String))
|
|
yyerror("syntax error");
|
|
else if (strcmp(strVal(lfirst(i)), "*") == 0)
|
|
yyerror("syntax error");
|
|
}
|
|
}
|
|
|
|
/* check_func_name --- check the result of func_name production
|
|
*
|
|
* It's easiest to let the grammar production for func_name allow subscripts
|
|
* and '*', which we then must reject here.
|
|
*/
|
|
static List *
|
|
check_func_name(List *names)
|
|
{
|
|
ListCell *i;
|
|
|
|
foreach(i, names)
|
|
{
|
|
if (!IsA(lfirst(i), String))
|
|
yyerror("syntax error");
|
|
else if (strcmp(strVal(lfirst(i)), "*") == 0)
|
|
yyerror("syntax error");
|
|
}
|
|
return names;
|
|
}
|
|
|
|
/* extractArgTypes()
|
|
* Given a list of FunctionParameter nodes, extract a list of just the
|
|
* argument types (TypeNames) for input parameters only. This is what
|
|
* is needed to look up an existing function, which is what is wanted by
|
|
* the productions that use this call.
|
|
*/
|
|
static List *
|
|
extractArgTypes(List *parameters)
|
|
{
|
|
List *result = NIL;
|
|
ListCell *i;
|
|
|
|
foreach(i, parameters)
|
|
{
|
|
FunctionParameter *p = (FunctionParameter *) lfirst(i);
|
|
|
|
if (p->mode != FUNC_PARAM_OUT) /* keep if IN or INOUT */
|
|
result = lappend(result, p->argType);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/* findLeftmostSelect()
|
|
* Find the leftmost component SelectStmt in a set-operation parsetree.
|
|
*/
|
|
static SelectStmt *
|
|
findLeftmostSelect(SelectStmt *node)
|
|
{
|
|
while (node && node->op != SETOP_NONE)
|
|
node = node->larg;
|
|
Assert(node && IsA(node, SelectStmt) && node->larg == NULL);
|
|
return node;
|
|
}
|
|
|
|
/* insertSelectOptions()
|
|
* Insert ORDER BY, etc into an already-constructed SelectStmt.
|
|
*
|
|
* This routine is just to avoid duplicating code in SelectStmt productions.
|
|
*/
|
|
static void
|
|
insertSelectOptions(SelectStmt *stmt,
|
|
List *sortClause, Node *lockingClause,
|
|
Node *limitOffset, Node *limitCount)
|
|
{
|
|
/*
|
|
* Tests here are to reject constructs like
|
|
* (SELECT foo ORDER BY bar) ORDER BY baz
|
|
*/
|
|
if (sortClause)
|
|
{
|
|
if (stmt->sortClause)
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_SYNTAX_ERROR),
|
|
errmsg("multiple ORDER BY clauses not allowed")));
|
|
stmt->sortClause = sortClause;
|
|
}
|
|
if (lockingClause)
|
|
{
|
|
if (stmt->lockingClause)
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_SYNTAX_ERROR),
|
|
errmsg("multiple FOR UPDATE/FOR SHARE clauses not allowed")));
|
|
stmt->lockingClause = (LockingClause *) lockingClause;
|
|
}
|
|
if (limitOffset)
|
|
{
|
|
if (stmt->limitOffset)
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_SYNTAX_ERROR),
|
|
errmsg("multiple OFFSET clauses not allowed")));
|
|
stmt->limitOffset = limitOffset;
|
|
}
|
|
if (limitCount)
|
|
{
|
|
if (stmt->limitCount)
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_SYNTAX_ERROR),
|
|
errmsg("multiple LIMIT clauses not allowed")));
|
|
stmt->limitCount = limitCount;
|
|
}
|
|
}
|
|
|
|
static Node *
|
|
makeSetOp(SetOperation op, bool all, Node *larg, Node *rarg)
|
|
{
|
|
SelectStmt *n = makeNode(SelectStmt);
|
|
|
|
n->op = op;
|
|
n->all = all;
|
|
n->larg = (SelectStmt *) larg;
|
|
n->rarg = (SelectStmt *) rarg;
|
|
return (Node *) n;
|
|
}
|
|
|
|
/* SystemFuncName()
|
|
* Build a properly-qualified reference to a built-in function.
|
|
*/
|
|
List *
|
|
SystemFuncName(char *name)
|
|
{
|
|
return list_make2(makeString("pg_catalog"), makeString(name));
|
|
}
|
|
|
|
/* SystemTypeName()
|
|
* Build a properly-qualified reference to a built-in type.
|
|
*
|
|
* typmod is defaulted, but may be changed afterwards by caller.
|
|
*/
|
|
TypeName *
|
|
SystemTypeName(char *name)
|
|
{
|
|
TypeName *n = makeNode(TypeName);
|
|
|
|
n->names = list_make2(makeString("pg_catalog"), makeString(name));
|
|
n->typmod = -1;
|
|
return n;
|
|
}
|
|
|
|
/* parser_init()
|
|
* Initialize to parse one query string
|
|
*/
|
|
void
|
|
parser_init(void)
|
|
{
|
|
QueryIsRule = FALSE;
|
|
}
|
|
|
|
/* exprIsNullConstant()
|
|
* Test whether an a_expr is a plain NULL constant or not.
|
|
*/
|
|
bool
|
|
exprIsNullConstant(Node *arg)
|
|
{
|
|
if (arg && IsA(arg, A_Const))
|
|
{
|
|
A_Const *con = (A_Const *) arg;
|
|
|
|
if (con->val.type == T_Null &&
|
|
con->typename == NULL)
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
/* doNegate()
|
|
* Handle negation of a numeric constant.
|
|
*
|
|
* Formerly, we did this here because the optimizer couldn't cope with
|
|
* indexquals that looked like "var = -4" --- it wants "var = const"
|
|
* and a unary minus operator applied to a constant didn't qualify.
|
|
* As of Postgres 7.0, that problem doesn't exist anymore because there
|
|
* is a constant-subexpression simplifier in the optimizer. However,
|
|
* there's still a good reason for doing this here, which is that we can
|
|
* postpone committing to a particular internal representation for simple
|
|
* negative constants. It's better to leave "-123.456" in string form
|
|
* until we know what the desired type is.
|
|
*/
|
|
static Node *
|
|
doNegate(Node *n)
|
|
{
|
|
if (IsA(n, A_Const))
|
|
{
|
|
A_Const *con = (A_Const *)n;
|
|
|
|
if (con->val.type == T_Integer)
|
|
{
|
|
con->val.val.ival = -con->val.val.ival;
|
|
return n;
|
|
}
|
|
if (con->val.type == T_Float)
|
|
{
|
|
doNegateFloat(&con->val);
|
|
return n;
|
|
}
|
|
}
|
|
|
|
return (Node *) makeSimpleA_Expr(AEXPR_OP, "-", NULL, n);
|
|
}
|
|
|
|
static void
|
|
doNegateFloat(Value *v)
|
|
{
|
|
char *oldval = v->val.str;
|
|
|
|
Assert(IsA(v, Float));
|
|
if (*oldval == '+')
|
|
oldval++;
|
|
if (*oldval == '-')
|
|
v->val.str = oldval+1; /* just strip the '-' */
|
|
else
|
|
{
|
|
char *newval = (char *) palloc(strlen(oldval) + 2);
|
|
|
|
*newval = '-';
|
|
strcpy(newval+1, oldval);
|
|
v->val.str = newval;
|
|
}
|
|
}
|
|
|
|
#include "scan.c"
|