📄 tablecmds.c
字号:
} values[Anum_pg_trigger_tgargs - 1] = PointerGetDatum(newtgargs); replaces[Anum_pg_trigger_tgargs - 1] = 'r'; tuple = heap_modifytuple(tuple, RelationGetDescr(tgrel), values, nulls, replaces); /* * Update pg_trigger and its indexes */ simple_heap_update(tgrel, &tuple->t_self, tuple); CatalogUpdateIndexes(tgrel, tuple); /* * Invalidate trigger's relation's relcache entry so that other * backends (and this one too!) are sent SI message to make them * rebuild relcache entries. (Ideally this should happen * automatically...) * * We can skip this for triggers on relid itself, since that relcache * flush will happen anyway due to the table or column rename. We * just need to catch the far ends of RI relationships. */ pg_trigger = (Form_pg_trigger) GETSTRUCT(tuple); if (pg_trigger->tgrelid != relid) CacheInvalidateRelcacheByRelid(pg_trigger->tgrelid); /* free up our scratch memory */ pfree(newtgargs); heap_freetuple(tuple); } systable_endscan(trigscan); heap_close(tgrel, RowExclusiveLock); /* * Increment cmd counter to make updates visible; this is needed in case * the same tuple has to be updated again by next pass (can happen in case * of a self-referential FK relationship). */ CommandCounterIncrement();}/* * AlterTable * Execute ALTER TABLE, which can be a list of subcommands * * ALTER TABLE is performed in three phases: * 1. Examine subcommands and perform pre-transformation checking. * 2. Update system catalogs. * 3. Scan table(s) to check new constraints, and optionally recopy * the data into new table(s). * Phase 3 is not performed unless one or more of the subcommands requires * it. The intention of this design is to allow multiple independent * updates of the table schema to be performed with only one pass over the * data. * * ATPrepCmd performs phase 1. A "work queue" entry is created for * each table to be affected (there may be multiple affected tables if the * commands traverse a table inheritance hierarchy). Also we do preliminary * validation of the subcommands, including parse transformation of those * expressions that need to be evaluated with respect to the old table * schema. * * ATRewriteCatalogs performs phase 2 for each affected table (note that * phases 2 and 3 do no explicit recursion, since phase 1 already did it). * Certain subcommands need to be performed before others to avoid * unnecessary conflicts; for example, DROP COLUMN should come before * ADD COLUMN. Therefore phase 1 divides the subcommands into multiple * lists, one for each logical "pass" of phase 2. * * ATRewriteTables performs phase 3 for those tables that need it. * * Thanks to the magic of MVCC, an error anywhere along the way rolls back * the whole operation; we don't have to do anything special to clean up. */voidAlterTable(AlterTableStmt *stmt){ ATController(relation_openrv(stmt->relation, AccessExclusiveLock), stmt->cmds, interpretInhOption(stmt->relation->inhOpt));}/* * AlterTableInternal * * ALTER TABLE with target specified by OID */voidAlterTableInternal(Oid relid, List *cmds, bool recurse){ ATController(relation_open(relid, AccessExclusiveLock), cmds, recurse);}static voidATController(Relation rel, List *cmds, bool recurse){ List *wqueue = NIL; ListCell *lcmd; /* Phase 1: preliminary examination of commands, create work queue */ foreach(lcmd, cmds) { AlterTableCmd *cmd = (AlterTableCmd *) lfirst(lcmd); ATPrepCmd(&wqueue, rel, cmd, recurse, false); } /* Close the relation, but keep lock until commit */ relation_close(rel, NoLock); /* Phase 2: update system catalogs */ ATRewriteCatalogs(&wqueue); /* Phase 3: scan/rewrite tables as needed */ ATRewriteTables(&wqueue);}/* * ATPrepCmd * * Traffic cop for ALTER TABLE Phase 1 operations, including simple * recursion and permission checks. * * Caller must have acquired AccessExclusiveLock on relation already. * This lock should be held until commit. */static voidATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd, bool recurse, bool recursing){ AlteredTableInfo *tab; int pass; /* Find or create work queue entry for this table */ tab = ATGetQueueEntry(wqueue, rel); /* * Copy the original subcommand for each table. This avoids conflicts * when different child tables need to make different parse * transformations (for example, the same column may have different column * numbers in different children). */ cmd = copyObject(cmd); /* * Do permissions checking, recursion to child tables if needed, and any * additional phase-1 processing needed. */ switch (cmd->subtype) { case AT_AddColumn: /* ADD COLUMN */ ATSimplePermissions(rel, false); /* Performs own recursion */ ATPrepAddColumn(wqueue, rel, recurse, cmd); pass = AT_PASS_ADD_COL; break; case AT_ColumnDefault: /* ALTER COLUMN DEFAULT */ /* * We allow defaults on views so that INSERT into a view can have * default-ish behavior. This works because the rewriter * substitutes default values into INSERTs before it expands * rules. */ ATSimplePermissions(rel, true); ATSimpleRecursion(wqueue, rel, cmd, recurse); /* No command-specific prep needed */ pass = AT_PASS_ADD_CONSTR; break; case AT_DropNotNull: /* ALTER COLUMN DROP NOT NULL */ ATSimplePermissions(rel, false); ATSimpleRecursion(wqueue, rel, cmd, recurse); /* No command-specific prep needed */ pass = AT_PASS_DROP; break; case AT_SetNotNull: /* ALTER COLUMN SET NOT NULL */ ATSimplePermissions(rel, false); ATSimpleRecursion(wqueue, rel, cmd, recurse); /* No command-specific prep needed */ pass = AT_PASS_ADD_CONSTR; break; case AT_SetStatistics: /* ALTER COLUMN STATISTICS */ ATSimpleRecursion(wqueue, rel, cmd, recurse); /* Performs own permission checks */ ATPrepSetStatistics(rel, cmd->name, cmd->def); pass = AT_PASS_COL_ATTRS; break; case AT_SetStorage: /* ALTER COLUMN STORAGE */ ATSimplePermissions(rel, false); ATSimpleRecursion(wqueue, rel, cmd, recurse); /* No command-specific prep needed */ pass = AT_PASS_COL_ATTRS; break; case AT_DropColumn: /* DROP COLUMN */ ATSimplePermissions(rel, false); /* Recursion occurs during execution phase */ /* No command-specific prep needed except saving recurse flag */ if (recurse) cmd->subtype = AT_DropColumnRecurse; pass = AT_PASS_DROP; break; case AT_AddIndex: /* ADD INDEX */ ATSimplePermissions(rel, false); /* This command never recurses */ /* No command-specific prep needed */ pass = AT_PASS_ADD_INDEX; break; case AT_AddConstraint: /* ADD CONSTRAINT */ ATSimplePermissions(rel, false); /* * Currently we recurse only for CHECK constraints, never for * foreign-key constraints. UNIQUE/PKEY constraints won't be seen * here. */ if (IsA(cmd->def, Constraint)) ATSimpleRecursion(wqueue, rel, cmd, recurse); /* No command-specific prep needed */ pass = AT_PASS_ADD_CONSTR; break; case AT_DropConstraint: /* DROP CONSTRAINT */ ATSimplePermissions(rel, false); /* Performs own recursion */ ATPrepDropConstraint(wqueue, rel, recurse, cmd); pass = AT_PASS_DROP; break; case AT_DropConstraintQuietly: /* DROP CONSTRAINT for child */ ATSimplePermissions(rel, false); ATSimpleRecursion(wqueue, rel, cmd, recurse); /* No command-specific prep needed */ pass = AT_PASS_DROP; break; case AT_AlterColumnType: /* ALTER COLUMN TYPE */ ATSimplePermissions(rel, false); /* Performs own recursion */ ATPrepAlterColumnType(wqueue, tab, rel, recurse, recursing, cmd); pass = AT_PASS_ALTER_TYPE; break; case AT_ToastTable: /* CREATE TOAST TABLE */ ATSimplePermissions(rel, false); /* This command never recurses */ /* No command-specific prep needed */ pass = AT_PASS_MISC; break; case AT_ChangeOwner: /* ALTER OWNER */ /* This command never recurses */ /* No command-specific prep needed */ pass = AT_PASS_MISC; break; case AT_ClusterOn: /* CLUSTER ON */ case AT_DropCluster: /* SET WITHOUT CLUSTER */ ATSimplePermissions(rel, false); /* These commands never recurse */ /* No command-specific prep needed */ pass = AT_PASS_MISC; break; case AT_DropOids: /* SET WITHOUT OIDS */ ATSimplePermissions(rel, false); /* Performs own recursion */ if (rel->rd_rel->relhasoids) { AlterTableCmd *dropCmd = makeNode(AlterTableCmd); dropCmd->subtype = AT_DropColumn; dropCmd->name = pstrdup("oid"); dropCmd->behavior = cmd->behavior; ATPrepCmd(wqueue, rel, dropCmd, recurse, false); } pass = AT_PASS_DROP; break; case AT_SetTableSpace: /* SET TABLESPACE */ /* This command never recurses */ ATPrepSetTableSpace(tab, rel, cmd->name); pass = AT_PASS_MISC; /* doesn't actually matter */ break; case AT_EnableTrig: /* ENABLE TRIGGER variants */ case AT_EnableTrigAll: case AT_EnableTrigUser: case AT_DisableTrig: /* DISABLE TRIGGER variants */ case AT_DisableTrigAll: case AT_DisableTrigUser: ATSimplePermissions(rel, false); /* These commands never recurse */ /* No command-specific prep needed */ pass = AT_PASS_MISC; break; default: /* oops */ elog(ERROR, "unrecognized alter table type: %d", (int) cmd->subtype); pass = 0; /* keep compiler quiet */ break; } /* Add the subcommand to the appropriate list for phase 2 */ tab->subcmds[pass] = lappend(tab->subcmds[pass], cmd);}/* * ATRewriteCatalogs * * Traffic cop for ALTER TABLE Phase 2 operations. Subcommands are * dispatched in a "safe" execution order (designed to avoid unnecessary * conflicts). */static voidATRewriteCatalogs(List **wqueue){ int pass; ListCell *ltab; /* * We process all the tables "in parallel", one pass at a time. This is * needed because we may have to propagate work from one table to another * (specifically, ALTER TYPE on a foreign key's PK has to dispatch the * re-adding of the foreign key constraint to the other table). Work can * only be propagated into later passes, however. */ for (pass = 0; pass < AT_NUM_PASSES; pass++) { /* Go through each table that needs to be processed */ foreach(ltab, *wqueue) { AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab); List *subcmds = tab->subcmds[pass]; Relation rel; ListCell *lcmd; if (subcmds == NIL) continue; /* * Exclusive lock was obtained by phase 1, needn't get it again */ rel = relation_open(tab->relid, NoLock); foreach(lcmd, subcmds) ATExecCmd(tab, rel, (AlterTableCmd *) lfirst(lcmd)); /* * After the ALTER TYPE pass, do cleanup work (this is not done in * ATExecAlterColumnType since it should be done only once if * multiple columns of a table are altered). */ if (pass == AT_PASS_ALTER_TYPE) ATPostAlterTypeCleanup(wqueue, tab); relation_close(rel, NoLock); } } /* * Do an implicit CREATE TOAST TABLE if we executed any subcommands that * might have added a column or changed column storage. */ foreach(ltab, *wqueue) { AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab); if (tab->relkind == RELKIND_RELATION && (tab->subcmds[AT_PASS_ADD_COL] || tab->subcmds[AT_PASS_ALTER_TYPE] || tab->subcmds[AT_PASS_COL_ATTRS])) AlterTableCreateToastTable(tab->relid, true); }}/* * ATExecCmd: dispatch a subcommand to appropriate execution routine */static voidATExecCmd(AlteredTableInfo *tab, Relation rel, AlterTableCmd *cmd){ switch (cmd->subtype) { case AT_AddColumn: /* ADD COLUMN */ ATExecAddColumn(tab, rel, (ColumnDef *) cmd->def); break; case AT_ColumnDefault: /* ALTER COLUMN DEFAULT */ ATExecColumnDefault(rel, cmd->name, cmd->def); break; case AT_DropNotNull: /* ALTER COLUMN DROP NOT NULL */ ATExecDropNotNull(rel, cmd->name); break; case AT_SetNotNull: /* ALTER COLUMN SET NOT NULL */ ATExecSetNotNull(tab, rel, cmd->name); break; case AT_SetStatistics: /* ALTER COLUMN STATISTICS */ ATExecSetStatistics(rel, cmd->name, cmd->def); break; case AT_SetStorage: /* ALTER COLUMN STORAGE */ ATExecSetStorage(rel, cmd->name, cmd->def); break; case AT_DropColumn: /* DROP COLUMN */ ATExecDropColumn(rel, cmd->name, cmd->behavior, false, false); break; case AT_DropColumnRecurse: /* DROP COLUMN with recursion */ ATExecDropColumn(rel, cmd->name, cmd->behavior, true, false); break; case AT_AddIndex: /* ADD INDEX */ ATExecAddIndex(tab, rel, (IndexStmt *) cmd->def, false); break; case AT_ReAddIndex: /* ADD INDEX */ ATExecAddIndex(tab, rel, (IndexStmt *) cmd->def, true); break; case AT_AddConstraint: /* ADD CONSTRAINT */ ATExecAddConstraint(tab, rel, cmd->def); break; case AT_DropConstraint: /* DROP CONSTRAINT */ ATExecDropConstraint(rel, cmd->name, cmd->behavior, false); break; case AT_DropConstraintQuietly: /* DROP CONSTRAINT for child */ ATExecDropConstraint(rel, cmd->name, cmd->behavior, true); break; case AT_AlterColumnType: /* ALTER COLUMN TYPE */ ATExecAlterColumnType(tab, rel, cmd->name, (TypeName *) cmd->def); break; case AT_ToastTable: /* CREATE TOAST TABLE */ AlterTableCreateToastTable(RelationGetRelid(rel), false); break; case AT_ChangeOwner: /* ALTER OWNER */ ATExecChangeOwner(RelationGetRelid(rel), g
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -