ddl_lock_reduce.v9.patch
text/x-patch
Filename: ddl_lock_reduce.v9.patch
Type: text/x-patch
Part: 0
Patch
Same data as JSON:
GET /api/v1/attachments/:id/patch
the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes.
API reference →
Format: context
Series: patch v9
| File | + | − |
|---|---|---|
| doc/src/sgml/mvcc.sgml | 9 | 0 |
| src/backend/commands/tablecmds.c | 236 | 0 |
| src/backend/commands/trigger.c | 14 | 0 |
| src/backend/parser/parse_utilcmd.c | 15 | 0 |
| src/backend/rewrite/rewriteDefine.c | 7 | 0 |
| src/backend/rewrite/rewriteSupport.c | 7 | 0 |
| src/backend/utils/adt/ri_triggers.c | 1 | 0 |
| src/include/commands/tablecmds.h | 3 | 0 |
*** a/doc/src/sgml/mvcc.sgml
--- b/doc/src/sgml/mvcc.sgml
***************
*** 532,538 **** SELECT SUM(value) FROM mytab WHERE class = 2;
most <productname>PostgreSQL</productname> commands automatically
acquire locks of appropriate modes to ensure that referenced
tables are not dropped or modified in incompatible ways while the
! command executes. (For example, <command>ALTER TABLE</> cannot safely be
executed concurrently with other operations on the same table, so it
obtains an exclusive lock on the table to enforce that.)
</para>
--- 532,538 ----
most <productname>PostgreSQL</productname> commands automatically
acquire locks of appropriate modes to ensure that referenced
tables are not dropped or modified in incompatible ways while the
! command executes. (For example, <command>TRUNCATE</> cannot safely be
executed concurrently with other operations on the same table, so it
obtains an exclusive lock on the table to enforce that.)
</para>
***************
*** 695,702 **** SELECT SUM(value) FROM mytab WHERE class = 2;
</para>
<para>
! This lock mode is not automatically acquired by any
! <productname>PostgreSQL</productname> command.
</para>
</listitem>
</varlistentry>
--- 695,703 ----
</para>
<para>
! Acquired by <command>CREATE TRIGGER</command>,
! <command>CREATE RULE</command> (except for <literal>ON SELECT</>
! rules) and in some cases <command>ALTER TABLE</command>.
</para>
</listitem>
</varlistentry>
***************
*** 742,752 **** SELECT SUM(value) FROM mytab WHERE class = 2;
</para>
<para>
! Acquired by the <command>ALTER TABLE</command>, <command>DROP
! TABLE</command>, <command>TRUNCATE</command>, <command>REINDEX</command>,
<command>CLUSTER</command>, and <command>VACUUM FULL</command>
! commands. This is also the default lock mode for <command>LOCK
! TABLE</command> statements that do not specify a mode explicitly.
</para>
</listitem>
</varlistentry>
--- 743,754 ----
</para>
<para>
! Acquired by the <command>DROP TABLE</command>,
! <command>TRUNCATE</command>, <command>REINDEX</command>,
<command>CLUSTER</command>, and <command>VACUUM FULL</command>
! commands, as well as most variants of <command>ALTER TABLE</>.
! This is also the default lock mode for <command>LOCK TABLE</command>
! statements that do not specify a mode explicitly.
</para>
</listitem>
</varlistentry>
*** a/src/backend/commands/tablecmds.c
--- b/src/backend/commands/tablecmds.c
***************
*** 64,69 ****
--- 64,70 ----
#include "rewrite/rewriteHandler.h"
#include "storage/bufmgr.h"
#include "storage/lmgr.h"
+ #include "storage/lock.h"
#include "storage/smgr.h"
#include "utils/acl.h"
#include "utils/builtins.h"
***************
*** 253,273 **** static void validateForeignKeyConstraint(Constraint *fkconstraint,
Oid pkindOid, Oid constraintOid);
static void createForeignKeyTriggers(Relation rel, Constraint *fkconstraint,
Oid constraintOid, Oid indexOid);
! static void ATController(Relation rel, List *cmds, bool recurse);
static void ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
! bool recurse, bool recursing);
! static void ATRewriteCatalogs(List **wqueue);
static void ATExecCmd(List **wqueue, AlteredTableInfo *tab, Relation rel,
! AlterTableCmd *cmd);
static void ATRewriteTables(List **wqueue);
static void ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap);
static AlteredTableInfo *ATGetQueueEntry(List **wqueue, Relation rel);
static void ATSimplePermissions(Relation rel, bool allowView);
static void ATSimplePermissionsRelationOrIndex(Relation rel);
static void ATSimpleRecursion(List **wqueue, Relation rel,
! AlterTableCmd *cmd, bool recurse);
static void ATOneLevelRecursion(List **wqueue, Relation rel,
! AlterTableCmd *cmd);
static void ATPrepAddColumn(List **wqueue, Relation rel, bool recurse,
AlterTableCmd *cmd);
static void ATExecAddColumn(AlteredTableInfo *tab, Relation rel,
--- 254,274 ----
Oid pkindOid, Oid constraintOid);
static void createForeignKeyTriggers(Relation rel, Constraint *fkconstraint,
Oid constraintOid, Oid indexOid);
! static void ATController(Relation rel, List *cmds, bool recurse, LOCKMODE lockmode);
static void ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
! bool recurse, bool recursing, LOCKMODE lockmode);
! static void ATRewriteCatalogs(List **wqueue, LOCKMODE lockmode);
static void ATExecCmd(List **wqueue, AlteredTableInfo *tab, Relation rel,
! AlterTableCmd *cmd, LOCKMODE lockmode);
static void ATRewriteTables(List **wqueue);
static void ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap);
static AlteredTableInfo *ATGetQueueEntry(List **wqueue, Relation rel);
static void ATSimplePermissions(Relation rel, bool allowView);
static void ATSimplePermissionsRelationOrIndex(Relation rel);
static void ATSimpleRecursion(List **wqueue, Relation rel,
! AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode);
static void ATOneLevelRecursion(List **wqueue, Relation rel,
! AlterTableCmd *cmd, LOCKMODE lockmode);
static void ATPrepAddColumn(List **wqueue, Relation rel, bool recurse,
AlterTableCmd *cmd);
static void ATExecAddColumn(AlteredTableInfo *tab, Relation rel,
***************
*** 293,308 **** static void ATExecDropColumn(List **wqueue, Relation rel, const char *colName,
bool recurse, bool recursing,
bool missing_ok);
static void ATExecAddIndex(AlteredTableInfo *tab, Relation rel,
! IndexStmt *stmt, bool is_rebuild);
static void ATExecAddConstraint(List **wqueue,
AlteredTableInfo *tab, Relation rel,
! Constraint *newConstraint, bool recurse);
static void ATAddCheckConstraint(List **wqueue,
AlteredTableInfo *tab, Relation rel,
Constraint *constr,
! bool recurse, bool recursing);
static void ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
! Constraint *fkconstraint);
static void ATExecDropConstraint(Relation rel, const char *constrName,
DropBehavior behavior,
bool recurse, bool recursing,
--- 294,309 ----
bool recurse, bool recursing,
bool missing_ok);
static void ATExecAddIndex(AlteredTableInfo *tab, Relation rel,
! IndexStmt *stmt, bool is_rebuild, LOCKMODE lockmode);
static void ATExecAddConstraint(List **wqueue,
AlteredTableInfo *tab, Relation rel,
! Constraint *newConstraint, bool recurse, LOCKMODE lockmode);
static void ATAddCheckConstraint(List **wqueue,
AlteredTableInfo *tab, Relation rel,
Constraint *constr,
! bool recurse, bool recursing, LOCKMODE lockmode);
static void ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
! Constraint *fkconstraint, LOCKMODE lockmode);
static void ATExecDropConstraint(Relation rel, const char *constrName,
DropBehavior behavior,
bool recurse, bool recursing,
***************
*** 310,316 **** static void ATExecDropConstraint(Relation rel, const char *constrName,
static void ATPrepAlterColumnType(List **wqueue,
AlteredTableInfo *tab, Relation rel,
bool recurse, bool recursing,
! AlterTableCmd *cmd);
static void ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
const char *colName, TypeName *typeName);
static void ATPostAlterTypeCleanup(List **wqueue, AlteredTableInfo *tab);
--- 311,317 ----
static void ATPrepAlterColumnType(List **wqueue,
AlteredTableInfo *tab, Relation rel,
bool recurse, bool recursing,
! AlterTableCmd *cmd, LOCKMODE lockmode);
static void ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
const char *colName, TypeName *typeName);
static void ATPostAlterTypeCleanup(List **wqueue, AlteredTableInfo *tab);
***************
*** 2237,2244 **** RenameRelationInternal(Oid myrelid, const char *newrelname, Oid namespaceId)
* Disallow ALTER TABLE (and similar commands) when the current backend has
* any open reference to the target table besides the one just acquired by
* the calling command; this implies there's an open cursor or active plan.
! * We need this check because our AccessExclusiveLock doesn't protect us
! * against stomping on our own foot, only other people's feet!
*
* For ALTER TABLE, the only case known to cause serious trouble is ALTER
* COLUMN TYPE, and some changes are obviously pretty benign, so this could
--- 2238,2245 ----
* Disallow ALTER TABLE (and similar commands) when the current backend has
* any open reference to the target table besides the one just acquired by
* the calling command; this implies there's an open cursor or active plan.
! * We need this check because our lock doesn't protect us against stomping
! * on our own foot, only other people's feet!
*
* For ALTER TABLE, the only case known to cause serious trouble is ALTER
* COLUMN TYPE, and some changes are obviously pretty benign, so this could
***************
*** 2315,2325 **** CheckTableNotInUse(Relation rel, const char *stmt)
*
* 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.
*/
void
AlterTable(AlterTableStmt *stmt)
{
! Relation rel = relation_openrv(stmt->relation, AccessExclusiveLock);
CheckTableNotInUse(rel, "ALTER TABLE");
--- 2316,2342 ----
*
* 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.
+ *
+ * We lock the table as the first action, with an appropriate lock level
+ * for the subcommands requested. Any subcommand that needs to rewrite
+ * tuples in the table forces the whole command to be executed with
+ * AccessExclusiveLock. If all subcommands do not require rewrite table
+ * then we may be able to use lower lock levels. We pass the lock level down
+ * so that we can apply it recursively to inherited tables. Note that the
+ * lock level we want as we recurse may well be higher than required for
+ * that specific subcommand. So we pass down the overall lock requirement,
+ * rather than reassess it at lower levels.
*/
void
AlterTable(AlterTableStmt *stmt)
{
! Relation rel;
! LOCKMODE lockmode = AlterTableGreatestLockLevel(stmt->cmds);
!
! /*
! * Acquire same level of lock as already acquired during parsing.
! */
! rel = relation_openrv(stmt->relation, lockmode);
CheckTableNotInUse(rel, "ALTER TABLE");
***************
*** 2362,2368 **** AlterTable(AlterTableStmt *stmt)
elog(ERROR, "unrecognized object type: %d", (int) stmt->relkind);
}
! ATController(rel, stmt->cmds, interpretInhOption(stmt->relation->inhOpt));
}
/*
--- 2379,2386 ----
elog(ERROR, "unrecognized object type: %d", (int) stmt->relkind);
}
! ATController(rel, stmt->cmds, interpretInhOption(stmt->relation->inhOpt),
! lockmode);
}
/*
***************
*** 2379,2391 **** AlterTable(AlterTableStmt *stmt)
void
AlterTableInternal(Oid relid, List *cmds, bool recurse)
{
! Relation rel = relation_open(relid, AccessExclusiveLock);
! ATController(rel, cmds, recurse);
}
static void
! ATController(Relation rel, List *cmds, bool recurse)
{
List *wqueue = NIL;
ListCell *lcmd;
--- 2397,2559 ----
void
AlterTableInternal(Oid relid, List *cmds, bool recurse)
{
! Relation rel;
! LOCKMODE lockmode = AlterTableGreatestLockLevel(cmds);
!
! rel = relation_open(relid, lockmode);
!
! ATController(rel, cmds, recurse, lockmode);
! }
!
! /*
! * AlterTableGreatestLockLevel
! *
! * Sets the overall lock level required for the supplied list of subcommands.
! * Policy for doing this set according to needs of AlterTable(), see
! * comments there for overall explanation.
! *
! * Function is called before and after parsing, so it must give same
! * answer each time it is called. Some subcommands are transformed
! * into other subcommand types, so the transform must never be made to a
! * lower lock level than previously assigned. All transforms are noted below.
! *
! * Since this is called before we lock the table we cannot use table metadata
! * to influence the type of lock we acquire.
! */
! LOCKMODE
! AlterTableGreatestLockLevel(List *cmds)
! {
! ListCell *lcmd;
! LOCKMODE lockmode = ShareUpdateExclusiveLock; /* default for compiler */
!
! foreach(lcmd, cmds)
! {
! AlterTableCmd *cmd = (AlterTableCmd *) lfirst(lcmd);
! LOCKMODE cmd_lockmode = AccessExclusiveLock; /* default for compiler */
!
! switch (cmd->subtype)
! {
! /*
! * Need AccessExclusiveLock for these subcommands because they
! * affect or potentially affect both read and write operations.
! *
! * New subcommand types should be added here by default.
! */
! case AT_AddColumn: /* may rewrite heap, in some cases and visible to SELECT */
! case AT_DropColumn: /* change visible to SELECT */
! case AT_AddColumnToView: /* CREATE VIEW */
! case AT_AlterColumnType: /* must rewrite heap */
! case AT_DropConstraint: /* as DROP INDEX */
! case AT_AddOids:
! case AT_DropOids: /* calls AT_DropColumn */
! case AT_EnableAlwaysRule: /* as DROP INDEX */
! case AT_EnableReplicaRule: /* as DROP INDEX */
! case AT_EnableRule: /* as DROP INDEX */
! case AT_DisableRule: /* as DROP INDEX */
! case AT_ChangeOwner: /* change visible to SELECT */
! case AT_SetTableSpace: /* must rewrite heap */
! case AT_DropNotNull: /* may change some SQL plans */
! case AT_SetNotNull:
! cmd_lockmode = AccessExclusiveLock;
! break;
!
! /*
! * These subcommands affect write operations only.
! */
! case AT_ColumnDefault:
! case AT_ProcessedConstraint: /* becomes AT_AddConstraint */
! case AT_AddConstraintRecurse: /* becomes AT_AddConstraint */
! case AT_EnableTrig:
! case AT_EnableAlwaysTrig:
! case AT_EnableReplicaTrig:
! case AT_EnableTrigAll:
! case AT_EnableTrigUser:
! case AT_DisableTrig:
! case AT_DisableTrigAll:
! case AT_DisableTrigUser:
! case AT_AddIndex: /* from ADD CONSTRAINT */
! cmd_lockmode = ShareRowExclusiveLock;
! break;
!
! case AT_AddConstraint:
! if (IsA(cmd->def, Constraint))
! {
! Constraint *con = (Constraint *) cmd->def;
!
! switch (con->contype)
! {
! case CONSTR_EXCLUSION:
! case CONSTR_PRIMARY:
! case CONSTR_UNIQUE:
! /*
! * Cases essentially the same as CREATE INDEX. We
! * could reduce the lock strength to ShareLock if we
! * can work out how to allow concurrent catalog updates.
! */
! cmd_lockmode = ShareRowExclusiveLock;
! break;
! case CONSTR_FOREIGN:
! /*
! * We add triggers to both tables when we add a
! * Foreign Key, so the lock level must be at least
! * as strong as CREATE TRIGGER.
! */
! cmd_lockmode = ShareRowExclusiveLock;
! break;
!
! default:
! cmd_lockmode = ShareRowExclusiveLock;
! }
! }
! break;
!
! /*
! * These subcommands affect inheritance behaviour. Queries started before us
! * will continue to see the old inheritance behaviour, while queries started
! * after we commit will see new behaviour. No need to prevent reads or writes
! * to the subtable while we hook it up though. In both cases the parent table
! * is locked with AccessShareLock.
! */
! case AT_AddInherit:
! case AT_DropInherit:
! cmd_lockmode = ShareUpdateExclusiveLock;
! break;
!
! /*
! * These subcommands affect general strategies for performance and maintenance,
! * though don't change the semantic results from normal data reads and writes.
! * Delaying an ALTER TABLE behind currently active writes only delays the point
! * where the new strategy begins to take effect, so there is no benefit in waiting.
! * In thise case the minimum restriction applies: we don't currently allow
! * concurrent catalog updates.
! */
! case AT_SetStatistics:
! case AT_ClusterOn:
! case AT_DropCluster:
! case AT_SetRelOptions:
! case AT_ResetRelOptions:
! case AT_SetStorage:
! cmd_lockmode = ShareUpdateExclusiveLock;
! break;
!
! default: /* oops */
! elog(ERROR, "unrecognized alter table type: %d",
! (int) cmd->subtype);
! break;
! }
!
! /*
! * Take the greatest lockmode from any subcommand
! */
! if (cmd_lockmode > lockmode)
! lockmode = cmd_lockmode;
! }
! return lockmode;
}
static void
! ATController(Relation rel, List *cmds, bool recurse, LOCKMODE lockmode)
{
List *wqueue = NIL;
ListCell *lcmd;
***************
*** 2395,2408 **** ATController(Relation rel, List *cmds, bool recurse)
{
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);
--- 2563,2576 ----
{
AlterTableCmd *cmd = (AlterTableCmd *) lfirst(lcmd);
! ATPrepCmd(&wqueue, rel, cmd, recurse, false, lockmode);
}
/* Close the relation, but keep lock until commit */
relation_close(rel, NoLock);
/* Phase 2: update system catalogs */
! ATRewriteCatalogs(&wqueue, lockmode);
/* Phase 3: scan/rewrite tables as needed */
ATRewriteTables(&wqueue);
***************
*** 2414,2425 **** ATController(Relation rel, List *cmds, bool recurse)
* 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 void
ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
! bool recurse, bool recursing)
{
AlteredTableInfo *tab;
int pass;
--- 2582,2593 ----
* Traffic cop for ALTER TABLE Phase 1 operations, including simple
* recursion and permission checks.
*
! * Caller must have acquired appropriate lock type on relation already.
* This lock should be held until commit.
*/
static void
ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
! bool recurse, bool recursing, LOCKMODE lockmode)
{
AlteredTableInfo *tab;
int pass;
***************
*** 2463,2486 **** ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
* rules.
*/
ATSimplePermissions(rel, true);
! ATSimpleRecursion(wqueue, rel, cmd, recurse);
/* No command-specific prep needed */
pass = cmd->def ? AT_PASS_ADD_CONSTR : AT_PASS_DROP;
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 SET STATISTICS */
! ATSimpleRecursion(wqueue, rel, cmd, recurse);
/* Performs own permission checks */
ATPrepSetStatistics(rel, cmd->name, cmd->def);
pass = AT_PASS_COL_ATTRS;
--- 2631,2654 ----
* rules.
*/
ATSimplePermissions(rel, true);
! ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode);
/* No command-specific prep needed */
pass = cmd->def ? AT_PASS_ADD_CONSTR : AT_PASS_DROP;
break;
case AT_DropNotNull: /* ALTER COLUMN DROP NOT NULL */
ATSimplePermissions(rel, false);
! ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode);
/* 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, lockmode);
/* No command-specific prep needed */
pass = AT_PASS_ADD_CONSTR;
break;
case AT_SetStatistics: /* ALTER COLUMN SET STATISTICS */
! ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode);
/* Performs own permission checks */
ATPrepSetStatistics(rel, cmd->name, cmd->def);
pass = AT_PASS_COL_ATTRS;
***************
*** 2493,2499 **** ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
break;
case AT_SetStorage: /* ALTER COLUMN SET STORAGE */
ATSimplePermissions(rel, false);
! ATSimpleRecursion(wqueue, rel, cmd, recurse);
/* No command-specific prep needed */
pass = AT_PASS_COL_ATTRS;
break;
--- 2661,2667 ----
break;
case AT_SetStorage: /* ALTER COLUMN SET STORAGE */
ATSimplePermissions(rel, false);
! ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode);
/* No command-specific prep needed */
pass = AT_PASS_COL_ATTRS;
break;
***************
*** 2530,2536 **** ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
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_ChangeOwner: /* ALTER OWNER */
--- 2698,2704 ----
case AT_AlterColumnType: /* ALTER COLUMN TYPE */
ATSimplePermissions(rel, false);
/* Performs own recursion */
! ATPrepAlterColumnType(wqueue, tab, rel, recurse, recursing, cmd, lockmode);
pass = AT_PASS_ALTER_TYPE;
break;
case AT_ChangeOwner: /* ALTER OWNER */
***************
*** 2562,2568 **** ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
dropCmd->subtype = AT_DropColumn;
dropCmd->name = pstrdup("oid");
dropCmd->behavior = cmd->behavior;
! ATPrepCmd(wqueue, rel, dropCmd, recurse, false);
}
pass = AT_PASS_DROP;
break;
--- 2730,2736 ----
dropCmd->subtype = AT_DropColumn;
dropCmd->name = pstrdup("oid");
dropCmd->behavior = cmd->behavior;
! ATPrepCmd(wqueue, rel, dropCmd, recurse, false, lockmode);
}
pass = AT_PASS_DROP;
break;
***************
*** 2617,2623 **** ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
* conflicts).
*/
static void
! ATRewriteCatalogs(List **wqueue)
{
int pass;
ListCell *ltab;
--- 2785,2791 ----
* conflicts).
*/
static void
! ATRewriteCatalogs(List **wqueue, LOCKMODE lockmode)
{
int pass;
ListCell *ltab;
***************
*** 2643,2654 **** ATRewriteCatalogs(List **wqueue)
continue;
/*
! * Exclusive lock was obtained by phase 1, needn't get it again
*/
rel = relation_open(tab->relid, NoLock);
foreach(lcmd, subcmds)
! ATExecCmd(wqueue, tab, rel, (AlterTableCmd *) lfirst(lcmd));
/*
* After the ALTER TYPE pass, do cleanup work (this is not done in
--- 2811,2822 ----
continue;
/*
! * Appropriate lock was obtained by phase 1, needn't get it again
*/
rel = relation_open(tab->relid, NoLock);
foreach(lcmd, subcmds)
! ATExecCmd(wqueue, tab, rel, (AlterTableCmd *) lfirst(lcmd), lockmode);
/*
* After the ALTER TYPE pass, do cleanup work (this is not done in
***************
*** 2683,2689 **** ATRewriteCatalogs(List **wqueue)
*/
static void
ATExecCmd(List **wqueue, AlteredTableInfo *tab, Relation rel,
! AlterTableCmd *cmd)
{
switch (cmd->subtype)
{
--- 2851,2857 ----
*/
static void
ATExecCmd(List **wqueue, AlteredTableInfo *tab, Relation rel,
! AlterTableCmd *cmd, LOCKMODE lockmode)
{
switch (cmd->subtype)
{
***************
*** 2722,2739 **** ATExecCmd(List **wqueue, AlteredTableInfo *tab, Relation rel,
cmd->behavior, true, false, cmd->missing_ok);
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(wqueue, tab, rel, (Constraint *) cmd->def,
! false);
break;
case AT_AddConstraintRecurse: /* ADD CONSTRAINT with recursion */
ATExecAddConstraint(wqueue, tab, rel, (Constraint *) cmd->def,
! true);
break;
case AT_DropConstraint: /* DROP CONSTRAINT */
ATExecDropConstraint(rel, cmd->name, cmd->behavior,
--- 2890,2907 ----
cmd->behavior, true, false, cmd->missing_ok);
break;
case AT_AddIndex: /* ADD INDEX */
! ATExecAddIndex(tab, rel, (IndexStmt *) cmd->def, false, lockmode);
break;
case AT_ReAddIndex: /* ADD INDEX */
! ATExecAddIndex(tab, rel, (IndexStmt *) cmd->def, true, lockmode);
break;
case AT_AddConstraint: /* ADD CONSTRAINT */
ATExecAddConstraint(wqueue, tab, rel, (Constraint *) cmd->def,
! false, lockmode);
break;
case AT_AddConstraintRecurse: /* ADD CONSTRAINT with recursion */
ATExecAddConstraint(wqueue, tab, rel, (Constraint *) cmd->def,
! true, lockmode);
break;
case AT_DropConstraint: /* DROP CONSTRAINT */
ATExecDropConstraint(rel, cmd->name, cmd->behavior,
***************
*** 2971,2982 **** ATRewriteTables(List **wqueue)
Relation refrel;
if (rel == NULL)
- {
/* Long since locked, no need for another */
rel = heap_open(tab->relid, NoLock);
- }
! refrel = heap_open(con->refrelid, RowShareLock);
validateForeignKeyConstraint(fkconstraint, rel, refrel,
con->refindid,
--- 3139,3152 ----
Relation refrel;
if (rel == NULL)
/* Long since locked, no need for another */
rel = heap_open(tab->relid, NoLock);
! /*
! * We're adding a trigger to both tables, so the lock level
! * here should sensibly reflect that.
! */
! refrel = heap_open(con->refrelid, ShareRowExclusiveLock);
validateForeignKeyConstraint(fkconstraint, rel, refrel,
con->refindid,
***************
*** 3387,3393 **** ATSimplePermissionsRelationOrIndex(Relation rel)
*/
static void
ATSimpleRecursion(List **wqueue, Relation rel,
! AlterTableCmd *cmd, bool recurse)
{
/*
* Propagate to children if desired. Non-table relations never have
--- 3557,3563 ----
*/
static void
ATSimpleRecursion(List **wqueue, Relation rel,
! AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode)
{
/*
* Propagate to children if desired. Non-table relations never have
***************
*** 3399,3405 **** ATSimpleRecursion(List **wqueue, Relation rel,
ListCell *child;
List *children;
! children = find_all_inheritors(relid, AccessExclusiveLock, NULL);
/*
* find_all_inheritors does the recursive search of the inheritance
--- 3569,3575 ----
ListCell *child;
List *children;
! children = find_all_inheritors(relid, lockmode, NULL);
/*
* find_all_inheritors does the recursive search of the inheritance
***************
*** 3416,3422 **** ATSimpleRecursion(List **wqueue, Relation rel,
/* find_all_inheritors already got lock */
childrel = relation_open(childrelid, NoLock);
CheckTableNotInUse(childrel, "ALTER TABLE");
! ATPrepCmd(wqueue, childrel, cmd, false, true);
relation_close(childrel, NoLock);
}
}
--- 3586,3592 ----
/* find_all_inheritors already got lock */
childrel = relation_open(childrelid, NoLock);
CheckTableNotInUse(childrel, "ALTER TABLE");
! ATPrepCmd(wqueue, childrel, cmd, false, true, lockmode);
relation_close(childrel, NoLock);
}
}
***************
*** 3432,3444 **** ATSimpleRecursion(List **wqueue, Relation rel,
*/
static void
ATOneLevelRecursion(List **wqueue, Relation rel,
! AlterTableCmd *cmd)
{
Oid relid = RelationGetRelid(rel);
ListCell *child;
List *children;
! children = find_inheritance_children(relid, AccessExclusiveLock);
foreach(child, children)
{
--- 3602,3614 ----
*/
static void
ATOneLevelRecursion(List **wqueue, Relation rel,
! AlterTableCmd *cmd, LOCKMODE lockmode)
{
Oid relid = RelationGetRelid(rel);
ListCell *child;
List *children;
! children = find_inheritance_children(relid, lockmode);
foreach(child, children)
{
***************
*** 3448,3454 **** ATOneLevelRecursion(List **wqueue, Relation rel,
/* find_inheritance_children already got lock */
childrel = relation_open(childrelid, NoLock);
CheckTableNotInUse(childrel, "ALTER TABLE");
! ATPrepCmd(wqueue, childrel, cmd, true, true);
relation_close(childrel, NoLock);
}
}
--- 3618,3624 ----
/* find_inheritance_children already got lock */
childrel = relation_open(childrelid, NoLock);
CheckTableNotInUse(childrel, "ALTER TABLE");
! ATPrepCmd(wqueue, childrel, cmd, true, true, lockmode);
relation_close(childrel, NoLock);
}
}
***************
*** 3584,3590 **** ATPrepAddColumn(List **wqueue, Relation rel, bool recurse,
colDefChild->inhcount = 1;
colDefChild->is_local = false;
! ATOneLevelRecursion(wqueue, rel, childCmd);
}
else
{
--- 3754,3760 ----
colDefChild->inhcount = 1;
colDefChild->is_local = false;
! ATOneLevelRecursion(wqueue, rel, childCmd, AccessExclusiveLock);
}
else
{
***************
*** 4392,4399 **** ATExecDropColumn(List **wqueue, Relation rel, const char *colName,
* routines, we have to do this one level of recursion at a time; we can't
* use find_all_inheritors to do it in one pass.
*/
! children = find_inheritance_children(RelationGetRelid(rel),
! AccessExclusiveLock);
if (children)
{
--- 4562,4568 ----
* routines, we have to do this one level of recursion at a time; we can't
* use find_all_inheritors to do it in one pass.
*/
! children = find_inheritance_children(RelationGetRelid(rel), AccessExclusiveLock);
if (children)
{
***************
*** 4528,4534 **** ATExecDropColumn(List **wqueue, Relation rel, const char *colName,
*/
static void
ATExecAddIndex(AlteredTableInfo *tab, Relation rel,
! IndexStmt *stmt, bool is_rebuild)
{
bool check_rights;
bool skip_build;
--- 4697,4703 ----
*/
static void
ATExecAddIndex(AlteredTableInfo *tab, Relation rel,
! IndexStmt *stmt, bool is_rebuild, LOCKMODE lockmode)
{
bool check_rights;
bool skip_build;
***************
*** 4571,4577 **** ATExecAddIndex(AlteredTableInfo *tab, Relation rel,
*/
static void
ATExecAddConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
! Constraint *newConstraint, bool recurse)
{
Assert(IsA(newConstraint, Constraint));
--- 4740,4746 ----
*/
static void
ATExecAddConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
! Constraint *newConstraint, bool recurse, LOCKMODE lockmode)
{
Assert(IsA(newConstraint, Constraint));
***************
*** 4584,4590 **** ATExecAddConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
{
case CONSTR_CHECK:
ATAddCheckConstraint(wqueue, tab, rel,
! newConstraint, recurse, false);
break;
case CONSTR_FOREIGN:
--- 4753,4759 ----
{
case CONSTR_CHECK:
ATAddCheckConstraint(wqueue, tab, rel,
! newConstraint, recurse, false, lockmode);
break;
case CONSTR_FOREIGN:
***************
*** 4615,4621 **** ATExecAddConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
RelationGetNamespace(rel),
NIL);
! ATAddForeignKeyConstraint(tab, rel, newConstraint);
break;
default:
--- 4784,4790 ----
RelationGetNamespace(rel),
NIL);
! ATAddForeignKeyConstraint(tab, rel, newConstraint, lockmode);
break;
default:
***************
*** 4639,4645 **** ATExecAddConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
*/
static void
ATAddCheckConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
! Constraint *constr, bool recurse, bool recursing)
{
List *newcons;
ListCell *lcon;
--- 4808,4815 ----
*/
static void
ATAddCheckConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
! Constraint *constr, bool recurse, bool recursing,
! LOCKMODE lockmode)
{
List *newcons;
ListCell *lcon;
***************
*** 4694,4701 **** ATAddCheckConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
* routines, we have to do this one level of recursion at a time; we can't
* use find_all_inheritors to do it in one pass.
*/
! children = find_inheritance_children(RelationGetRelid(rel),
! AccessExclusiveLock);
/*
* If we are told not to recurse, there had better not be any child
--- 4864,4870 ----
* routines, we have to do this one level of recursion at a time; we can't
* use find_all_inheritors to do it in one pass.
*/
! children = find_inheritance_children(RelationGetRelid(rel), lockmode);
/*
* If we are told not to recurse, there had better not be any child
***************
*** 4721,4727 **** ATAddCheckConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
/* Recurse to child */
ATAddCheckConstraint(wqueue, childtab, childrel,
! constr, recurse, true);
heap_close(childrel, NoLock);
}
--- 4890,4896 ----
/* Recurse to child */
ATAddCheckConstraint(wqueue, childtab, childrel,
! constr, recurse, true, lockmode);
heap_close(childrel, NoLock);
}
***************
*** 4736,4742 **** ATAddCheckConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
*/
static void
ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
! Constraint *fkconstraint)
{
Relation pkrel;
int16 pkattnum[INDEX_MAX_KEYS];
--- 4905,4911 ----
*/
static void
ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
! Constraint *fkconstraint, LOCKMODE lockmode)
{
Relation pkrel;
int16 pkattnum[INDEX_MAX_KEYS];
***************
*** 4753,4766 **** ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
Oid indexOid;
Oid constrOid;
! /*
! * Grab an exclusive lock on the pk table, so that someone doesn't delete
! * rows out from under us. (Although a lesser lock would do for that
! * purpose, we'll need exclusive lock anyway to add triggers to the pk
! * table; trying to start with a lesser lock will just create a risk of
! * deadlock.)
! */
! pkrel = heap_openrv(fkconstraint->pktable, AccessExclusiveLock);
/*
* Validity checks (permission checks wait till we have the column
--- 4922,4928 ----
Oid indexOid;
Oid constrOid;
! pkrel = heap_openrv(fkconstraint->pktable, lockmode);
/*
* Validity checks (permission checks wait till we have the column
***************
*** 5324,5330 **** checkFkeyPermissions(Relation rel, int16 *attnums, int natts)
* Scan the existing rows in a table to verify they meet a proposed FK
* constraint.
*
! * Caller must have opened and locked both relations.
*/
static void
validateForeignKeyConstraint(Constraint *fkconstraint,
--- 5486,5492 ----
* Scan the existing rows in a table to verify they meet a proposed FK
* constraint.
*
! * Caller must have opened and locked both relations appropriately.
*/
static void
validateForeignKeyConstraint(Constraint *fkconstraint,
***************
*** 5663,5670 **** ATExecDropConstraint(Relation rel, const char *constrName,
* use find_all_inheritors to do it in one pass.
*/
if (is_check_constraint)
! children = find_inheritance_children(RelationGetRelid(rel),
! AccessExclusiveLock);
else
children = NIL;
--- 5825,5831 ----
* use find_all_inheritors to do it in one pass.
*/
if (is_check_constraint)
! children = find_inheritance_children(RelationGetRelid(rel), AccessExclusiveLock);
else
children = NIL;
***************
*** 5775,5781 **** static void
ATPrepAlterColumnType(List **wqueue,
AlteredTableInfo *tab, Relation rel,
bool recurse, bool recursing,
! AlterTableCmd *cmd)
{
char *colName = cmd->name;
TypeName *typeName = (TypeName *) cmd->def;
--- 5936,5942 ----
ATPrepAlterColumnType(List **wqueue,
AlteredTableInfo *tab, Relation rel,
bool recurse, bool recursing,
! AlterTableCmd *cmd, LOCKMODE lockmode)
{
char *colName = cmd->name;
TypeName *typeName = (TypeName *) cmd->def;
***************
*** 5897,5903 **** ATPrepAlterColumnType(List **wqueue,
* alter would put them out of step.
*/
if (recurse)
! ATSimpleRecursion(wqueue, rel, cmd, recurse);
else if (!recursing &&
find_inheritance_children(RelationGetRelid(rel), NoLock) != NIL)
ereport(ERROR,
--- 6058,6064 ----
* alter would put them out of step.
*/
if (recurse)
! ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode);
else if (!recursing &&
find_inheritance_children(RelationGetRelid(rel), NoLock) != NIL)
ereport(ERROR,
***************
*** 6304,6309 **** ATPostAlterTypeCleanup(List **wqueue, AlteredTableInfo *tab)
--- 6465,6474 ----
*/
}
+ /*
+ * Called to add additional work items if ALTER TYPE touched indexed
+ * columns. Hence we always take AccessExclusiveLocks here.
+ */
static void
ATPostAlterTypeParse(char *cmd, List **wqueue)
{
*** a/src/backend/commands/trigger.c
--- b/src/backend/commands/trigger.c
***************
*** 141,147 **** CreateTrigger(CreateTrigStmt *stmt, const char *queryString,
ObjectAddress myself,
referenced;
! rel = heap_openrv(stmt->relation, AccessExclusiveLock);
if (rel->rd_rel->relkind != RELKIND_RELATION)
ereport(ERROR,
--- 141,154 ----
ObjectAddress myself,
referenced;
! /*
! * ShareRowExclusiveLock is sufficient to prevent concurrent write activity
! * to the relation, and thus to lock out any operations that might want to
! * fire triggers on the relation. If we had ON SELECT triggers we would
! * need to take an AccessExclusiveLock to add one of those, just as we do
! * with ON SELECT rules.
! */
! rel = heap_openrv(stmt->relation, ShareRowExclusiveLock);
if (rel->rd_rel->relkind != RELKIND_RELATION)
ereport(ERROR,
***************
*** 417,423 **** CreateTrigger(CreateTrigStmt *stmt, const char *queryString,
* can skip this for internally generated triggers, since the name
* modification above should be sufficient.
*
! * NOTE that this is cool only because we have AccessExclusiveLock on the
* relation, so the trigger set won't be changing underneath us.
*/
if (!isInternal)
--- 424,430 ----
* can skip this for internally generated triggers, since the name
* modification above should be sufficient.
*
! * NOTE that this is cool only because we have ShareRowExclusiveLock on the
* relation, so the trigger set won't be changing underneath us.
*/
if (!isInternal)
***************
*** 1051,1061 **** RemoveTriggerById(Oid trigOid)
elog(ERROR, "could not find tuple for trigger %u", trigOid);
/*
! * Open and exclusive-lock the relation the trigger belongs to.
*/
relid = ((Form_pg_trigger) GETSTRUCT(tup))->tgrelid;
! rel = heap_open(relid, AccessExclusiveLock);
if (rel->rd_rel->relkind != RELKIND_RELATION)
ereport(ERROR,
--- 1058,1071 ----
elog(ERROR, "could not find tuple for trigger %u", trigOid);
/*
! * Open and lock the relation the trigger belongs to. As in
! * CreateTrigger, this is sufficient to lock out all operations that
! * could fire or add triggers; but it would need to be revisited if
! * we had ON SELECT triggers.
*/
relid = ((Form_pg_trigger) GETSTRUCT(tup))->tgrelid;
! rel = heap_open(relid, ShareRowExclusiveLock);
if (rel->rd_rel->relkind != RELKIND_RELATION)
ereport(ERROR,
*** a/src/backend/parser/parse_utilcmd.c
--- b/src/backend/parser/parse_utilcmd.c
***************
*** 53,58 ****
--- 53,59 ----
#include "parser/parse_utilcmd.h"
#include "parser/parser.h"
#include "rewrite/rewriteManip.h"
+ #include "storage/lock.h"
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/lsyscache.h"
***************
*** 1528,1534 **** transformFKConstraints(ParseState *pstate, CreateStmtContext *cxt,
}
/*
! * transformIndexStmt - parse analysis for CREATE INDEX
*
* Note: this is a no-op for an index not using either index expressions or
* a predicate expression. There are several code paths that create indexes
--- 1529,1535 ----
}
/*
! * transformIndexStmt - parse analysis for CREATE INDEX and ALTER TABLE
*
* Note: this is a no-op for an index not using either index expressions or
* a predicate expression. There are several code paths that create indexes
***************
*** 1554,1560 **** transformIndexStmt(IndexStmt *stmt, const char *queryString)
* because addRangeTableEntry() would acquire only AccessShareLock,
* leaving DefineIndex() needing to do a lock upgrade with consequent risk
* of deadlock. Make sure this stays in sync with the type of lock
! * DefineIndex() wants.
*/
rel = heap_openrv(stmt->relation,
(stmt->concurrent ? ShareUpdateExclusiveLock : ShareLock));
--- 1555,1562 ----
* because addRangeTableEntry() would acquire only AccessShareLock,
* leaving DefineIndex() needing to do a lock upgrade with consequent risk
* of deadlock. Make sure this stays in sync with the type of lock
! * DefineIndex() wants. If we are being called by ALTER TABLE, we will
! * already hold a higher lock.
*/
rel = heap_openrv(stmt->relation,
(stmt->concurrent ? ShareUpdateExclusiveLock : ShareLock));
***************
*** 1919,1924 **** transformAlterTableStmt(AlterTableStmt *stmt, const char *queryString)
--- 1921,1927 ----
List *newcmds = NIL;
bool skipValidation = true;
AlterTableCmd *newcmd;
+ LOCKMODE lockmode;
/*
* We must not scribble on the passed-in AlterTableStmt, so copy it. (This
***************
*** 1927,1939 **** transformAlterTableStmt(AlterTableStmt *stmt, const char *queryString)
stmt = (AlterTableStmt *) copyObject(stmt);
/*
! * Acquire exclusive lock on the target relation, which will be held until
* end of transaction. This ensures any decisions we make here based on
* the state of the relation will still be good at execution. We must get
! * exclusive lock now because execution will; taking a lower grade lock
! * now and trying to upgrade later risks deadlock.
*/
! rel = relation_openrv(stmt->relation, AccessExclusiveLock);
/* Set up pstate */
pstate = make_parsestate(NULL);
--- 1930,1948 ----
stmt = (AlterTableStmt *) copyObject(stmt);
/*
! * Assign the appropriate lock level for this list of subcommands.
! */
! lockmode = AlterTableGreatestLockLevel(stmt->cmds);
!
! /*
! * Acquire appropriate lock on the target relation, which will be held until
* end of transaction. This ensures any decisions we make here based on
* the state of the relation will still be good at execution. We must get
! * lock now because execution will later require it; taking a lower grade lock
! * now and trying to upgrade later risks deadlock. Any new commands we add
! * after this must not upgrade the lock level requested here.
*/
! rel = relation_openrv(stmt->relation, lockmode);
/* Set up pstate */
pstate = make_parsestate(NULL);
*** a/src/backend/rewrite/rewriteDefine.c
--- b/src/backend/rewrite/rewriteDefine.c
***************
*** 236,246 **** DefineQueryRewrite(char *rulename,
/*
* If we are installing an ON SELECT rule, we had better grab
* AccessExclusiveLock to ensure no SELECTs are currently running on the
! * event relation. For other types of rules, it might be sufficient to
! * grab ShareLock to lock out insert/update/delete actions. But for now,
! * let's just grab AccessExclusiveLock all the time.
*/
! event_relation = heap_open(event_relid, AccessExclusiveLock);
/*
* Verify relation is of a type that rules can sensibly be applied to.
--- 236,249 ----
/*
* If we are installing an ON SELECT rule, we had better grab
* AccessExclusiveLock to ensure no SELECTs are currently running on the
! * event relation. For other types of rules, it is sufficient to
! * grab ShareRowExclusiveLock to lock out insert/update/delete actions
! * and to ensure that we lock out current CREATE RULE statements.
*/
! if (event_type == CMD_SELECT)
! event_relation = heap_open(event_relid, AccessExclusiveLock);
! else
! event_relation = heap_open(event_relid, ShareRowExclusiveLock);
/*
* Verify relation is of a type that rules can sensibly be applied to.
*** a/src/backend/rewrite/rewriteSupport.c
--- b/src/backend/rewrite/rewriteSupport.c
***************
*** 70,81 **** SetRelationRuleStatus(Oid relationId, bool relHasRules,
/* Do the update */
classForm->relhasrules = relHasRules;
if (relIsBecomingView)
classForm->relkind = RELKIND_VIEW;
! simple_heap_update(relationRelation, &tuple->t_self, tuple);
! /* Keep the catalog indexes up to date */
! CatalogUpdateIndexes(relationRelation, tuple);
}
else
{
--- 70,85 ----
/* Do the update */
classForm->relhasrules = relHasRules;
if (relIsBecomingView)
+ {
classForm->relkind = RELKIND_VIEW;
! simple_heap_update(relationRelation, &tuple->t_self, tuple);
! /* Keep the catalog indexes up to date */
! CatalogUpdateIndexes(relationRelation, tuple);
! }
! else
! heap_inplace_update(relationRelation, tuple);
}
else
{
*** a/src/backend/utils/adt/ri_triggers.c
--- b/src/backend/utils/adt/ri_triggers.c
***************
*** 2606,2612 **** RI_FKey_keyequal_upd_fk(Trigger *trigger, Relation fk_rel,
* This is not a trigger procedure, but is called during ALTER TABLE
* ADD FOREIGN KEY to validate the initial table contents.
*
! * We expect that an exclusive lock has been taken on rel and pkrel;
* hence, we do not need to lock individual rows for the check.
*
* If the check fails because the current user doesn't have permissions
--- 2606,2612 ----
* This is not a trigger procedure, but is called during ALTER TABLE
* ADD FOREIGN KEY to validate the initial table contents.
*
! * We expect that a ShareRowExclusiveLock or higher has been taken on rel and pkrel;
* hence, we do not need to lock individual rows for the check.
*
* If the check fails because the current user doesn't have permissions
*** a/src/include/commands/tablecmds.h
--- b/src/include/commands/tablecmds.h
***************
*** 15,20 ****
--- 15,21 ----
#define TABLECMDS_H
#include "nodes/parsenodes.h"
+ #include "storage/lock.h"
#include "utils/relcache.h"
***************
*** 24,29 **** extern void RemoveRelations(DropStmt *drop);
--- 25,32 ----
extern void AlterTable(AlterTableStmt *stmt);
+ extern LOCKMODE AlterTableGreatestLockLevel(List *cmds);
+
extern void ATExecChangeOwner(Oid relationOid, Oid newOwnerId, bool recursing);
extern void AlterTableInternal(Oid relid, List *cmds, bool recurse);