diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/backend/parser/analyze.c | 147 | ||||
-rw-r--r-- | src/backend/parser/gram.y | 429 | ||||
-rw-r--r-- | src/include/nodes/parsenodes.h | 45 |
3 files changed, 338 insertions, 283 deletions
diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c index a78a0571fe4..612481deabc 100644 --- a/src/backend/parser/analyze.c +++ b/src/backend/parser/analyze.c @@ -6,7 +6,7 @@ * Portions Copyright (c) 1996-2000, PostgreSQL, Inc * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: analyze.c,v 1.138 2000/02/29 12:28:25 wieck Exp $ + * $Id: analyze.c,v 1.139 2000/03/01 05:18:20 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -44,6 +44,7 @@ static Query *transformAlterTableStmt(ParseState *pstate, AlterTableStmt *stmt); static void transformForUpdate(Query *qry, List *forUpdate); static void transformFkeyGetPrimaryKey(FkConstraint *fkconstraint); +static void transformConstraintAttrs(List *constraintList); static void transformColumnType(ParseState *pstate, ColumnDef *column); /* kluge to return extra info from transformCreateStmt() */ @@ -589,6 +590,7 @@ transformCreateStmt(ParseState *pstate, CreateStmt *stmt) IndexStmt *index, *pkey = NULL; IndexElem *iparam; + bool saw_nullable; q = makeNode(Query); q->commandType = CMD_UTILITY; @@ -621,6 +623,12 @@ transformCreateStmt(ParseState *pstate, CreateStmt *stmt) FuncCall *funccallnode; CreateSeqStmt *sequence; + /* + * Create appropriate constraints for SERIAL. We do this + * in full, rather than shortcutting, so that we will + * detect any conflicting constraints the user wrote + * (like a different DEFAULT). + */ sname = makeObjectName(stmt->relname, column->colname, "seq"); /* @@ -644,27 +652,37 @@ transformCreateStmt(ParseState *pstate, CreateStmt *stmt) constraint->raw_expr = (Node *) funccallnode; constraint->cooked_expr = NULL; constraint->keys = NULL; - - column->constraints = lappend(column->constraints, constraint); + column->constraints = lappend(column->constraints, + constraint); constraint = makeNode(Constraint); constraint->contype = CONSTR_UNIQUE; constraint->name = makeObjectName(stmt->relname, column->colname, "key"); - column->constraints = lappend(column->constraints, constraint); + column->constraints = lappend(column->constraints, + constraint); + + constraint = makeNode(Constraint); + constraint->contype = CONSTR_NOTNULL; + column->constraints = lappend(column->constraints, + constraint); sequence = makeNode(CreateSeqStmt); sequence->seqname = pstrdup(sname); sequence->options = NIL; elog(NOTICE, "CREATE TABLE will create implicit sequence '%s' for SERIAL column '%s.%s'", - sequence->seqname, stmt->relname, column->colname); + sequence->seqname, stmt->relname, column->colname); blist = lcons(sequence, NIL); } /* Process column constraints, if any... */ + transformConstraintAttrs(column->constraints); + + saw_nullable = false; + foreach(clist, column->constraints) { constraint = lfirst(clist); @@ -676,7 +694,7 @@ transformCreateStmt(ParseState *pstate, CreateStmt *stmt) * to be processed later. * ---------- */ - if (nodeTag(constraint) == T_FkConstraint) + if (IsA(constraint, FkConstraint)) { Ident *id = makeNode(Ident); id->name = column->colname; @@ -693,23 +711,19 @@ transformCreateStmt(ParseState *pstate, CreateStmt *stmt) switch (constraint->contype) { case CONSTR_NULL: - - /* - * We should mark this explicitly, so we - * can tell if NULL and NOT NULL are both - * specified - */ - if (column->is_not_null) + if (saw_nullable && column->is_not_null) elog(ERROR, "CREATE TABLE/(NOT) NULL conflicting declaration" " for '%s.%s'", stmt->relname, column->colname); column->is_not_null = FALSE; + saw_nullable = true; break; case CONSTR_NOTNULL: - if (column->is_not_null) - elog(ERROR, "CREATE TABLE/NOT NULL already specified" + if (saw_nullable && ! column->is_not_null) + elog(ERROR, "CREATE TABLE/(NOT) NULL conflicting declaration" " for '%s.%s'", stmt->relname, column->colname); column->is_not_null = TRUE; + saw_nullable = true; break; case CONSTR_DEFAULT: @@ -742,6 +756,13 @@ transformCreateStmt(ParseState *pstate, CreateStmt *stmt) constraints = lappend(constraints, constraint); break; + case CONSTR_ATTR_DEFERRABLE: + case CONSTR_ATTR_NOT_DEFERRABLE: + case CONSTR_ATTR_DEFERRED: + case CONSTR_ATTR_IMMEDIATE: + /* transformConstraintAttrs took care of these */ + break; + default: elog(ERROR, "parser: unrecognized constraint (internal error)"); break; @@ -767,10 +788,16 @@ transformCreateStmt(ParseState *pstate, CreateStmt *stmt) constraints = lappend(constraints, constraint); break; + case CONSTR_NULL: case CONSTR_NOTNULL: case CONSTR_DEFAULT: + case CONSTR_ATTR_DEFERRABLE: + case CONSTR_ATTR_NOT_DEFERRABLE: + case CONSTR_ATTR_DEFERRED: + case CONSTR_ATTR_IMMEDIATE: elog(ERROR, "parser: illegal context for constraint (internal error)"); break; + default: elog(ERROR, "parser: unrecognized constraint (internal error)"); break; @@ -2000,6 +2027,95 @@ transformFkeyGetPrimaryKey(FkConstraint *fkconstraint) } /* + * Preprocess a list of column constraint clauses + * to attach constraint attributes to their primary constraint nodes + * and detect inconsistent/misplaced constraint attributes. + * + * NOTE: currently, attributes are only supported for FOREIGN KEY primary + * constraints, but someday they ought to be supported for other constraints. + */ +static void +transformConstraintAttrs(List *constraintList) +{ + Node *lastprimarynode = NULL; + bool saw_deferrability = false; + bool saw_initially = false; + List *clist; + + foreach(clist, constraintList) + { + Node *node = lfirst(clist); + + if (! IsA(node, Constraint)) + { + lastprimarynode = node; + /* reset flags for new primary node */ + saw_deferrability = false; + saw_initially = false; + } + else + { + Constraint *con = (Constraint *) node; + + switch (con->contype) + { + case CONSTR_ATTR_DEFERRABLE: + if (lastprimarynode == NULL || + ! IsA(lastprimarynode, FkConstraint)) + elog(ERROR, "Misplaced DEFERRABLE clause"); + if (saw_deferrability) + elog(ERROR, "Multiple DEFERRABLE/NOT DEFERRABLE clauses not allowed"); + saw_deferrability = true; + ((FkConstraint *) lastprimarynode)->deferrable = true; + break; + case CONSTR_ATTR_NOT_DEFERRABLE: + if (lastprimarynode == NULL || + ! IsA(lastprimarynode, FkConstraint)) + elog(ERROR, "Misplaced NOT DEFERRABLE clause"); + if (saw_deferrability) + elog(ERROR, "Multiple DEFERRABLE/NOT DEFERRABLE clauses not allowed"); + saw_deferrability = true; + ((FkConstraint *) lastprimarynode)->deferrable = false; + if (saw_initially && + ((FkConstraint *) lastprimarynode)->initdeferred) + elog(ERROR, "INITIALLY DEFERRED constraint must be DEFERRABLE"); + break; + case CONSTR_ATTR_DEFERRED: + if (lastprimarynode == NULL || + ! IsA(lastprimarynode, FkConstraint)) + elog(ERROR, "Misplaced INITIALLY DEFERRED clause"); + if (saw_initially) + elog(ERROR, "Multiple INITIALLY IMMEDIATE/DEFERRED clauses not allowed"); + saw_initially = true; + ((FkConstraint *) lastprimarynode)->initdeferred = true; + /* If only INITIALLY DEFERRED appears, assume DEFERRABLE */ + if (! saw_deferrability) + ((FkConstraint *) lastprimarynode)->deferrable = true; + else if (! ((FkConstraint *) lastprimarynode)->deferrable) + elog(ERROR, "INITIALLY DEFERRED constraint must be DEFERRABLE"); + break; + case CONSTR_ATTR_IMMEDIATE: + if (lastprimarynode == NULL || + ! IsA(lastprimarynode, FkConstraint)) + elog(ERROR, "Misplaced INITIALLY IMMEDIATE clause"); + if (saw_initially) + elog(ERROR, "Multiple INITIALLY IMMEDIATE/DEFERRED clauses not allowed"); + saw_initially = true; + ((FkConstraint *) lastprimarynode)->initdeferred = false; + break; + default: + /* Otherwise it's not an attribute */ + lastprimarynode = node; + /* reset flags for new primary node */ + saw_deferrability = false; + saw_initially = false; + break; + } + } + } +} + +/* * Special handling of type definition for a column */ static void @@ -2027,4 +2143,3 @@ transformColumnType(ParseState *pstate, ColumnDef *column) } } } - diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index b39f2cf175e..79bd88f8f1a 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -11,7 +11,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.152 2000/02/26 18:13:41 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.153 2000/03/01 05:18:19 tgl Exp $ * * HISTORY * AUTHOR DATE MAJOR EVENT @@ -168,7 +168,7 @@ static void doNegateFloat(Value *v); %type <chr> operation, TriggerOneEvent %type <list> stmtblock, stmtmulti, - result, relation_name_list, OptTableElementList, + result, OptTempTableName, relation_name_list, OptTableElementList, OptInherit, definition, opt_distinct, opt_with, func_args, func_args_list, func_as, oper_argtypes, RuleActionList, RuleActionMulti, @@ -182,7 +182,7 @@ static void doNegateFloat(Value *v); %type <node> func_return %type <boolean> set_opt -%type <boolean> TriggerForOpt, TriggerForType, OptTemp, OptTempType, OptTempScope +%type <boolean> TriggerForOpt, TriggerForType, OptTemp %type <list> for_update_clause, update_list %type <boolean> opt_all @@ -263,15 +263,12 @@ static void doNegateFloat(Value *v); %type <str> TypeId %type <node> TableConstraint -%type <list> ColQualList, ColQualifier -%type <list> ColQualListWithNull -%type <node> ColConstraint, ColConstraintElem, PrimaryKey, NotNull -%type <node> DefaultClause, DefaultExpr -%type <node> ColConstraintWithNull, ColConstraintElemWithNull +%type <list> ColQualList +%type <node> ColConstraint, ColConstraintElem, ConstraintAttr %type <ival> key_actions, key_delete, key_update, key_reference %type <str> key_match -%type <ival> ConstraintAttribute, DeferrabilityClause, - TimeClause +%type <ival> ConstraintAttributeSpec, ConstraintDeferrabilitySpec, + ConstraintTimeSpec %type <list> constraints_set_list %type <list> constraints_set_namelist @@ -987,24 +984,25 @@ CreateStmt: CREATE OptTemp TABLE relation_name '(' OptTableElementList ')' } ; -OptTemp: OptTempType { $$ = $1; } - | OptTempScope OptTempType { $$ = $2; } - ; - -OptTempType: TEMP { $$ = TRUE; } - | TEMPORARY { $$ = TRUE; } - | /*EMPTY*/ { $$ = FALSE; } - ; - -OptTempScope: GLOBAL +/* + * Redundancy here is needed to avoid shift/reduce conflicts, + * since TEMP is not a reserved word. See also OptTempTableName. + */ +OptTemp: TEMPORARY { $$ = TRUE; } + | TEMP { $$ = TRUE; } + | LOCAL TEMPORARY { $$ = TRUE; } + | LOCAL TEMP { $$ = TRUE; } + | GLOBAL TEMPORARY { elog(ERROR, "GLOBAL TEMPORARY TABLE is not currently supported"); $$ = TRUE; } - | LOCAL + | GLOBAL TEMP { - $$ = FALSE; + elog(ERROR, "GLOBAL TEMPORARY TABLE is not currently supported"); + $$ = TRUE; } + | /*EMPTY*/ { $$ = FALSE; } ; OptTableElementList: OptTableElementList ',' OptTableElement @@ -1028,16 +1026,11 @@ OptTableElement: columnDef { $$ = $1; } | TableConstraint { $$ = $1; } ; -columnDef: ColId Typename ColQualifier opt_collate +columnDef: ColId Typename ColQualList opt_collate { ColumnDef *n = makeNode(ColumnDef); n->colname = $1; n->typename = $2; -#if 0 - n->raw_default = NULL; - n->cooked_default = NULL; - n->is_not_null = FALSE; -#endif n->constraints = $3; if ($4 != NULL) @@ -1046,18 +1039,13 @@ columnDef: ColId Typename ColQualifier opt_collate $$ = (Node *)n; } - | ColId SERIAL ColQualifier opt_collate + | ColId SERIAL ColQualList opt_collate { ColumnDef *n = makeNode(ColumnDef); n->colname = $1; n->typename = makeNode(TypeName); n->typename->name = xlateSqlType("integer"); n->typename->typmod = -1; -#if 0 - n->raw_default = NULL; - n->cooked_default = NULL; -#endif - n->is_not_null = TRUE; n->is_sequence = TRUE; n->constraints = $3; @@ -1069,141 +1057,25 @@ columnDef: ColId Typename ColQualifier opt_collate } ; -/* - * ColQualifier encapsulates an entire column qualification, - * including DEFAULT, constraints, and constraint attributes. - * Note that the DefaultClause handles the empty case. - */ -ColQualifier: DefaultClause ColQualList - { - if ($1 != NULL) - $$ = lcons($1, $2); - else - $$ = $2; - } - | NotNull DefaultClause ColQualListWithNull - { - $$ = lcons($1, $3); - if ($2 != NULL) - $$ = lcons($2, $$); - } - | DefaultExpr NotNull ColQualListWithNull - { - $$ = lcons($2, $3); - if ($1 != NULL) - $$ = lcons($1, $$); - } - | DefaultExpr NotNull - { - $$ = lcons($2, NIL); - if ($1 != NULL) - $$ = lcons($1, $$); - } - | NotNull DefaultClause - { - $$ = lcons($1, NIL); - if ($2 != NULL) - $$ = lcons($2, $$); - } - | NULL_P DefaultClause ColQualListWithNull - { - $$ = $3; - if ($2 != NULL) - $$ = lcons($2, $$); - } - | NULL_P DefaultClause - { - if ($2 != NULL) - $$ = lcons($2, NIL); - else - $$ = NIL; - } - | DefaultClause - { - if ($1 != NULL) - $$ = lcons($1, NIL); - else - $$ = NIL; - } - ; - -/* - * 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). - */ -DefaultClause: DefaultExpr { $$ = $1; } - | /*EMPTY*/ { $$ = NULL; } +ColQualList: ColQualList ColConstraint { $$ = lappend($1, $2); } + | /*EMPTY*/ { $$ = NIL; } ; -DefaultExpr: DEFAULT NULL_P - { - Constraint *n = makeNode(Constraint); - n->contype = CONSTR_DEFAULT; - n->name = NULL; - n->raw_expr = NULL; - n->cooked_expr = NULL; - n->keys = NULL; - $$ = (Node *)n; - } - | DEFAULT b_expr - { - Constraint *n = makeNode(Constraint); - n->contype = CONSTR_DEFAULT; - n->name = NULL; - n->raw_expr = $2; - n->cooked_expr = NULL; - n->keys = NULL; - $$ = (Node *)n; - } - ; - -ColQualList: ColQualList ColConstraint - { - if ($2 != NULL) - $$ = lappend($1, $2); - else - $$ = $1; - } - | ColConstraint - { - if ($1 != NULL) - $$ = lcons($1, NIL); - else - $$ = NULL; - } - ; - -ColQualListWithNull: ColConstraintWithNull ColQualListWithNull - { - if ($1 != NULL) - $$ = lcons($1, $2); - else - $$ = $2; - } - | ColConstraintWithNull - { - if ($1 != NULL) - $$ = lcons($1, NIL); - else - $$ = NULL; - } - ; - -ColConstraint: CONSTRAINT name ColConstraintElem +ColConstraint: + CONSTRAINT name ColConstraintElem { switch (nodeTag($3)) { case T_Constraint: { Constraint *n = (Constraint *)$3; - if (n != NULL) n->name = $2; + n->name = $2; } break; case T_FkConstraint: { FkConstraint *n = (FkConstraint *)$3; - if (n != NULL) n->constr_name = $2; + n->constr_name = $2; } break; default: @@ -1213,30 +1085,7 @@ ColConstraint: CONSTRAINT name ColConstraintElem } | ColConstraintElem { $$ = $1; } - ; - -ColConstraintWithNull: CONSTRAINT name ColConstraintElemWithNull - { - switch (nodeTag($3)) - { - case T_Constraint: - { - Constraint *n = (Constraint *)$3; - if (n != NULL) n->name = $2; - } - break; - case T_FkConstraint: - { - FkConstraint *n = (FkConstraint *)$3; - if (n != NULL) n->constr_name = $2; - } - break; - default: - break; - } - $$ = $3; - } - | ColConstraintElemWithNull + | ConstraintAttr { $$ = $1; } ; @@ -1250,10 +1099,31 @@ ColConstraintWithNull: CONSTRAINT name ColConstraintElemWithNull * 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: ColConstraintElemWithNull +ColConstraintElem: + NOT NULL_P { - $$ = $1; + Constraint *n = makeNode(Constraint); + n->contype = CONSTR_NOTNULL; + n->name = NULL; + n->raw_expr = NULL; + n->cooked_expr = NULL; + n->keys = 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; + $$ = (Node *)n; } | UNIQUE { @@ -1265,13 +1135,17 @@ ColConstraintElem: ColConstraintElemWithNull n->keys = NULL; $$ = (Node *)n; } - | PrimaryKey + | PRIMARY KEY { - $$ = $1; + Constraint *n = makeNode(Constraint); + n->contype = CONSTR_PRIMARY; + n->name = NULL; + n->raw_expr = NULL; + n->cooked_expr = NULL; + n->keys = NULL; + $$ = (Node *)n; } - ; - -ColConstraintElemWithNull: CHECK '(' a_expr ')' + | CHECK '(' a_expr ')' { Constraint *n = makeNode(Constraint); n->contype = CONSTR_CHECK; @@ -1281,22 +1155,27 @@ ColConstraintElemWithNull: CHECK '(' a_expr ')' n->keys = NULL; $$ = (Node *)n; } - | REFERENCES ColId opt_column_list - key_match key_actions ConstraintAttribute + | DEFAULT NULL_P { - FkConstraint *n = makeNode(FkConstraint); - n->constr_name = NULL; - n->pktable_name = $2; - n->fk_attrs = NIL; - n->pk_attrs = $3; - n->match_type = $4; - n->actions = $5; - n->deferrable = (($6 & 1) != 0); - n->initdeferred = (($6 & 2) != 0); + Constraint *n = makeNode(Constraint); + n->contype = CONSTR_DEFAULT; + n->name = NULL; + n->raw_expr = NULL; + n->cooked_expr = NULL; + n->keys = NULL; $$ = (Node *)n; } - | REFERENCES ColId opt_column_list - key_match key_actions + | DEFAULT b_expr + { + Constraint *n = makeNode(Constraint); + n->contype = CONSTR_DEFAULT; + n->name = NULL; + n->raw_expr = $2; + n->cooked_expr = NULL; + n->keys = NULL; + $$ = (Node *)n; + } + | REFERENCES ColId opt_column_list key_match key_actions { FkConstraint *n = makeNode(FkConstraint); n->constr_name = NULL; @@ -1305,34 +1184,49 @@ ColConstraintElemWithNull: CHECK '(' a_expr ')' n->pk_attrs = $3; n->match_type = $4; n->actions = $5; - n->deferrable = true; + n->deferrable = false; n->initdeferred = false; $$ = (Node *)n; } ; -PrimaryKey: PRIMARY KEY +/* + * 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_PRIMARY; - n->name = NULL; - n->raw_expr = NULL; - n->cooked_expr = NULL; - n->keys = NULL; + n->contype = CONSTR_ATTR_DEFERRABLE; $$ = (Node *)n; } - ; - -NotNull: NOT NULL_P + | NOT DEFERRABLE { Constraint *n = makeNode(Constraint); - n->contype = CONSTR_NOTNULL; - n->name = NULL; - n->raw_expr = NULL; - n->cooked_expr = NULL; - n->keys = NULL; + 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; } + ; + /* ConstraintElem specifies constraint syntax which is not embedded into * a column definition. ColConstraintElem specifies the embedded form. @@ -1345,13 +1239,13 @@ TableConstraint: CONSTRAINT name ConstraintElem case T_Constraint: { Constraint *n = (Constraint *)$3; - if (n != NULL) n->name = $2; + n->name = $2; } break; case T_FkConstraint: { FkConstraint *n = (FkConstraint *)$3; - if (n != NULL) n->constr_name = $2; + n->constr_name = $2; } break; default: @@ -1382,14 +1276,18 @@ ConstraintElem: CHECK '(' a_expr ')' n->keys = $3; $$ = (Node *)n; } - | PrimaryKey '(' columnList ')' + | PRIMARY KEY '(' columnList ')' { - Constraint *n = (Constraint *)$1; - n->keys = $3; + Constraint *n = makeNode(Constraint); + n->contype = CONSTR_PRIMARY; + n->name = NULL; + n->raw_expr = NULL; + n->cooked_expr = NULL; + n->keys = $4; $$ = (Node *)n; } | FOREIGN KEY '(' columnList ')' REFERENCES ColId opt_column_list - key_match key_actions ConstraintAttribute + key_match key_actions ConstraintAttributeSpec { FkConstraint *n = makeNode(FkConstraint); n->constr_name = NULL; @@ -1402,20 +1300,6 @@ ConstraintElem: CHECK '(' a_expr ')' n->initdeferred = ($11 & 2) != 0; $$ = (Node *)n; } - | FOREIGN KEY '(' columnList ')' REFERENCES ColId opt_column_list - key_match key_actions - { - FkConstraint *n = makeNode(FkConstraint); - n->constr_name = NULL; - n->pktable_name = $7; - n->fk_attrs = $4; - n->pk_attrs = $8; - n->match_type = $9; - n->actions = $10; - n->deferrable = false; - n->initdeferred = false; - $$ = (Node *)n; - } ; key_match: MATCH FULL @@ -1645,7 +1529,7 @@ CreateTrigStmt: CREATE TRIGGER name TriggerActionTime TriggerEvents ON } | CREATE CONSTRAINT TRIGGER name AFTER TriggerEvents ON relation_name OptConstrFromTable - ConstraintAttribute + ConstraintAttributeSpec FOR EACH ROW EXECUTE PROCEDURE name '(' TriggerFuncArgs ')' { CreateTrigStmt *n = makeNode(CreateTrigStmt); @@ -1740,37 +1624,41 @@ OptConstrFromTable: /* Empty */ } ; -ConstraintAttribute: DeferrabilityClause +ConstraintAttributeSpec: ConstraintDeferrabilitySpec + { $$ = $1; } + | ConstraintDeferrabilitySpec ConstraintTimeSpec { - $$ = $1; + if ($1 == 0 && $2 != 0) + elog(ERROR, "INITIALLY DEFERRED constraint must be DEFERRABLE"); + $$ = $1 | $2; } - | TimeClause + | ConstraintTimeSpec { if ($1 != 0) $$ = 3; else $$ = 0; } - | DeferrabilityClause TimeClause - { - if ($1 == 0 && $2 != 0) - elog(ERROR, "INITIALLY DEFERRED constraint must be DEFERRABLE"); - $$ = $1 | $2; - } - | TimeClause DeferrabilityClause + | ConstraintTimeSpec ConstraintDeferrabilitySpec { if ($2 == 0 && $1 != 0) elog(ERROR, "INITIALLY DEFERRED constraint must be DEFERRABLE"); $$ = $1 | $2; } + | /* Empty */ + { $$ = 0; } ; -DeferrabilityClause: DEFERRABLE { $$ = 1; } - | NOT DEFERRABLE { $$ = 0; } +ConstraintDeferrabilitySpec: NOT DEFERRABLE + { $$ = 0; } + | DEFERRABLE + { $$ = 1; } ; -TimeClause: INITIALLY IMMEDIATE { $$ = 0; } - | INITIALLY DEFERRED { $$ = 2; } +ConstraintTimeSpec: INITIALLY IMMEDIATE + { $$ = 0; } + | INITIALLY DEFERRED + { $$ = 2; } ; @@ -3395,8 +3283,39 @@ SubSelect: SELECT opt_distinct target_list ; /* easy way to return two values. Can someone improve this? bjm */ -result: INTO OptTemp opt_table relation_name { $$ = lcons(makeInteger($2), (List *)$4); } - | /*EMPTY*/ { $$ = lcons(makeInteger(false), NIL); } +result: INTO OptTempTableName { $$ = $2; } + | /*EMPTY*/ { $$ = lcons(makeInteger(false), NIL); } + ; + +/* + * Redundancy here is needed to avoid shift/reduce conflicts, + * since TEMP is not a reserved word. See also OptTemp. + * + * The result is a cons cell (not a true list!) containing + * a boolean and a table name. + */ +OptTempTableName: TEMPORARY opt_table relation_name + { $$ = lcons(makeInteger(TRUE), (List *) $3); } + | TEMP opt_table relation_name + { $$ = lcons(makeInteger(TRUE), (List *) $3); } + | LOCAL TEMPORARY opt_table relation_name + { $$ = lcons(makeInteger(TRUE), (List *) $4); } + | LOCAL TEMP opt_table relation_name + { $$ = lcons(makeInteger(TRUE), (List *) $4); } + | GLOBAL TEMPORARY opt_table relation_name + { + elog(ERROR, "GLOBAL TEMPORARY TABLE is not currently supported"); + $$ = lcons(makeInteger(TRUE), (List *) $4); + } + | GLOBAL TEMP opt_table relation_name + { + elog(ERROR, "GLOBAL TEMPORARY TABLE is not currently supported"); + $$ = lcons(makeInteger(TRUE), (List *) $4); + } + | TABLE relation_name + { $$ = lcons(makeInteger(FALSE), (List *) $2); } + | relation_name + { $$ = lcons(makeInteger(FALSE), (List *) $1); } ; opt_table: TABLE { $$ = TRUE; } @@ -5274,7 +5193,6 @@ ColId: IDENT { $$ = $1; } | CREATEUSER { $$ = "createuser"; } | CYCLE { $$ = "cycle"; } | DATABASE { $$ = "database"; } - | DEFERRABLE { $$ = "deferrable"; } | DEFERRED { $$ = "deferred"; } | DELIMITERS { $$ = "delimiters"; } | DOUBLE { $$ = "double"; } @@ -5288,7 +5206,6 @@ ColId: IDENT { $$ = $1; } | INCREMENT { $$ = "increment"; } | INDEX { $$ = "index"; } | INHERITS { $$ = "inherits"; } - | INITIALLY { $$ = "initially"; } | INSENSITIVE { $$ = "insensitive"; } | INSTEAD { $$ = "instead"; } | INTERVAL { $$ = "interval"; } @@ -5335,6 +5252,8 @@ ColId: IDENT { $$ = $1; } | STDIN { $$ = "stdin"; } | STDOUT { $$ = "stdout"; } | SYSID { $$ = "sysid"; } + | TEMP { $$ = "temp"; } + | TEMPORARY { $$ = "temporary"; } | TIME { $$ = "time"; } | TIMESTAMP { $$ = "timestamp"; } | TIMEZONE_HOUR { $$ = "timezone_hour"; } @@ -5371,6 +5290,7 @@ ColLabel: ColId { $$ = $1; } | CURRENT_USER { $$ = "current_user"; } | DEC { $$ = "dec"; } | DECIMAL { $$ = "decimal"; } + | DEFERRABLE { $$ = "deferrable"; } | DO { $$ = "do"; } | ELSE { $$ = "else"; } | END_TRANS { $$ = "end"; } @@ -5381,6 +5301,7 @@ ColLabel: ColId { $$ = $1; } | FOREIGN { $$ = "foreign"; } | GLOBAL { $$ = "global"; } | GROUP { $$ = "group"; } + | INITIALLY { $$ = "initially"; } | LISTEN { $$ = "listen"; } | LOAD { $$ = "load"; } | LOCAL { $$ = "local"; } diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index d98398aadec..e90b42c01e8 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2000, PostgreSQL, Inc * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: parsenodes.h,v 1.100 2000/02/18 09:29:44 inoue Exp $ + * $Id: parsenodes.h,v 1.101 2000/03/01 05:18:18 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -152,32 +152,51 @@ typedef struct CreateStmt List *tableElts; /* column definitions (list of ColumnDef) */ List *inhRelnames; /* relations to inherit from (list of * T_String Values) */ - List *constraints; /* list of constraints (Constraint nodes) */ + List *constraints; /* constraints (list of Constraint and + * FkConstraint nodes) */ } CreateStmt; -typedef enum ConstrType /* types of constraints */ -{ - CONSTR_NULL, CONSTR_NOTNULL, CONSTR_DEFAULT, CONSTR_CHECK, - CONSTR_PRIMARY, CONSTR_UNIQUE -} ConstrType; - -/* +/* ---------- + * Definitions for plain (non-FOREIGN KEY) constraints in CreateStmt + * + * XXX probably these ought to be unified with FkConstraints at some point? + * * For constraints that use expressions (CONSTR_DEFAULT, CONSTR_CHECK) * we may have the expression in either "raw" form (an untransformed * parse tree) or "cooked" form (the nodeToString representation of * an executable expression tree), depending on how this Constraint * node was created (by parsing, or by inheritance from an existing * relation). We should never have both in the same node! + * + * Constraint attributes (DEFERRABLE etc) are initially represented as + * separate Constraint nodes for simplicity of parsing. analyze.c makes + * a pass through the constraints list to attach the info to the appropriate + * FkConstraint node (and, perhaps, someday to other kinds of constraints). + * ---------- */ +typedef enum ConstrType /* types of constraints */ +{ + CONSTR_NULL, /* not SQL92, but a lot of people expect it */ + CONSTR_NOTNULL, + CONSTR_DEFAULT, + CONSTR_CHECK, + CONSTR_PRIMARY, + CONSTR_UNIQUE, + CONSTR_ATTR_DEFERRABLE, /* attributes for previous constraint node */ + CONSTR_ATTR_NOT_DEFERRABLE, + CONSTR_ATTR_DEFERRED, + CONSTR_ATTR_IMMEDIATE +} ConstrType; + typedef struct Constraint { NodeTag type; ConstrType contype; - char *name; /* name */ - Node *raw_expr; /* untransformed parse tree */ - char *cooked_expr; /* nodeToString representation */ - List *keys; /* list of primary keys */ + char *name; /* name, or NULL if unnamed */ + Node *raw_expr; /* expr, as untransformed parse tree */ + char *cooked_expr; /* expr, as nodeToString representation */ + List *keys; /* list of primary keys (or unique columns) */ } Constraint; |