mirror of
https://github.com/postgres/postgres.git
synced 2025-06-05 23:56:58 +03:00
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
This commit is contained in:
parent
80a5cf643a
commit
e5550d5fec
@ -865,7 +865,9 @@ ERROR: could not serialize access due to read/write dependencies among transact
|
|||||||
<para>
|
<para>
|
||||||
Acquired by <command>VACUUM</command> (without <option>FULL</option>),
|
Acquired by <command>VACUUM</command> (without <option>FULL</option>),
|
||||||
<command>ANALYZE</>, <command>CREATE INDEX CONCURRENTLY</>, and
|
<command>ANALYZE</>, <command>CREATE INDEX CONCURRENTLY</>, and
|
||||||
some forms of <command>ALTER TABLE</command>.
|
<command>ALTER TABLE VALIDATE</command> and other
|
||||||
|
<command>ALTER TABLE</command> variants (for full details see
|
||||||
|
<xref linkend="SQL-ALTERTABLE">).
|
||||||
</para>
|
</para>
|
||||||
</listitem>
|
</listitem>
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
@ -951,10 +953,11 @@ ERROR: could not serialize access due to read/write dependencies among transact
|
|||||||
</para>
|
</para>
|
||||||
|
|
||||||
<para>
|
<para>
|
||||||
Acquired by the <command>ALTER TABLE</>, <command>DROP TABLE</>,
|
Acquired by the <command>DROP TABLE</>,
|
||||||
<command>TRUNCATE</command>, <command>REINDEX</command>,
|
<command>TRUNCATE</command>, <command>REINDEX</command>,
|
||||||
<command>CLUSTER</command>, and <command>VACUUM FULL</command>
|
<command>CLUSTER</command>, and <command>VACUUM FULL</command>
|
||||||
commands.
|
commands. Many forms of <command>ALTER TABLE</> also acquire
|
||||||
|
a lock at this level (see <xref linkend="SQL-ALTERTABLE">).
|
||||||
This is also the default lock mode for <command>LOCK TABLE</command>
|
This is also the default lock mode for <command>LOCK TABLE</command>
|
||||||
statements that do not specify a mode explicitly.
|
statements that do not specify a mode explicitly.
|
||||||
</para>
|
</para>
|
||||||
|
@ -84,7 +84,10 @@ ALTER TABLE [ IF EXISTS ] <replaceable class="PARAMETER">name</replaceable>
|
|||||||
|
|
||||||
<para>
|
<para>
|
||||||
<command>ALTER TABLE</command> changes the definition of an existing table.
|
<command>ALTER TABLE</command> changes the definition of an existing table.
|
||||||
There are several subforms:
|
There are several subforms described below. Note that the lock level required
|
||||||
|
may differ for each subform. An <literal>ACCESS EXCLUSIVE</literal> lock is held
|
||||||
|
unless explicitly noted. When multiple subcommands are listed, the lock
|
||||||
|
held will be the strictest one required from any subcommand.
|
||||||
|
|
||||||
<variablelist>
|
<variablelist>
|
||||||
<varlistentry>
|
<varlistentry>
|
||||||
@ -181,6 +184,9 @@ ALTER TABLE [ IF EXISTS ] <replaceable class="PARAMETER">name</replaceable>
|
|||||||
<productname>PostgreSQL</productname> query planner, refer to
|
<productname>PostgreSQL</productname> query planner, refer to
|
||||||
<xref linkend="planner-stats">.
|
<xref linkend="planner-stats">.
|
||||||
</para>
|
</para>
|
||||||
|
<para>
|
||||||
|
SET STATISTICS acquires a <literal>SHARE UPDATE EXCLUSIVE</literal> lock.
|
||||||
|
</para>
|
||||||
</listitem>
|
</listitem>
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
|
|
||||||
@ -213,6 +219,10 @@ ALTER TABLE [ IF EXISTS ] <replaceable class="PARAMETER">name</replaceable>
|
|||||||
of statistics by the <productname>PostgreSQL</productname> query
|
of statistics by the <productname>PostgreSQL</productname> query
|
||||||
planner, refer to <xref linkend="planner-stats">.
|
planner, refer to <xref linkend="planner-stats">.
|
||||||
</para>
|
</para>
|
||||||
|
<para>
|
||||||
|
Changing per-attribute options acquires a
|
||||||
|
<literal>SHARE UPDATE EXCLUSIVE</literal> lock.
|
||||||
|
</para>
|
||||||
</listitem>
|
</listitem>
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
|
|
||||||
@ -338,11 +348,17 @@ ALTER TABLE [ IF EXISTS ] <replaceable class="PARAMETER">name</replaceable>
|
|||||||
Nothing happens if the constraint is already marked valid.
|
Nothing happens if the constraint is already marked valid.
|
||||||
</para>
|
</para>
|
||||||
<para>
|
<para>
|
||||||
Validation can be a long process on larger tables and currently requires
|
Validation can be a long process on larger tables. The value of separating
|
||||||
an <literal>ACCESS EXCLUSIVE</literal> lock. The value of separating
|
|
||||||
validation from initial creation is that you can defer validation to less
|
validation from initial creation is that you can defer validation to less
|
||||||
busy times, or can be used to give additional time to correct pre-existing
|
busy times, or can be used to give additional time to correct pre-existing
|
||||||
errors while preventing new errors.
|
errors while preventing new errors. Note also that validation on its own
|
||||||
|
does not prevent normal write commands against the table while it runs.
|
||||||
|
</para>
|
||||||
|
<para>
|
||||||
|
Validation acquires only a <literal>SHARE UPDATE EXCLUSIVE</literal> lock
|
||||||
|
on the table being altered. If the constraint is a foreign key then
|
||||||
|
a <literal>ROW SHARE</literal> lock is also required on
|
||||||
|
the table referenced by the constraint.
|
||||||
</para>
|
</para>
|
||||||
</listitem>
|
</listitem>
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
@ -408,6 +424,9 @@ ALTER TABLE [ IF EXISTS ] <replaceable class="PARAMETER">name</replaceable>
|
|||||||
<xref linkend="SQL-CLUSTER">
|
<xref linkend="SQL-CLUSTER">
|
||||||
operations. It does not actually re-cluster the table.
|
operations. It does not actually re-cluster the table.
|
||||||
</para>
|
</para>
|
||||||
|
<para>
|
||||||
|
Changing cluster options acquires a <literal>SHARE UPDATE EXCLUSIVE</literal> lock.
|
||||||
|
</para>
|
||||||
</listitem>
|
</listitem>
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
|
|
||||||
@ -420,6 +439,9 @@ ALTER TABLE [ IF EXISTS ] <replaceable class="PARAMETER">name</replaceable>
|
|||||||
index specification from the table. This affects
|
index specification from the table. This affects
|
||||||
future cluster operations that don't specify an index.
|
future cluster operations that don't specify an index.
|
||||||
</para>
|
</para>
|
||||||
|
<para>
|
||||||
|
Changing cluster options acquires a <literal>SHARE UPDATE EXCLUSIVE</literal> lock.
|
||||||
|
</para>
|
||||||
</listitem>
|
</listitem>
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
|
|
||||||
@ -1078,6 +1100,14 @@ ALTER TABLE distributors ADD CONSTRAINT distfk FOREIGN KEY (address) REFERENCES
|
|||||||
</programlisting>
|
</programlisting>
|
||||||
</para>
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
To add a foreign key constraint to a table with the least impact on other work:
|
||||||
|
<programlisting>
|
||||||
|
ALTER TABLE distributors ADD CONSTRAINT distfk FOREIGN KEY (address) REFERENCES addresses (address) NOT VALID;
|
||||||
|
ALTER TABLE distributors VALIDATE CONSTRAINT distfk;
|
||||||
|
</programlisting>
|
||||||
|
</para>
|
||||||
|
|
||||||
<para>
|
<para>
|
||||||
To add a (multicolumn) unique constraint to a table:
|
To add a (multicolumn) unique constraint to a table:
|
||||||
<programlisting>
|
<programlisting>
|
||||||
|
@ -27,6 +27,7 @@
|
|||||||
#include "catalog/toasting.h"
|
#include "catalog/toasting.h"
|
||||||
#include "miscadmin.h"
|
#include "miscadmin.h"
|
||||||
#include "nodes/makefuncs.h"
|
#include "nodes/makefuncs.h"
|
||||||
|
#include "storage/lock.h"
|
||||||
#include "utils/builtins.h"
|
#include "utils/builtins.h"
|
||||||
#include "utils/rel.h"
|
#include "utils/rel.h"
|
||||||
#include "utils/syscache.h"
|
#include "utils/syscache.h"
|
||||||
@ -34,13 +35,15 @@
|
|||||||
/* Potentially set by contrib/pg_upgrade_support functions */
|
/* Potentially set by contrib/pg_upgrade_support functions */
|
||||||
Oid binary_upgrade_next_toast_pg_type_oid = InvalidOid;
|
Oid binary_upgrade_next_toast_pg_type_oid = InvalidOid;
|
||||||
|
|
||||||
|
static void CheckAndCreateToastTable(Oid relOid, Datum reloptions,
|
||||||
|
LOCKMODE lockmode, bool check);
|
||||||
static bool create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid,
|
static bool create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid,
|
||||||
Datum reloptions);
|
Datum reloptions, LOCKMODE lockmode, bool check);
|
||||||
static bool needs_toast_table(Relation rel);
|
static bool needs_toast_table(Relation rel);
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* AlterTableCreateToastTable
|
* CreateToastTable variants
|
||||||
* If the table needs a toast table, and doesn't already have one,
|
* If the table needs a toast table, and doesn't already have one,
|
||||||
* then create a toast table for it.
|
* then create a toast table for it.
|
||||||
*
|
*
|
||||||
@ -52,21 +55,32 @@ static bool needs_toast_table(Relation rel);
|
|||||||
* to end with CommandCounterIncrement if it makes any changes.
|
* to end with CommandCounterIncrement if it makes any changes.
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
AlterTableCreateToastTable(Oid relOid, Datum reloptions)
|
AlterTableCreateToastTable(Oid relOid, Datum reloptions, LOCKMODE lockmode)
|
||||||
|
{
|
||||||
|
CheckAndCreateToastTable(relOid, reloptions, lockmode, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
NewHeapCreateToastTable(Oid relOid, Datum reloptions, LOCKMODE lockmode)
|
||||||
|
{
|
||||||
|
CheckAndCreateToastTable(relOid, reloptions, lockmode, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
NewRelationCreateToastTable(Oid relOid, Datum reloptions)
|
||||||
|
{
|
||||||
|
CheckAndCreateToastTable(relOid, reloptions, AccessExclusiveLock, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
CheckAndCreateToastTable(Oid relOid, Datum reloptions, LOCKMODE lockmode, bool check)
|
||||||
{
|
{
|
||||||
Relation rel;
|
Relation rel;
|
||||||
|
|
||||||
/*
|
rel = heap_open(relOid, lockmode);
|
||||||
* Grab an exclusive lock on the target table, since we'll update its
|
|
||||||
* pg_class tuple. This is redundant for all present uses, since caller
|
|
||||||
* will have such a lock already. But the lock is needed to ensure that
|
|
||||||
* concurrent readers of the pg_class tuple won't have visibility issues,
|
|
||||||
* so let's be safe.
|
|
||||||
*/
|
|
||||||
rel = heap_open(relOid, AccessExclusiveLock);
|
|
||||||
|
|
||||||
/* create_toast_table does all the work */
|
/* create_toast_table does all the work */
|
||||||
(void) create_toast_table(rel, InvalidOid, InvalidOid, reloptions);
|
(void) create_toast_table(rel, InvalidOid, InvalidOid, reloptions, lockmode, check);
|
||||||
|
|
||||||
heap_close(rel, NoLock);
|
heap_close(rel, NoLock);
|
||||||
}
|
}
|
||||||
@ -91,7 +105,8 @@ BootstrapToastTable(char *relName, Oid toastOid, Oid toastIndexOid)
|
|||||||
relName)));
|
relName)));
|
||||||
|
|
||||||
/* create_toast_table does all the work */
|
/* create_toast_table does all the work */
|
||||||
if (!create_toast_table(rel, toastOid, toastIndexOid, (Datum) 0))
|
if (!create_toast_table(rel, toastOid, toastIndexOid, (Datum) 0,
|
||||||
|
AccessExclusiveLock, false))
|
||||||
elog(ERROR, "\"%s\" does not require a toast table",
|
elog(ERROR, "\"%s\" does not require a toast table",
|
||||||
relName);
|
relName);
|
||||||
|
|
||||||
@ -107,7 +122,8 @@ BootstrapToastTable(char *relName, Oid toastOid, Oid toastIndexOid)
|
|||||||
* bootstrap they can be nonzero to specify hand-assigned OIDs
|
* bootstrap they can be nonzero to specify hand-assigned OIDs
|
||||||
*/
|
*/
|
||||||
static bool
|
static bool
|
||||||
create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid, Datum reloptions)
|
create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid,
|
||||||
|
Datum reloptions, LOCKMODE lockmode, bool check)
|
||||||
{
|
{
|
||||||
Oid relOid = RelationGetRelid(rel);
|
Oid relOid = RelationGetRelid(rel);
|
||||||
HeapTuple reltup;
|
HeapTuple reltup;
|
||||||
@ -160,6 +176,13 @@ create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid, Datum reloptio
|
|||||||
!OidIsValid(binary_upgrade_next_toast_pg_class_oid)))
|
!OidIsValid(binary_upgrade_next_toast_pg_class_oid)))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If requested check lockmode is sufficient. This is a cross check
|
||||||
|
* in case of errors or conflicting decisions in earlier code.
|
||||||
|
*/
|
||||||
|
if (check && lockmode != AccessExclusiveLock)
|
||||||
|
elog(ERROR, "AccessExclusiveLock required to add toast table.");
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Create the toast table and its index
|
* Create the toast table and its index
|
||||||
*/
|
*/
|
||||||
|
@ -408,10 +408,10 @@ cluster_rel(Oid tableOid, Oid indexOid, bool recheck, bool verbose)
|
|||||||
/*
|
/*
|
||||||
* Verify that the specified heap and index are valid to cluster on
|
* Verify that the specified heap and index are valid to cluster on
|
||||||
*
|
*
|
||||||
* Side effect: obtains exclusive lock on the index. The caller should
|
* Side effect: obtains lock on the index. The caller may
|
||||||
* already have exclusive lock on the table, so the index lock is likely
|
* in some cases already have AccessExclusiveLock on the table, but
|
||||||
* redundant, but it seems best to grab it anyway to ensure the index
|
* not in all cases so we can't rely on the table-level lock for
|
||||||
* definition can't change under us.
|
* protection here.
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
check_index_is_clusterable(Relation OldHeap, Oid indexOid, bool recheck, LOCKMODE lockmode)
|
check_index_is_clusterable(Relation OldHeap, Oid indexOid, bool recheck, LOCKMODE lockmode)
|
||||||
@ -696,10 +696,10 @@ make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, bool forcetemp,
|
|||||||
*
|
*
|
||||||
* If the relation doesn't have a TOAST table already, we can't need one
|
* If the relation doesn't have a TOAST table already, we can't need one
|
||||||
* for the new relation. The other way around is possible though: if some
|
* for the new relation. The other way around is possible though: if some
|
||||||
* wide columns have been dropped, AlterTableCreateToastTable can decide
|
* wide columns have been dropped, NewHeapCreateToastTable can decide
|
||||||
* that no TOAST table is needed for the new table.
|
* that no TOAST table is needed for the new table.
|
||||||
*
|
*
|
||||||
* Note that AlterTableCreateToastTable ends with CommandCounterIncrement,
|
* Note that NewHeapCreateToastTable ends with CommandCounterIncrement,
|
||||||
* so that the TOAST table will be visible for insertion.
|
* so that the TOAST table will be visible for insertion.
|
||||||
*/
|
*/
|
||||||
toastid = OldHeap->rd_rel->reltoastrelid;
|
toastid = OldHeap->rd_rel->reltoastrelid;
|
||||||
@ -714,7 +714,7 @@ make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, bool forcetemp,
|
|||||||
if (isNull)
|
if (isNull)
|
||||||
reloptions = (Datum) 0;
|
reloptions = (Datum) 0;
|
||||||
|
|
||||||
AlterTableCreateToastTable(OIDNewHeap, reloptions);
|
NewHeapCreateToastTable(OIDNewHeap, reloptions, lockmode);
|
||||||
|
|
||||||
ReleaseSysCache(tuple);
|
ReleaseSysCache(tuple);
|
||||||
}
|
}
|
||||||
|
@ -359,7 +359,7 @@ intorel_startup(DestReceiver *self, int operation, TupleDesc typeinfo)
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* If necessary, create a TOAST table for the target table. Note that
|
* If necessary, create a TOAST table for the target table. Note that
|
||||||
* AlterTableCreateToastTable ends with CommandCounterIncrement(), so that
|
* NewRelationCreateToastTable ends with CommandCounterIncrement(), so that
|
||||||
* the TOAST table will be visible for insertion.
|
* the TOAST table will be visible for insertion.
|
||||||
*/
|
*/
|
||||||
CommandCounterIncrement();
|
CommandCounterIncrement();
|
||||||
@ -373,7 +373,7 @@ intorel_startup(DestReceiver *self, int operation, TupleDesc typeinfo)
|
|||||||
|
|
||||||
(void) heap_reloptions(RELKIND_TOASTVALUE, toast_options, true);
|
(void) heap_reloptions(RELKIND_TOASTVALUE, toast_options, true);
|
||||||
|
|
||||||
AlterTableCreateToastTable(intoRelationId, toast_options);
|
NewRelationCreateToastTable(intoRelationId, toast_options);
|
||||||
|
|
||||||
/* Create the "view" part of a materialized view. */
|
/* Create the "view" part of a materialized view. */
|
||||||
if (is_matview)
|
if (is_matview)
|
||||||
|
@ -2685,10 +2685,8 @@ AlterTableLookupRelation(AlterTableStmt *stmt, LOCKMODE lockmode)
|
|||||||
* the whole operation; we don't have to do anything special to clean up.
|
* 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
|
* The caller must lock the relation, with an appropriate lock level
|
||||||
* for the subcommands requested. Any subcommand that needs to rewrite
|
* for the subcommands requested, using AlterTableGetLockLevel(stmt->cmds)
|
||||||
* tuples in the table forces the whole command to be executed with
|
* or higher. We pass the lock level down
|
||||||
* AccessExclusiveLock (actually, that is currently required always, but
|
|
||||||
* we hope to relax it at some point). We pass the lock level down
|
|
||||||
* so that we can apply it recursively to inherited tables. Note that the
|
* 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
|
* 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,
|
* 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
|
* 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
|
* and does not travel through this section of code and cannot be combined with
|
||||||
* any of the subcommands given here.
|
* 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
|
LOCKMODE
|
||||||
AlterTableGetLockLevel(List *cmds)
|
AlterTableGetLockLevel(List *cmds)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
* Late in 9.1 dev cycle a number of issues were uncovered with access to
|
* This only works if we read catalog tables using MVCC snapshots.
|
||||||
* 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.
|
|
||||||
*/
|
*/
|
||||||
#ifdef REDUCED_ALTER_TABLE_LOCK_LEVELS
|
|
||||||
ListCell *lcmd;
|
ListCell *lcmd;
|
||||||
LOCKMODE lockmode = ShareUpdateExclusiveLock;
|
LOCKMODE lockmode = ShareUpdateExclusiveLock;
|
||||||
|
|
||||||
@ -2790,28 +2777,58 @@ AlterTableGetLockLevel(List *cmds)
|
|||||||
switch (cmd->subtype)
|
switch (cmd->subtype)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
* Need AccessExclusiveLock for these subcommands because they
|
* These subcommands rewrite the heap, so require full locks.
|
||||||
* 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
|
case AT_AddColumn: /* may rewrite heap, in some cases and visible
|
||||||
* to SELECT */
|
* to SELECT */
|
||||||
case AT_DropColumn: /* change visible to SELECT */
|
case AT_SetTableSpace: /* must rewrite heap */
|
||||||
case AT_AddColumnToView: /* CREATE VIEW */
|
|
||||||
case AT_AlterColumnType: /* 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_DropConstraint: /* as DROP INDEX */
|
||||||
case AT_AddOids: /* must rewrite heap */
|
case AT_DropNotNull: /* may change some SQL plans */
|
||||||
case AT_DropOids: /* calls AT_DropColumn */
|
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_EnableAlwaysRule: /* may change SELECT rules */
|
||||||
case AT_EnableReplicaRule: /* 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 */
|
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_ChangeOwner: /* change visible to SELECT */
|
||||||
case AT_SetTableSpace: /* must rewrite heap */
|
cmd_lockmode = AccessExclusiveLock;
|
||||||
case AT_DropNotNull: /* may change some SQL plans */
|
break;
|
||||||
case AT_SetNotNull:
|
|
||||||
|
/*
|
||||||
|
* Changing foreign table options may affect optimisation.
|
||||||
|
*/
|
||||||
case AT_GenericOptions:
|
case AT_GenericOptions:
|
||||||
case AT_AlterColumnGenericOptions:
|
case AT_AlterColumnGenericOptions:
|
||||||
cmd_lockmode = AccessExclusiveLock;
|
cmd_lockmode = AccessExclusiveLock;
|
||||||
@ -2819,6 +2836,7 @@ AlterTableGetLockLevel(List *cmds)
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* These subcommands affect write operations only.
|
* These subcommands affect write operations only.
|
||||||
|
* XXX Theoretically, these could be ShareRowExclusiveLock.
|
||||||
*/
|
*/
|
||||||
case AT_ColumnDefault:
|
case AT_ColumnDefault:
|
||||||
case AT_ProcessedConstraint: /* becomes AT_AddConstraint */
|
case AT_ProcessedConstraint: /* becomes AT_AddConstraint */
|
||||||
@ -2832,10 +2850,12 @@ AlterTableGetLockLevel(List *cmds)
|
|||||||
case AT_DisableTrig:
|
case AT_DisableTrig:
|
||||||
case AT_DisableTrigAll:
|
case AT_DisableTrigAll:
|
||||||
case AT_DisableTrigUser:
|
case AT_DisableTrigUser:
|
||||||
|
case AT_AlterConstraint:
|
||||||
case AT_AddIndex: /* from ADD CONSTRAINT */
|
case AT_AddIndex: /* from ADD CONSTRAINT */
|
||||||
case AT_AddIndexConstraint:
|
case AT_AddIndexConstraint:
|
||||||
case AT_ReplicaIdentity:
|
case AT_ReplicaIdentity:
|
||||||
cmd_lockmode = ShareRowExclusiveLock;
|
case AT_SetNotNull:
|
||||||
|
cmd_lockmode = AccessExclusiveLock;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case AT_AddConstraint:
|
case AT_AddConstraint:
|
||||||
@ -2854,8 +2874,10 @@ AlterTableGetLockLevel(List *cmds)
|
|||||||
* could reduce the lock strength to ShareLock if
|
* could reduce the lock strength to ShareLock if
|
||||||
* we can work out how to allow concurrent catalog
|
* we can work out how to allow concurrent catalog
|
||||||
* updates.
|
* updates.
|
||||||
|
* XXX Might be set down to ShareRowExclusiveLock
|
||||||
|
* but requires further analysis.
|
||||||
*/
|
*/
|
||||||
cmd_lockmode = ShareRowExclusiveLock;
|
cmd_lockmode = AccessExclusiveLock;
|
||||||
break;
|
break;
|
||||||
case CONSTR_FOREIGN:
|
case CONSTR_FOREIGN:
|
||||||
|
|
||||||
@ -2863,12 +2885,15 @@ AlterTableGetLockLevel(List *cmds)
|
|||||||
* We add triggers to both tables when we add a
|
* We add triggers to both tables when we add a
|
||||||
* Foreign Key, so the lock level must be at least
|
* Foreign Key, so the lock level must be at least
|
||||||
* as strong as CREATE TRIGGER.
|
* 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;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
cmd_lockmode = ShareRowExclusiveLock;
|
cmd_lockmode = AccessExclusiveLock;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -2879,22 +2904,31 @@ AlterTableGetLockLevel(List *cmds)
|
|||||||
* behaviour, while queries started after we commit will see
|
* behaviour, while queries started after we commit will see
|
||||||
* new behaviour. No need to prevent reads or writes to the
|
* new behaviour. No need to prevent reads or writes to the
|
||||||
* subtable while we hook it up though.
|
* subtable while we hook it up though.
|
||||||
|
* Changing the TupDesc may be a problem, so keep highest lock.
|
||||||
*/
|
*/
|
||||||
case AT_AddInherit:
|
case AT_AddInherit:
|
||||||
case AT_DropInherit:
|
case AT_DropInherit:
|
||||||
cmd_lockmode = ShareUpdateExclusiveLock;
|
cmd_lockmode = AccessExclusiveLock;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* These subcommands affect implicit row type conversion. They
|
* 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
|
* don't provide for invalidating parse trees as a result of
|
||||||
* such changes. Do avoid concurrent pg_class updates,
|
* such changes, so we keep these at AccessExclusiveLock.
|
||||||
* though.
|
|
||||||
*/
|
*/
|
||||||
case AT_AddOf:
|
case AT_AddOf:
|
||||||
case AT_DropOf:
|
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
|
* These subcommands affect general strategies for performance
|
||||||
@ -2906,20 +2940,33 @@ AlterTableGetLockLevel(List *cmds)
|
|||||||
* applies: we don't currently allow concurrent catalog
|
* applies: we don't currently allow concurrent catalog
|
||||||
* updates.
|
* updates.
|
||||||
*/
|
*/
|
||||||
case AT_SetStatistics:
|
case AT_SetStatistics: /* Uses MVCC in getTableAttrs() */
|
||||||
case AT_ClusterOn:
|
case AT_ClusterOn: /* Uses MVCC in getIndexes() */
|
||||||
case AT_DropCluster:
|
case AT_DropCluster: /* Uses MVCC in getIndexes() */
|
||||||
case AT_SetRelOptions:
|
case AT_SetOptions: /* Uses MVCC in getTableAttrs() */
|
||||||
case AT_ResetRelOptions:
|
case AT_ResetOptions: /* Uses MVCC in getTableAttrs() */
|
||||||
case AT_ReplaceRelOptions:
|
|
||||||
case AT_SetOptions:
|
|
||||||
case AT_ResetOptions:
|
|
||||||
case AT_SetStorage:
|
|
||||||
case AT_AlterConstraint:
|
|
||||||
case AT_ValidateConstraint:
|
|
||||||
cmd_lockmode = ShareUpdateExclusiveLock;
|
cmd_lockmode = ShareUpdateExclusiveLock;
|
||||||
break;
|
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 */
|
default: /* oops */
|
||||||
elog(ERROR, "unrecognized alter table type: %d",
|
elog(ERROR, "unrecognized alter table type: %d",
|
||||||
(int) cmd->subtype);
|
(int) cmd->subtype);
|
||||||
@ -2932,9 +2979,6 @@ AlterTableGetLockLevel(List *cmds)
|
|||||||
if (cmd_lockmode > lockmode)
|
if (cmd_lockmode > lockmode)
|
||||||
lockmode = cmd_lockmode;
|
lockmode = cmd_lockmode;
|
||||||
}
|
}
|
||||||
#else
|
|
||||||
LOCKMODE lockmode = AccessExclusiveLock;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return lockmode;
|
return lockmode;
|
||||||
}
|
}
|
||||||
@ -3272,7 +3316,7 @@ ATRewriteCatalogs(List **wqueue, LOCKMODE lockmode)
|
|||||||
|
|
||||||
if (tab->relkind == RELKIND_RELATION ||
|
if (tab->relkind == RELKIND_RELATION ||
|
||||||
tab->relkind == RELKIND_MATVIEW)
|
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 */
|
/* Create transient table that will receive the modified data */
|
||||||
OIDNewHeap = make_new_heap(tab->relid, NewTableSpace, false,
|
OIDNewHeap = make_new_heap(tab->relid, NewTableSpace, false,
|
||||||
AccessExclusiveLock);
|
lockmode);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copy the heap data into the new table with the desired
|
* Copy the heap data into the new table with the desired
|
||||||
|
@ -908,7 +908,7 @@ ProcessUtilitySlow(Node *parsetree,
|
|||||||
InvalidOid);
|
InvalidOid);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Let AlterTableCreateToastTable decide if this
|
* Let NewRelationCreateToastTable decide if this
|
||||||
* one needs a secondary relation too.
|
* one needs a secondary relation too.
|
||||||
*/
|
*/
|
||||||
CommandCounterIncrement();
|
CommandCounterIncrement();
|
||||||
@ -927,7 +927,7 @@ ProcessUtilitySlow(Node *parsetree,
|
|||||||
toast_options,
|
toast_options,
|
||||||
true);
|
true);
|
||||||
|
|
||||||
AlterTableCreateToastTable(relOid, toast_options);
|
NewRelationCreateToastTable(relOid, toast_options);
|
||||||
}
|
}
|
||||||
else if (IsA(stmt, CreateForeignTableStmt))
|
else if (IsA(stmt, CreateForeignTableStmt))
|
||||||
{
|
{
|
||||||
|
@ -54,6 +54,7 @@
|
|||||||
#include "utils/fmgroids.h"
|
#include "utils/fmgroids.h"
|
||||||
#include "utils/lsyscache.h"
|
#include "utils/lsyscache.h"
|
||||||
#include "utils/rel.h"
|
#include "utils/rel.h"
|
||||||
|
#include "utils/snapmgr.h"
|
||||||
#include "utils/syscache.h"
|
#include "utils/syscache.h"
|
||||||
#include "utils/tqual.h"
|
#include "utils/tqual.h"
|
||||||
#include "utils/typcache.h"
|
#include "utils/typcache.h"
|
||||||
@ -1284,6 +1285,9 @@ pg_get_constraintdef_string(Oid constraintId)
|
|||||||
return pg_get_constraintdef_worker(constraintId, true, 0);
|
return pg_get_constraintdef_worker(constraintId, true, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* As of 9.4, we now use an MVCC snapshot for this.
|
||||||
|
*/
|
||||||
static char *
|
static char *
|
||||||
pg_get_constraintdef_worker(Oid constraintId, bool fullCommand,
|
pg_get_constraintdef_worker(Oid constraintId, bool fullCommand,
|
||||||
int prettyFlags)
|
int prettyFlags)
|
||||||
@ -1291,10 +1295,34 @@ pg_get_constraintdef_worker(Oid constraintId, bool fullCommand,
|
|||||||
HeapTuple tup;
|
HeapTuple tup;
|
||||||
Form_pg_constraint conForm;
|
Form_pg_constraint conForm;
|
||||||
StringInfoData buf;
|
StringInfoData buf;
|
||||||
|
SysScanDesc scandesc;
|
||||||
|
ScanKeyData scankey[1];
|
||||||
|
Snapshot snapshot = RegisterSnapshot(GetTransactionSnapshot());
|
||||||
|
Relation relation = heap_open(ConstraintRelationId, AccessShareLock);
|
||||||
|
|
||||||
|
ScanKeyInit(&scankey[0],
|
||||||
|
ObjectIdAttributeNumber,
|
||||||
|
BTEqualStrategyNumber, F_OIDEQ,
|
||||||
|
ObjectIdGetDatum(constraintId));
|
||||||
|
|
||||||
|
scandesc = systable_beginscan(relation,
|
||||||
|
ConstraintOidIndexId,
|
||||||
|
true,
|
||||||
|
snapshot,
|
||||||
|
1,
|
||||||
|
scankey);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We later use the tuple with SysCacheGetAttr() as if we
|
||||||
|
* had obtained it via SearchSysCache, which works fine.
|
||||||
|
*/
|
||||||
|
tup = systable_getnext(scandesc);
|
||||||
|
|
||||||
|
UnregisterSnapshot(snapshot);
|
||||||
|
|
||||||
tup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(constraintId));
|
|
||||||
if (!HeapTupleIsValid(tup)) /* should not happen */
|
if (!HeapTupleIsValid(tup)) /* should not happen */
|
||||||
elog(ERROR, "cache lookup failed for constraint %u", constraintId);
|
elog(ERROR, "cache lookup failed for constraint %u", constraintId);
|
||||||
|
|
||||||
conForm = (Form_pg_constraint) GETSTRUCT(tup);
|
conForm = (Form_pg_constraint) GETSTRUCT(tup);
|
||||||
|
|
||||||
initStringInfo(&buf);
|
initStringInfo(&buf);
|
||||||
@ -1575,7 +1603,8 @@ pg_get_constraintdef_worker(Oid constraintId, bool fullCommand,
|
|||||||
appendStringInfoString(&buf, " NOT VALID");
|
appendStringInfoString(&buf, " NOT VALID");
|
||||||
|
|
||||||
/* Cleanup */
|
/* Cleanup */
|
||||||
ReleaseSysCache(tup);
|
systable_endscan(scandesc);
|
||||||
|
heap_close(relation, AccessShareLock);
|
||||||
|
|
||||||
return buf.data;
|
return buf.data;
|
||||||
}
|
}
|
||||||
|
74
src/backend/utils/cache/relcache.c
vendored
74
src/backend/utils/cache/relcache.c
vendored
@ -162,6 +162,14 @@ static bool eoxact_list_overflowed = false;
|
|||||||
eoxact_list_overflowed = true; \
|
eoxact_list_overflowed = true; \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* EOXactTupleDescArray stores TupleDescs that (might) need AtEOXact
|
||||||
|
* cleanup work. The array expands as needed; there is no hashtable because
|
||||||
|
* we don't need to access individual items except at EOXact.
|
||||||
|
*/
|
||||||
|
static TupleDesc *EOXactTupleDescArray;
|
||||||
|
static int NextEOXactTupleDescNum = 0;
|
||||||
|
static int EOXactTupleDescArrayLen = 0;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* macros to manipulate the lookup hashtables
|
* macros to manipulate the lookup hashtables
|
||||||
@ -220,11 +228,12 @@ static HTAB *OpClassCache = NULL;
|
|||||||
|
|
||||||
/* non-export function prototypes */
|
/* non-export function prototypes */
|
||||||
|
|
||||||
static void RelationDestroyRelation(Relation relation);
|
static void RelationDestroyRelation(Relation relation, bool remember_tupdesc);
|
||||||
static void RelationClearRelation(Relation relation, bool rebuild);
|
static void RelationClearRelation(Relation relation, bool rebuild);
|
||||||
|
|
||||||
static void RelationReloadIndexInfo(Relation relation);
|
static void RelationReloadIndexInfo(Relation relation);
|
||||||
static void RelationFlushRelation(Relation relation);
|
static void RelationFlushRelation(Relation relation);
|
||||||
|
static void RememberToFreeTupleDescAtEOX(TupleDesc td);
|
||||||
static void AtEOXact_cleanup(Relation relation, bool isCommit);
|
static void AtEOXact_cleanup(Relation relation, bool isCommit);
|
||||||
static void AtEOSubXact_cleanup(Relation relation, bool isCommit,
|
static void AtEOSubXact_cleanup(Relation relation, bool isCommit,
|
||||||
SubTransactionId mySubid, SubTransactionId parentSubid);
|
SubTransactionId mySubid, SubTransactionId parentSubid);
|
||||||
@ -1858,7 +1867,7 @@ RelationReloadIndexInfo(Relation relation)
|
|||||||
* Caller must already have unhooked the entry from the hash table.
|
* Caller must already have unhooked the entry from the hash table.
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
RelationDestroyRelation(Relation relation)
|
RelationDestroyRelation(Relation relation, bool remember_tupdesc)
|
||||||
{
|
{
|
||||||
Assert(RelationHasReferenceCountZero(relation));
|
Assert(RelationHasReferenceCountZero(relation));
|
||||||
|
|
||||||
@ -1878,7 +1887,20 @@ RelationDestroyRelation(Relation relation)
|
|||||||
/* can't use DecrTupleDescRefCount here */
|
/* can't use DecrTupleDescRefCount here */
|
||||||
Assert(relation->rd_att->tdrefcount > 0);
|
Assert(relation->rd_att->tdrefcount > 0);
|
||||||
if (--relation->rd_att->tdrefcount == 0)
|
if (--relation->rd_att->tdrefcount == 0)
|
||||||
FreeTupleDesc(relation->rd_att);
|
{
|
||||||
|
/*
|
||||||
|
* If we Rebuilt a relcache entry during a transaction then its
|
||||||
|
* possible we did that because the TupDesc changed as the result
|
||||||
|
* of an ALTER TABLE that ran at less than AccessExclusiveLock.
|
||||||
|
* It's possible someone copied that TupDesc, in which case the
|
||||||
|
* copy would point to free'd memory. So if we rebuild an entry
|
||||||
|
* we keep the TupDesc around until end of transaction, to be safe.
|
||||||
|
*/
|
||||||
|
if (remember_tupdesc)
|
||||||
|
RememberToFreeTupleDescAtEOX(relation->rd_att);
|
||||||
|
else
|
||||||
|
FreeTupleDesc(relation->rd_att);
|
||||||
|
}
|
||||||
list_free(relation->rd_indexlist);
|
list_free(relation->rd_indexlist);
|
||||||
bms_free(relation->rd_indexattr);
|
bms_free(relation->rd_indexattr);
|
||||||
FreeTriggerDesc(relation->trigdesc);
|
FreeTriggerDesc(relation->trigdesc);
|
||||||
@ -1992,7 +2014,7 @@ RelationClearRelation(Relation relation, bool rebuild)
|
|||||||
RelationCacheDelete(relation);
|
RelationCacheDelete(relation);
|
||||||
|
|
||||||
/* And release storage */
|
/* And release storage */
|
||||||
RelationDestroyRelation(relation);
|
RelationDestroyRelation(relation, false);
|
||||||
}
|
}
|
||||||
else if (!IsTransactionState())
|
else if (!IsTransactionState())
|
||||||
{
|
{
|
||||||
@ -2059,7 +2081,7 @@ RelationClearRelation(Relation relation, bool rebuild)
|
|||||||
{
|
{
|
||||||
/* Should only get here if relation was deleted */
|
/* Should only get here if relation was deleted */
|
||||||
RelationCacheDelete(relation);
|
RelationCacheDelete(relation);
|
||||||
RelationDestroyRelation(relation);
|
RelationDestroyRelation(relation, false);
|
||||||
elog(ERROR, "relation %u deleted while still in use", save_relid);
|
elog(ERROR, "relation %u deleted while still in use", save_relid);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2121,7 +2143,7 @@ RelationClearRelation(Relation relation, bool rebuild)
|
|||||||
#undef SWAPFIELD
|
#undef SWAPFIELD
|
||||||
|
|
||||||
/* And now we can throw away the temporary entry */
|
/* And now we can throw away the temporary entry */
|
||||||
RelationDestroyRelation(newrel);
|
RelationDestroyRelation(newrel, !keep_tupdesc);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2359,6 +2381,33 @@ RelationCloseSmgrByOid(Oid relationId)
|
|||||||
RelationCloseSmgr(relation);
|
RelationCloseSmgr(relation);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
RememberToFreeTupleDescAtEOX(TupleDesc td)
|
||||||
|
{
|
||||||
|
if (EOXactTupleDescArray == NULL)
|
||||||
|
{
|
||||||
|
MemoryContext oldcxt;
|
||||||
|
oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
|
||||||
|
|
||||||
|
EOXactTupleDescArray = (TupleDesc *) palloc(16 * sizeof(TupleDesc));
|
||||||
|
EOXactTupleDescArrayLen = 16;
|
||||||
|
NextEOXactTupleDescNum = 0;
|
||||||
|
MemoryContextSwitchTo(oldcxt);
|
||||||
|
}
|
||||||
|
else if (NextEOXactTupleDescNum >= EOXactTupleDescArrayLen)
|
||||||
|
{
|
||||||
|
int32 newlen = EOXactTupleDescArrayLen * 2;
|
||||||
|
|
||||||
|
Assert(EOXactTupleDescArrayLen > 0);
|
||||||
|
|
||||||
|
EOXactTupleDescArray = (TupleDesc *) repalloc(EOXactTupleDescArray,
|
||||||
|
newlen * sizeof(TupleDesc));
|
||||||
|
EOXactTupleDescArrayLen = newlen;
|
||||||
|
}
|
||||||
|
|
||||||
|
EOXactTupleDescArray[NextEOXactTupleDescNum++] = td;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* AtEOXact_RelationCache
|
* AtEOXact_RelationCache
|
||||||
*
|
*
|
||||||
@ -2414,9 +2463,20 @@ AtEOXact_RelationCache(bool isCommit)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Now we're out of the transaction and can clear the list */
|
if (EOXactTupleDescArrayLen > 0)
|
||||||
|
{
|
||||||
|
Assert(EOXactTupleDescArray != NULL);
|
||||||
|
for (i = 0; i < NextEOXactTupleDescNum; i++)
|
||||||
|
FreeTupleDesc(EOXactTupleDescArray[i]);
|
||||||
|
pfree(EOXactTupleDescArray);
|
||||||
|
EOXactTupleDescArray = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Now we're out of the transaction and can clear the lists */
|
||||||
eoxact_list_len = 0;
|
eoxact_list_len = 0;
|
||||||
eoxact_list_overflowed = false;
|
eoxact_list_overflowed = false;
|
||||||
|
NextEOXactTupleDescNum = 0;
|
||||||
|
EOXactTupleDescArrayLen = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -14,10 +14,16 @@
|
|||||||
#ifndef TOASTING_H
|
#ifndef TOASTING_H
|
||||||
#define TOASTING_H
|
#define TOASTING_H
|
||||||
|
|
||||||
|
#include "storage/lock.h"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* toasting.c prototypes
|
* toasting.c prototypes
|
||||||
*/
|
*/
|
||||||
extern void AlterTableCreateToastTable(Oid relOid, Datum reloptions);
|
extern void NewRelationCreateToastTable(Oid relOid, Datum reloptions);
|
||||||
|
extern void NewHeapCreateToastTable(Oid relOid, Datum reloptions,
|
||||||
|
LOCKMODE lockmode);
|
||||||
|
extern void AlterTableCreateToastTable(Oid relOid, Datum reloptions,
|
||||||
|
LOCKMODE lockmode);
|
||||||
extern void BootstrapToastTable(char *relName,
|
extern void BootstrapToastTable(char *relName,
|
||||||
Oid toastOid, Oid toastIndexOid);
|
Oid toastOid, Oid toastIndexOid);
|
||||||
|
|
||||||
|
@ -23,4 +23,5 @@ test: multixact-no-deadlock
|
|||||||
test: multixact-no-forget
|
test: multixact-no-forget
|
||||||
test: propagate-lock-delete
|
test: propagate-lock-delete
|
||||||
test: drop-index-concurrently-1
|
test: drop-index-concurrently-1
|
||||||
|
test: alter-table-1
|
||||||
test: timeouts
|
test: timeouts
|
||||||
|
@ -1840,28 +1840,31 @@ and relnamespace != (select oid from pg_namespace where nspname = 'pg_catalog')
|
|||||||
and c.relname != 'my_locks'
|
and c.relname != 'my_locks'
|
||||||
group by c.relname;
|
group by c.relname;
|
||||||
create table alterlock (f1 int primary key, f2 text);
|
create table alterlock (f1 int primary key, f2 text);
|
||||||
|
insert into alterlock values (1, 'foo');
|
||||||
|
create table alterlock2 (f3 int primary key, f1 int);
|
||||||
|
insert into alterlock2 values (1, 1);
|
||||||
begin; alter table alterlock alter column f2 set statistics 150;
|
begin; alter table alterlock alter column f2 set statistics 150;
|
||||||
select * from my_locks order by 1;
|
select * from my_locks order by 1;
|
||||||
relname | max_lockmode
|
relname | max_lockmode
|
||||||
-----------+---------------------
|
-----------+--------------------------
|
||||||
alterlock | AccessExclusiveLock
|
alterlock | ShareUpdateExclusiveLock
|
||||||
(1 row)
|
(1 row)
|
||||||
|
|
||||||
rollback;
|
rollback;
|
||||||
begin; alter table alterlock cluster on alterlock_pkey;
|
begin; alter table alterlock cluster on alterlock_pkey;
|
||||||
select * from my_locks order by 1;
|
select * from my_locks order by 1;
|
||||||
relname | max_lockmode
|
relname | max_lockmode
|
||||||
----------------+---------------------
|
----------------+--------------------------
|
||||||
alterlock | AccessExclusiveLock
|
alterlock | ShareUpdateExclusiveLock
|
||||||
alterlock_pkey | AccessExclusiveLock
|
alterlock_pkey | ShareUpdateExclusiveLock
|
||||||
(2 rows)
|
(2 rows)
|
||||||
|
|
||||||
commit;
|
commit;
|
||||||
begin; alter table alterlock set without cluster;
|
begin; alter table alterlock set without cluster;
|
||||||
select * from my_locks order by 1;
|
select * from my_locks order by 1;
|
||||||
relname | max_lockmode
|
relname | max_lockmode
|
||||||
-----------+---------------------
|
-----------+--------------------------
|
||||||
alterlock | AccessExclusiveLock
|
alterlock | ShareUpdateExclusiveLock
|
||||||
(1 row)
|
(1 row)
|
||||||
|
|
||||||
commit;
|
commit;
|
||||||
@ -1903,9 +1906,9 @@ select * from my_locks order by 1;
|
|||||||
commit;
|
commit;
|
||||||
begin; alter table alterlock alter column f2 set (n_distinct = 1);
|
begin; alter table alterlock alter column f2 set (n_distinct = 1);
|
||||||
select * from my_locks order by 1;
|
select * from my_locks order by 1;
|
||||||
relname | max_lockmode
|
relname | max_lockmode
|
||||||
-----------+---------------------
|
-----------+--------------------------
|
||||||
alterlock | AccessExclusiveLock
|
alterlock | ShareUpdateExclusiveLock
|
||||||
(1 row)
|
(1 row)
|
||||||
|
|
||||||
rollback;
|
rollback;
|
||||||
@ -1924,8 +1927,62 @@ select * from my_locks order by 1;
|
|||||||
alterlock | AccessExclusiveLock
|
alterlock | AccessExclusiveLock
|
||||||
(1 row)
|
(1 row)
|
||||||
|
|
||||||
|
rollback;
|
||||||
|
begin;
|
||||||
|
create trigger ttdummy
|
||||||
|
before delete or update on alterlock
|
||||||
|
for each row
|
||||||
|
execute procedure
|
||||||
|
ttdummy (1, 1);
|
||||||
|
select * from my_locks order by 1;
|
||||||
|
relname | max_lockmode
|
||||||
|
-----------+---------------------
|
||||||
|
alterlock | AccessExclusiveLock
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
rollback;
|
||||||
|
begin;
|
||||||
|
select * from my_locks order by 1;
|
||||||
|
relname | max_lockmode
|
||||||
|
---------+--------------
|
||||||
|
(0 rows)
|
||||||
|
|
||||||
|
alter table alterlock2 add foreign key (f1) references alterlock (f1);
|
||||||
|
select * from my_locks order by 1;
|
||||||
|
relname | max_lockmode
|
||||||
|
-----------------+---------------------
|
||||||
|
alterlock | AccessExclusiveLock
|
||||||
|
alterlock2 | AccessExclusiveLock
|
||||||
|
alterlock2_pkey | AccessShareLock
|
||||||
|
alterlock_pkey | AccessShareLock
|
||||||
|
(4 rows)
|
||||||
|
|
||||||
|
rollback;
|
||||||
|
begin;
|
||||||
|
alter table alterlock2
|
||||||
|
add constraint alterlock2nv foreign key (f1) references alterlock (f1) NOT VALID;
|
||||||
|
select * from my_locks order by 1;
|
||||||
|
relname | max_lockmode
|
||||||
|
------------+---------------------
|
||||||
|
alterlock | AccessExclusiveLock
|
||||||
|
alterlock2 | AccessExclusiveLock
|
||||||
|
(2 rows)
|
||||||
|
|
||||||
|
commit;
|
||||||
|
begin;
|
||||||
|
alter table alterlock2 validate constraint alterlock2nv;
|
||||||
|
select * from my_locks order by 1;
|
||||||
|
relname | max_lockmode
|
||||||
|
-----------------+--------------------------
|
||||||
|
alterlock | RowShareLock
|
||||||
|
alterlock2 | ShareUpdateExclusiveLock
|
||||||
|
alterlock2_pkey | AccessShareLock
|
||||||
|
alterlock_pkey | AccessShareLock
|
||||||
|
(4 rows)
|
||||||
|
|
||||||
rollback;
|
rollback;
|
||||||
-- cleanup
|
-- cleanup
|
||||||
|
drop table alterlock2;
|
||||||
drop table alterlock;
|
drop table alterlock;
|
||||||
drop view my_locks;
|
drop view my_locks;
|
||||||
drop type lockmodes;
|
drop type lockmodes;
|
||||||
|
@ -1283,6 +1283,9 @@ and c.relname != 'my_locks'
|
|||||||
group by c.relname;
|
group by c.relname;
|
||||||
|
|
||||||
create table alterlock (f1 int primary key, f2 text);
|
create table alterlock (f1 int primary key, f2 text);
|
||||||
|
insert into alterlock values (1, 'foo');
|
||||||
|
create table alterlock2 (f3 int primary key, f1 int);
|
||||||
|
insert into alterlock2 values (1, 1);
|
||||||
|
|
||||||
begin; alter table alterlock alter column f2 set statistics 150;
|
begin; alter table alterlock alter column f2 set statistics 150;
|
||||||
select * from my_locks order by 1;
|
select * from my_locks order by 1;
|
||||||
@ -1324,7 +1327,33 @@ begin; alter table alterlock alter column f2 set default 'x';
|
|||||||
select * from my_locks order by 1;
|
select * from my_locks order by 1;
|
||||||
rollback;
|
rollback;
|
||||||
|
|
||||||
|
begin;
|
||||||
|
create trigger ttdummy
|
||||||
|
before delete or update on alterlock
|
||||||
|
for each row
|
||||||
|
execute procedure
|
||||||
|
ttdummy (1, 1);
|
||||||
|
select * from my_locks order by 1;
|
||||||
|
rollback;
|
||||||
|
|
||||||
|
begin;
|
||||||
|
select * from my_locks order by 1;
|
||||||
|
alter table alterlock2 add foreign key (f1) references alterlock (f1);
|
||||||
|
select * from my_locks order by 1;
|
||||||
|
rollback;
|
||||||
|
|
||||||
|
begin;
|
||||||
|
alter table alterlock2
|
||||||
|
add constraint alterlock2nv foreign key (f1) references alterlock (f1) NOT VALID;
|
||||||
|
select * from my_locks order by 1;
|
||||||
|
commit;
|
||||||
|
begin;
|
||||||
|
alter table alterlock2 validate constraint alterlock2nv;
|
||||||
|
select * from my_locks order by 1;
|
||||||
|
rollback;
|
||||||
|
|
||||||
-- cleanup
|
-- cleanup
|
||||||
|
drop table alterlock2;
|
||||||
drop table alterlock;
|
drop table alterlock;
|
||||||
drop view my_locks;
|
drop view my_locks;
|
||||||
drop type lockmodes;
|
drop type lockmodes;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user