aboutsummaryrefslogtreecommitdiff
path: root/src/backend/commands/tablecmds.c
diff options
context:
space:
mode:
authorSimon Riggs <simon@2ndQuadrant.com>2014-04-06 11:13:43 -0400
committerSimon Riggs <simon@2ndQuadrant.com>2014-04-06 11:13:43 -0400
commite5550d5fec66aa74caad1f79b79826ec64898688 (patch)
tree046444c974bf3aa9833545c0b9bbc183c37dbfa1 /src/backend/commands/tablecmds.c
parent80a5cf643adb496abe577a1ca6dc0c476d849c19 (diff)
downloadpostgresql-e5550d5fec66aa74caad1f79b79826ec64898688.tar.gz
postgresql-e5550d5fec66aa74caad1f79b79826ec64898688.zip
Reduce lock levels of some ALTER TABLE cmds
VALIDATE CONSTRAINT CLUSTER ON SET WITHOUT CLUSTER ALTER COLUMN SET STATISTICS ALTER COLUMN SET () ALTER COLUMN RESET () All other sub-commands use AccessExclusiveLock Simon Riggs and Noah Misch Reviews by Robert Haas and Andres Freund
Diffstat (limited to 'src/backend/commands/tablecmds.c')
-rw-r--r--src/backend/commands/tablecmds.c174
1 files changed, 109 insertions, 65 deletions
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 7f3f730a87b..8f9e5e56079 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -2685,10 +2685,8 @@ AlterTableLookupRelation(AlterTableStmt *stmt, LOCKMODE lockmode)
* the whole operation; we don't have to do anything special to clean up.
*
* The caller must lock the relation, 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 (actually, that is currently required always, but
- * we hope to relax it at some point). We pass the lock level down
+ * for the subcommands requested, using AlterTableGetLockLevel(stmt->cmds)
+ * or higher. 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 might well be higher than required for
* that specific subcommand. So we pass down the overall lock requirement,
@@ -2750,35 +2748,24 @@ AlterTableInternal(Oid relid, List *cmds, bool recurse)
* ALTER TABLE RENAME which is treated as a different statement type T_RenameStmt
* and does not travel through this section of code and cannot be combined with
* any of the subcommands given here.
+ *
+ * Note that Hot Standby only knows about AccessExclusiveLocks on the master
+ * so any changes that might affect SELECTs running on standbys need to use
+ * AccessExclusiveLocks even if you think a lesser lock would do, unless you
+ * have a solution for that also.
+ *
+ * Also note that pg_dump uses only an AccessShareLock, meaning that anything
+ * that takes a lock less than AccessExclusiveLock can change object definitions
+ * while pg_dump is running. Be careful to check that the appropriate data is
+ * derived by pg_dump using an MVCC snapshot, rather than syscache lookups,
+ * otherwise we might end up with an inconsistent dump that can't restore.
*/
LOCKMODE
AlterTableGetLockLevel(List *cmds)
{
/*
- * Late in 9.1 dev cycle a number of issues were uncovered with access to
- * catalog relations, leading to the decision to re-enforce all DDL at
- * AccessExclusiveLock level by default.
- *
- * The issues are that there is a pervasive assumption in the code that
- * the catalogs will not be read unless an AccessExclusiveLock is held. If
- * that rule is relaxed, we must protect against a number of potential
- * effects - infrequent, but proven possible with test cases where
- * multiple DDL operations occur in a stream against frequently accessed
- * tables.
- *
- * 1. Catalog tables were read using SnapshotNow, which has a race bug that
- * allows a scan to return no valid rows even when one is present in the
- * case of a commit of a concurrent update of the catalog table.
- * SnapshotNow also ignores transactions in progress, so takes the latest
- * committed version without waiting for the latest changes.
- *
- * 2. Relcache needs to be internally consistent, so unless we lock the
- * definition during reads we have no way to guarantee that.
- *
- * 3. Catcache access isn't coordinated at all so refreshes can occur at
- * any time.
+ * This only works if we read catalog tables using MVCC snapshots.
*/
-#ifdef REDUCED_ALTER_TABLE_LOCK_LEVELS
ListCell *lcmd;
LOCKMODE lockmode = ShareUpdateExclusiveLock;
@@ -2790,28 +2777,58 @@ AlterTableGetLockLevel(List *cmds)
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.
+ * These subcommands rewrite the heap, so require full locks.
*/
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_SetTableSpace: /* must rewrite heap */
case AT_AlterColumnType: /* must rewrite heap */
+ case AT_AddOids: /* must rewrite heap */
+ cmd_lockmode = AccessExclusiveLock;
+ break;
+
+ /*
+ * These subcommands may require addition of toast tables. If we
+ * add a toast table to a table currently being scanned, we
+ * might miss data added to the new toast table by concurrent
+ * insert transactions.
+ */
+ case AT_SetStorage: /* may add toast tables, see ATRewriteCatalogs() */
+ cmd_lockmode = AccessExclusiveLock;
+ break;
+
+ /*
+ * Removing constraints can affect SELECTs that have been
+ * optimised assuming the constraint holds true.
+ */
case AT_DropConstraint: /* as DROP INDEX */
- case AT_AddOids: /* must rewrite heap */
- case AT_DropOids: /* calls AT_DropColumn */
+ case AT_DropNotNull: /* may change some SQL plans */
+ cmd_lockmode = AccessExclusiveLock;
+ break;
+
+ /*
+ * Subcommands that may be visible to concurrent SELECTs
+ */
+ case AT_DropColumn: /* change visible to SELECT */
+ case AT_AddColumnToView: /* CREATE VIEW */
+ case AT_DropOids: /* calls AT_DropColumn */
case AT_EnableAlwaysRule: /* may change SELECT rules */
case AT_EnableReplicaRule: /* may change SELECT rules */
- case AT_EnableRule: /* may change SELECT rules */
+ case AT_EnableRule: /* may change SELECT rules */
case AT_DisableRule: /* may change SELECT rules */
+ cmd_lockmode = AccessExclusiveLock;
+ break;
+
+ /*
+ * Changing owner may remove implicit SELECT privileges
+ */
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;
+
+ /*
+ * Changing foreign table options may affect optimisation.
+ */
case AT_GenericOptions:
case AT_AlterColumnGenericOptions:
cmd_lockmode = AccessExclusiveLock;
@@ -2819,6 +2836,7 @@ AlterTableGetLockLevel(List *cmds)
/*
* These subcommands affect write operations only.
+ * XXX Theoretically, these could be ShareRowExclusiveLock.
*/
case AT_ColumnDefault:
case AT_ProcessedConstraint: /* becomes AT_AddConstraint */
@@ -2832,10 +2850,12 @@ AlterTableGetLockLevel(List *cmds)
case AT_DisableTrig:
case AT_DisableTrigAll:
case AT_DisableTrigUser:
+ case AT_AlterConstraint:
case AT_AddIndex: /* from ADD CONSTRAINT */
case AT_AddIndexConstraint:
case AT_ReplicaIdentity:
- cmd_lockmode = ShareRowExclusiveLock;
+ case AT_SetNotNull:
+ cmd_lockmode = AccessExclusiveLock;
break;
case AT_AddConstraint:
@@ -2854,8 +2874,10 @@ AlterTableGetLockLevel(List *cmds)
* could reduce the lock strength to ShareLock if
* we can work out how to allow concurrent catalog
* updates.
+ * XXX Might be set down to ShareRowExclusiveLock
+ * but requires further analysis.
*/
- cmd_lockmode = ShareRowExclusiveLock;
+ cmd_lockmode = AccessExclusiveLock;
break;
case CONSTR_FOREIGN:
@@ -2863,12 +2885,15 @@ AlterTableGetLockLevel(List *cmds)
* 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.
+ * XXX Might be set down to ShareRowExclusiveLock
+ * though trigger info is accessed by
+ * pg_get_triggerdef
*/
- cmd_lockmode = ShareRowExclusiveLock;
+ cmd_lockmode = AccessExclusiveLock;
break;
default:
- cmd_lockmode = ShareRowExclusiveLock;
+ cmd_lockmode = AccessExclusiveLock;
}
}
break;
@@ -2879,22 +2904,31 @@ AlterTableGetLockLevel(List *cmds)
* 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.
+ * Changing the TupDesc may be a problem, so keep highest lock.
*/
case AT_AddInherit:
case AT_DropInherit:
- cmd_lockmode = ShareUpdateExclusiveLock;
+ cmd_lockmode = AccessExclusiveLock;
break;
/*
* These subcommands affect implicit row type conversion. They
- * have affects similar to CREATE/DROP CAST on queries. We
+ * have affects similar to CREATE/DROP CAST on queries.
* don't provide for invalidating parse trees as a result of
- * such changes. Do avoid concurrent pg_class updates,
- * though.
+ * such changes, so we keep these at AccessExclusiveLock.
*/
case AT_AddOf:
case AT_DropOf:
- cmd_lockmode = ShareUpdateExclusiveLock;
+ cmd_lockmode = AccessExclusiveLock;
+ break;
+
+ /*
+ * Only used by CREATE OR REPLACE VIEW which must conflict
+ * with an SELECTs currently using the view.
+ */
+ case AT_ReplaceRelOptions:
+ cmd_lockmode = AccessExclusiveLock;
+ break;
/*
* These subcommands affect general strategies for performance
@@ -2906,20 +2940,33 @@ AlterTableGetLockLevel(List *cmds)
* 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_ReplaceRelOptions:
- case AT_SetOptions:
- case AT_ResetOptions:
- case AT_SetStorage:
- case AT_AlterConstraint:
- case AT_ValidateConstraint:
+ case AT_SetStatistics: /* Uses MVCC in getTableAttrs() */
+ case AT_ClusterOn: /* Uses MVCC in getIndexes() */
+ case AT_DropCluster: /* Uses MVCC in getIndexes() */
+ case AT_SetOptions: /* Uses MVCC in getTableAttrs() */
+ case AT_ResetOptions: /* Uses MVCC in getTableAttrs() */
cmd_lockmode = ShareUpdateExclusiveLock;
break;
+ case AT_ValidateConstraint: /* Uses MVCC in getConstraints() */
+ cmd_lockmode = ShareUpdateExclusiveLock;
+ break;
+
+ /*
+ * Rel options are more complex than first appears. Options
+ * are set here for tables, views and indexes; for historical
+ * reasons these can all be used with ALTER TABLE, so we
+ * can't decide between them using the basic grammar.
+ *
+ * XXX Look in detail at each option to determine lock level,
+ * e.g.
+ * cmd_lockmode = GetRelOptionsLockLevel((List *) cmd->def);
+ */
+ case AT_SetRelOptions: /* Uses MVCC in getIndexes() and getTables() */
+ case AT_ResetRelOptions: /* Uses MVCC in getIndexes() and getTables() */
+ cmd_lockmode = AccessExclusiveLock;
+ break;
+
default: /* oops */
elog(ERROR, "unrecognized alter table type: %d",
(int) cmd->subtype);
@@ -2932,9 +2979,6 @@ AlterTableGetLockLevel(List *cmds)
if (cmd_lockmode > lockmode)
lockmode = cmd_lockmode;
}
-#else
- LOCKMODE lockmode = AccessExclusiveLock;
-#endif
return lockmode;
}
@@ -3272,7 +3316,7 @@ ATRewriteCatalogs(List **wqueue, LOCKMODE lockmode)
if (tab->relkind == RELKIND_RELATION ||
tab->relkind == RELKIND_MATVIEW)
- AlterTableCreateToastTable(tab->relid, (Datum) 0);
+ AlterTableCreateToastTable(tab->relid, (Datum) 0, lockmode);
}
}
@@ -3586,7 +3630,7 @@ ATRewriteTables(List **wqueue, LOCKMODE lockmode)
/* Create transient table that will receive the modified data */
OIDNewHeap = make_new_heap(tab->relid, NewTableSpace, false,
- AccessExclusiveLock);
+ lockmode);
/*
* Copy the heap data into the new table with the desired