diff options
Diffstat (limited to 'src/backend/parser/analyze.c')
-rw-r--r-- | src/backend/parser/analyze.c | 429 |
1 files changed, 181 insertions, 248 deletions
diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c index 2330bf18d43..4a251b63de4 100644 --- a/src/backend/parser/analyze.c +++ b/src/backend/parser/analyze.c @@ -6,7 +6,7 @@ * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/backend/parser/analyze.c,v 1.298 2004/04/02 21:05:32 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/parser/analyze.c,v 1.299 2004/05/05 04:48:46 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -77,7 +77,7 @@ typedef struct RangeVar *relation; /* relation to create */ List *inhRelations; /* relations to inherit from */ bool hasoids; /* does relation have an OID column? */ - Oid relOid; /* OID of table, if ALTER TABLE case */ + bool isalter; /* true if altering existing table */ List *columns; /* ColumnDef items */ List *ckconstraints; /* CHECK constraints */ List *fkconstraints; /* FOREIGN KEY constraints */ @@ -131,18 +131,17 @@ static void transformIndexConstraints(ParseState *pstate, CreateStmtContext *cxt); static void transformFKConstraints(ParseState *pstate, CreateStmtContext *cxt, + bool skipValidation, bool isAddConstraint); static void applyColumnNames(List *dst, List *src); static List *getSetColTypes(ParseState *pstate, Node *node); static void transformForUpdate(Query *qry, List *forUpdate); static void transformConstraintAttrs(List *constraintList); static void transformColumnType(ParseState *pstate, ColumnDef *column); -static bool relationHasPrimaryKey(Oid relationOid); static void release_pstate_resources(ParseState *pstate); static FromExpr *makeFromExpr(List *fromlist, Node *quals); static bool check_parameter_resolution_walker(Node *node, check_parameter_resolution_context *context); -static char *makeObjectName(char *name1, char *name2, char *typename); /* @@ -346,7 +345,8 @@ transformStmt(ParseState *pstate, Node *parseTree, break; case T_AlterTableStmt: - result = transformAlterTableStmt(pstate, (AlterTableStmt *) parseTree, + result = transformAlterTableStmt(pstate, + (AlterTableStmt *) parseTree, extras_before, extras_after); break; @@ -733,8 +733,8 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt, * from the truncated characters. Currently it seems best to keep it simple, * so that the generated names are easily predictable by a person. */ -static char * -makeObjectName(char *name1, char *name2, char *typename) +char * +makeObjectName(const char *name1, const char *name2, const char *typename) { char *name; int overhead = 0; /* chars needed for type and underscores */ @@ -795,48 +795,6 @@ makeObjectName(char *name1, char *name2, char *typename) return name; } -static char * -CreateIndexName(char *table_name, char *column_name, - char *label, List *indices) -{ - int pass = 0; - char *iname = NULL; - List *ilist; - char typename[NAMEDATALEN]; - - /* - * The type name for makeObjectName is label, or labelN if that's - * necessary to prevent collisions among multiple indexes for the same - * table. Note there is no check for collisions with already-existing - * indexes, only among the indexes we're about to create now; this - * ought to be improved someday. - */ - strncpy(typename, label, sizeof(typename)); - - for (;;) - { - iname = makeObjectName(table_name, column_name, typename); - - foreach(ilist, indices) - { - IndexStmt *index = lfirst(ilist); - - if (index->idxname != NULL && - strcmp(iname, index->idxname) == 0) - break; - } - /* ran through entire list? then no name conflict found so done */ - if (ilist == NIL) - break; - - /* found a conflict, so try a new name component */ - pfree(iname); - snprintf(typename, sizeof(typename), "%s%d", label, ++pass); - } - - return iname; -} - /* * transformCreateStmt - * transforms the "create table" statement @@ -857,7 +815,7 @@ transformCreateStmt(ParseState *pstate, CreateStmt *stmt, cxt.stmtType = "CREATE TABLE"; cxt.relation = stmt->relation; cxt.inhRelations = stmt->inhRelations; - cxt.relOid = InvalidOid; + cxt.isalter = false; cxt.columns = NIL; cxt.ckconstraints = NIL; cxt.fkconstraints = NIL; @@ -914,7 +872,7 @@ transformCreateStmt(ParseState *pstate, CreateStmt *stmt, /* * Postprocess foreign-key constraints. */ - transformFKConstraints(pstate, &cxt, false); + transformFKConstraints(pstate, &cxt, true, false); /* * Output results. @@ -1326,24 +1284,23 @@ transformIndexConstraints(ParseState *pstate, CreateStmtContext *cxt) index->primary = (constraint->contype == CONSTR_PRIMARY); if (index->primary) { - /* In ALTER TABLE case, a primary index might already exist */ - if (cxt->pkey != NULL || - (OidIsValid(cxt->relOid) && - relationHasPrimaryKey(cxt->relOid))) + if (cxt->pkey != NULL) ereport(ERROR, (errcode(ERRCODE_INVALID_TABLE_DEFINITION), errmsg("multiple primary keys for table \"%s\" are not allowed", cxt->relation->relname))); cxt->pkey = index; + /* + * In ALTER TABLE case, a primary index might already exist, + * but DefineIndex will check for it. + */ } index->isconstraint = true; if (constraint->name != NULL) index->idxname = pstrdup(constraint->name); - else if (constraint->contype == CONSTR_PRIMARY) - index->idxname = makeObjectName(cxt->relation->relname, NULL, "pkey"); else - index->idxname = NULL; /* will set it later */ + index->idxname = NULL; /* DefineIndex will choose name */ index->relation = cxt->relation; index->accessMethod = DEFAULT_INDEX_TYPE; @@ -1431,25 +1388,14 @@ transformIndexConstraints(ParseState *pstate, CreateStmtContext *cxt) break; } } - else if (OidIsValid(cxt->relOid)) - { - /* ALTER TABLE case: does column already exist? */ - HeapTuple atttuple; - - atttuple = SearchSysCacheAttName(cxt->relOid, key); - if (HeapTupleIsValid(atttuple)) - { - found = true; - /* - * If it's not already NOT NULL, leave it to - * DefineIndex to fix later. - */ - ReleaseSysCache(atttuple); - } - } - - if (!found) + /* + * In the ALTER TABLE case, don't complain about index keys + * not created in the command; they may well exist already. + * DefineIndex will complain about them if not, and will also + * take care of marking them NOT NULL. + */ + if (!found && !cxt->isalter) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_COLUMN), errmsg("column \"%s\" named in key does not exist", @@ -1537,51 +1483,36 @@ transformIndexConstraints(ParseState *pstate, CreateStmtContext *cxt) indexlist = lnext(indexlist); } - - /* - * Finally, select unique names for all not-previously-named indices, - * and display NOTICE messages. - * - * XXX in ALTER TABLE case, we fail to consider name collisions against - * pre-existing indexes. - */ - foreach(indexlist, cxt->alist) - { - index = lfirst(indexlist); - - if (index->idxname == NULL && index->indexParams != NIL) - { - iparam = (IndexElem *) lfirst(index->indexParams); - /* we should never see an expression item here */ - Assert(iparam->expr == NULL); - index->idxname = CreateIndexName(cxt->relation->relname, - iparam->name, - "key", - cxt->alist); - } - if (index->idxname == NULL) /* should not happen */ - elog(ERROR, "failed to make implicit index name"); - - ereport(NOTICE, - (errmsg("%s / %s%s will create implicit index \"%s\" for table \"%s\"", - cxt->stmtType, - (strcmp(cxt->stmtType, "ALTER TABLE") == 0) ? "ADD " : "", - (index->primary ? "PRIMARY KEY" : "UNIQUE"), - index->idxname, cxt->relation->relname))); - } } static void transformFKConstraints(ParseState *pstate, CreateStmtContext *cxt, - bool isAddConstraint) + bool skipValidation, bool isAddConstraint) { + List *fkclist; + if (cxt->fkconstraints == NIL) return; /* - * For ALTER TABLE ADD CONSTRAINT, nothing to do. For CREATE TABLE or - * ALTER TABLE ADD COLUMN, gin up an ALTER TABLE ADD CONSTRAINT - * command to execute after the basic command is complete. + * If CREATE TABLE or adding a column with NULL default, we can safely + * skip validation of the constraint. + */ + if (skipValidation) + { + foreach(fkclist, cxt->fkconstraints) + { + FkConstraint *fkconstraint = (FkConstraint *) lfirst(fkclist); + + fkconstraint->skip_validation = true; + } + } + + /* + * For CREATE TABLE or ALTER TABLE ADD COLUMN, gin up an ALTER TABLE + * ADD CONSTRAINT command to execute after the basic command is complete. + * (If called from ADD CONSTRAINT, that routine will add the FK constraints + * to its own subcommand list.) * * Note: the ADD CONSTRAINT command must also execute after any index * creation commands. Thus, this should run after @@ -1591,22 +1522,22 @@ transformFKConstraints(ParseState *pstate, CreateStmtContext *cxt, if (!isAddConstraint) { AlterTableStmt *alterstmt = makeNode(AlterTableStmt); - List *fkclist; - alterstmt->subtype = 'c'; /* preprocessed add constraint */ alterstmt->relation = cxt->relation; - alterstmt->name = NULL; - alterstmt->def = (Node *) cxt->fkconstraints; + alterstmt->cmds = NIL; - /* Don't need to scan the table contents in this case */ foreach(fkclist, cxt->fkconstraints) { FkConstraint *fkconstraint = (FkConstraint *) lfirst(fkclist); + AlterTableCmd *altercmd = makeNode(AlterTableCmd); - fkconstraint->skip_validation = true; + altercmd->subtype = AT_ProcessedConstraint; + altercmd->name = NULL; + altercmd->def = (Node *) fkconstraint; + alterstmt->cmds = lappend(alterstmt->cmds, altercmd); } - cxt->alist = lappend(cxt->alist, (Node *) alterstmt); + cxt->alist = lappend(cxt->alist, alterstmt); } } @@ -2554,111 +2485,158 @@ static Query * transformAlterTableStmt(ParseState *pstate, AlterTableStmt *stmt, List **extras_before, List **extras_after) { - Relation rel; CreateStmtContext cxt; Query *qry; + List *lcmd, + *l; + List *newcmds = NIL; + bool skipValidation = true; + AlterTableCmd *newcmd; + + cxt.stmtType = "ALTER TABLE"; + cxt.relation = stmt->relation; + cxt.inhRelations = NIL; + cxt.isalter = true; + cxt.hasoids = false; /* need not be right */ + cxt.columns = NIL; + cxt.ckconstraints = NIL; + cxt.fkconstraints = NIL; + cxt.ixconstraints = NIL; + cxt.blist = NIL; + cxt.alist = NIL; + cxt.pkey = NULL; /* * The only subtypes that currently require parse transformation - * handling are 'A'dd column and Add 'C'onstraint. These largely + * handling are ADD COLUMN and ADD CONSTRAINT. These largely * re-use code from CREATE TABLE. - * - * If we need to do any parse transformation, get exclusive lock on the - * relation to make sure it won't change before we execute the - * command. */ - switch (stmt->subtype) + foreach(lcmd, stmt->cmds) { - case 'A': - rel = heap_openrv(stmt->relation, AccessExclusiveLock); - - cxt.stmtType = "ALTER TABLE"; - cxt.relation = stmt->relation; - cxt.inhRelations = NIL; - cxt.relOid = RelationGetRelid(rel); - cxt.hasoids = SearchSysCacheExists(ATTNUM, - ObjectIdGetDatum(cxt.relOid), - Int16GetDatum(ObjectIdAttributeNumber), - 0, 0); - cxt.columns = NIL; - cxt.ckconstraints = NIL; - cxt.fkconstraints = NIL; - cxt.ixconstraints = NIL; - cxt.blist = NIL; - cxt.alist = NIL; - cxt.pkey = NULL; - - Assert(IsA(stmt->def, ColumnDef)); - transformColumnDefinition(pstate, &cxt, - (ColumnDef *) stmt->def); - - transformIndexConstraints(pstate, &cxt); - transformFKConstraints(pstate, &cxt, false); - - ((ColumnDef *) stmt->def)->constraints = cxt.ckconstraints; - *extras_before = nconc(*extras_before, cxt.blist); - *extras_after = nconc(cxt.alist, *extras_after); - - heap_close(rel, NoLock); /* close rel, keep lock */ - break; + AlterTableCmd *cmd = (AlterTableCmd *) lfirst(lcmd); - case 'C': - rel = heap_openrv(stmt->relation, AccessExclusiveLock); - - cxt.stmtType = "ALTER TABLE"; - cxt.relation = stmt->relation; - cxt.inhRelations = NIL; - cxt.relOid = RelationGetRelid(rel); - cxt.hasoids = SearchSysCacheExists(ATTNUM, - ObjectIdGetDatum(cxt.relOid), - Int16GetDatum(ObjectIdAttributeNumber), - 0, 0); - cxt.columns = NIL; - cxt.ckconstraints = NIL; - cxt.fkconstraints = NIL; - cxt.ixconstraints = NIL; - cxt.blist = NIL; - cxt.alist = NIL; - cxt.pkey = NULL; - - if (IsA(stmt->def, Constraint)) - transformTableConstraint(pstate, &cxt, - (Constraint *) stmt->def); - else if (IsA(stmt->def, FkConstraint)) - cxt.fkconstraints = lappend(cxt.fkconstraints, stmt->def); - else - elog(ERROR, "unrecognized node type: %d", - (int) nodeTag(stmt->def)); + switch (cmd->subtype) + { + case AT_AddColumn: + { + ColumnDef *def = (ColumnDef *) cmd->def; - transformIndexConstraints(pstate, &cxt); - transformFKConstraints(pstate, &cxt, true); + Assert(IsA(cmd->def, ColumnDef)); + transformColumnDefinition(pstate, &cxt, + (ColumnDef *) cmd->def); - Assert(cxt.columns == NIL); - /* fkconstraints should be put into my own stmt in this case */ - stmt->def = (Node *) nconc(cxt.ckconstraints, cxt.fkconstraints); - *extras_before = nconc(*extras_before, cxt.blist); - *extras_after = nconc(cxt.alist, *extras_after); + /* + * If the column has a non-null default, we can't skip + * validation of foreign keys. + */ + if (((ColumnDef *) cmd->def)->raw_default != NULL) + skipValidation = false; - heap_close(rel, NoLock); /* close rel, keep lock */ - break; + newcmds = lappend(newcmds, cmd); - case 'c': + /* + * Convert an ADD COLUMN ... NOT NULL constraint to a separate + * command + */ + if (def->is_not_null) + { + /* Remove NOT NULL from AddColumn */ + def->is_not_null = false; + + /* Add as a separate AlterTableCmd */ + newcmd = makeNode(AlterTableCmd); + newcmd->subtype = AT_SetNotNull; + newcmd->name = pstrdup(def->colname); + newcmds = lappend(newcmds, newcmd); + } - /* - * Already-transformed ADD CONSTRAINT, so just make it look - * like the standard case. - */ - stmt->subtype = 'C'; - break; + /* + * All constraints are processed in other ways. + * Remove the original list + */ + def->constraints = NIL; - default: - break; + break; + } + case AT_AddConstraint: + /* The original AddConstraint cmd node doesn't go to newcmds */ + + if (IsA(cmd->def, Constraint)) + transformTableConstraint(pstate, &cxt, + (Constraint *) cmd->def); + else if (IsA(cmd->def, FkConstraint)) + { + cxt.fkconstraints = lappend(cxt.fkconstraints, cmd->def); + skipValidation = false; + } + else + elog(ERROR, "unrecognized node type: %d", + (int) nodeTag(cmd->def)); + break; + + case AT_ProcessedConstraint: + + /* + * Already-transformed ADD CONSTRAINT, so just make it look + * like the standard case. + */ + cmd->subtype = AT_AddConstraint; + newcmds = lappend(newcmds, cmd); + break; + + default: + newcmds = lappend(newcmds, cmd); + break; + } } + /* Postprocess index and FK constraints */ + transformIndexConstraints(pstate, &cxt); + + transformFKConstraints(pstate, &cxt, skipValidation, true); + + /* + * Push any index-creation commands into the ALTER, so that + * they can be scheduled nicely by tablecmds.c. + */ + foreach(l, cxt.alist) + { + Node *idxstmt = (Node *) lfirst(l); + + Assert(IsA(idxstmt, IndexStmt)); + newcmd = makeNode(AlterTableCmd); + newcmd->subtype = AT_AddIndex; + newcmd->def = idxstmt; + newcmds = lappend(newcmds, newcmd); + } + cxt.alist = NIL; + + /* Append any CHECK or FK constraints to the commands list */ + foreach(l, cxt.ckconstraints) + { + newcmd = makeNode(AlterTableCmd); + newcmd->subtype = AT_AddConstraint; + newcmd->def = (Node *) lfirst(l); + newcmds = lappend(newcmds, newcmd); + } + foreach(l, cxt.fkconstraints) + { + newcmd = makeNode(AlterTableCmd); + newcmd->subtype = AT_AddConstraint; + newcmd->def = (Node *) lfirst(l); + newcmds = lappend(newcmds, newcmd); + } + + /* Update statement's commands list */ + stmt->cmds = newcmds; + qry = makeNode(Query); qry->commandType = CMD_UTILITY; qry->utilityStmt = (Node *) stmt; + *extras_before = nconc(*extras_before, cxt.blist); + *extras_after = nconc(cxt.alist, *extras_after); + return qry; } @@ -2947,51 +2925,6 @@ transformForUpdate(Query *qry, List *forUpdate) /* - * relationHasPrimaryKey - - * - * See whether an existing relation has a primary key. - */ -static bool -relationHasPrimaryKey(Oid relationOid) -{ - bool result = false; - Relation rel; - List *indexoidlist, - *indexoidscan; - - rel = heap_open(relationOid, AccessShareLock); - - /* - * Get the list of index OIDs for the table from the relcache, and - * look up each one in the pg_index syscache until we find one marked - * primary key (hopefully there isn't more than one such). - */ - indexoidlist = RelationGetIndexList(rel); - - foreach(indexoidscan, indexoidlist) - { - Oid indexoid = lfirsto(indexoidscan); - HeapTuple indexTuple; - - indexTuple = SearchSysCache(INDEXRELID, - ObjectIdGetDatum(indexoid), - 0, 0, 0); - if (!HeapTupleIsValid(indexTuple)) /* should not happen */ - elog(ERROR, "cache lookup failed for index %u", indexoid); - result = ((Form_pg_index) GETSTRUCT(indexTuple))->indisprimary; - ReleaseSysCache(indexTuple); - if (result) - break; - } - - freeList(indexoidlist); - - heap_close(rel, AccessShareLock); - - return result; -} - -/* * Preprocess a list of column constraint clauses * to attach constraint attributes to their primary constraint nodes * and detect inconsistent/misplaced constraint attributes. |