Support deferrable uniqueness constraints.
The current implementation fires an AFTER ROW trigger for each tuple that looks like it might be non-unique according to the index contents at the time of insertion. This works well as long as there aren't many conflicts, but won't scale to massive unique-key reassignments. Improving that case is a TODO item. Dean Rasheed
This commit is contained in:
parent
8504905793
commit
25d9bf2e3e
@ -1,4 +1,4 @@
|
|||||||
<!-- $PostgreSQL: pgsql/doc/src/sgml/catalogs.sgml,v 2.202 2009/07/28 02:56:29 tgl Exp $ -->
|
<!-- $PostgreSQL: pgsql/doc/src/sgml/catalogs.sgml,v 2.203 2009/07/29 20:56:17 tgl Exp $ -->
|
||||||
<!--
|
<!--
|
||||||
Documentation of the system catalogs, directed toward PostgreSQL developers
|
Documentation of the system catalogs, directed toward PostgreSQL developers
|
||||||
-->
|
-->
|
||||||
@ -2675,6 +2675,14 @@
|
|||||||
(<structfield>indisunique</> should always be true when this is true)</entry>
|
(<structfield>indisunique</> should always be true when this is true)</entry>
|
||||||
</row>
|
</row>
|
||||||
|
|
||||||
|
<row>
|
||||||
|
<entry><structfield>indimmediate</structfield></entry>
|
||||||
|
<entry><type>bool</type></entry>
|
||||||
|
<entry></entry>
|
||||||
|
<entry>If true, the uniqueness check is enforced immediately on insertion
|
||||||
|
(<structfield>indisunique</> should always be true when this is true)</entry>
|
||||||
|
</row>
|
||||||
|
|
||||||
<row>
|
<row>
|
||||||
<entry><structfield>indisclustered</structfield></entry>
|
<entry><structfield>indisclustered</structfield></entry>
|
||||||
<entry><type>bool</type></entry>
|
<entry><type>bool</type></entry>
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
<!-- $PostgreSQL: pgsql/doc/src/sgml/indexam.sgml,v 2.30 2009/03/24 20:17:08 tgl Exp $ -->
|
<!-- $PostgreSQL: pgsql/doc/src/sgml/indexam.sgml,v 2.31 2009/07/29 20:56:17 tgl Exp $ -->
|
||||||
|
|
||||||
<chapter id="indexam">
|
<chapter id="indexam">
|
||||||
<title>Index Access Method Interface Definition</title>
|
<title>Index Access Method Interface Definition</title>
|
||||||
@ -172,20 +172,32 @@ aminsert (Relation indexRelation,
|
|||||||
bool *isnull,
|
bool *isnull,
|
||||||
ItemPointer heap_tid,
|
ItemPointer heap_tid,
|
||||||
Relation heapRelation,
|
Relation heapRelation,
|
||||||
bool check_uniqueness);
|
IndexUniqueCheck checkUnique);
|
||||||
</programlisting>
|
</programlisting>
|
||||||
Insert a new tuple into an existing index. The <literal>values</> and
|
Insert a new tuple into an existing index. The <literal>values</> and
|
||||||
<literal>isnull</> arrays give the key values to be indexed, and
|
<literal>isnull</> arrays give the key values to be indexed, and
|
||||||
<literal>heap_tid</> is the TID to be indexed.
|
<literal>heap_tid</> is the TID to be indexed.
|
||||||
If the access method supports unique indexes (its
|
If the access method supports unique indexes (its
|
||||||
<structname>pg_am</>.<structfield>amcanunique</> flag is true) then
|
<structname>pg_am</>.<structfield>amcanunique</> flag is true) then
|
||||||
<literal>check_uniqueness</> might be true, in which case the access method
|
<literal>checkUnique</> indicates the type of uniqueness check to
|
||||||
must verify that there is no conflicting row; this is the only situation in
|
perform. This varies depending on whether the unique constraint is
|
||||||
which the access method normally needs the <literal>heapRelation</>
|
deferrable; see <xref linkend="index-unique-checks"> for details.
|
||||||
parameter. See <xref linkend="index-unique-checks"> for details.
|
Normally the access method only needs the <literal>heapRelation</>
|
||||||
The result is TRUE if an index entry was inserted, FALSE if not. (A FALSE
|
parameter when performing uniqueness checking (since then it will have to
|
||||||
result does not denote an error condition, but is used for cases such
|
look into the heap to verify tuple liveness).
|
||||||
as an index method refusing to index a NULL.)
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
The function's boolean result value is significant only when
|
||||||
|
<literal>checkUnique</> is <literal>UNIQUE_CHECK_PARTIAL</>.
|
||||||
|
In this case a TRUE result means the new entry is known unique, whereas
|
||||||
|
FALSE means it might be non-unique (and a deferred uniqueness check must
|
||||||
|
be scheduled). For other cases a constant FALSE result is recommended.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Some indexes might not index all tuples. If the tuple is not to be
|
||||||
|
indexed, <function>aminsert</> should just return without doing anything.
|
||||||
</para>
|
</para>
|
||||||
|
|
||||||
<para>
|
<para>
|
||||||
@ -706,10 +718,10 @@ amrestrpos (IndexScanDesc scan);
|
|||||||
</para>
|
</para>
|
||||||
|
|
||||||
<para>
|
<para>
|
||||||
Furthermore, immediately before raising a uniqueness violation
|
Furthermore, immediately before reporting a uniqueness violation
|
||||||
according to the above rules, the access method must recheck the
|
according to the above rules, the access method must recheck the
|
||||||
liveness of the row being inserted. If it is committed dead then
|
liveness of the row being inserted. If it is committed dead then
|
||||||
no error should be raised. (This case cannot occur during the
|
no violation should be reported. (This case cannot occur during the
|
||||||
ordinary scenario of inserting a row that's just been created by
|
ordinary scenario of inserting a row that's just been created by
|
||||||
the current transaction. It can happen during
|
the current transaction. It can happen during
|
||||||
<command>CREATE UNIQUE INDEX CONCURRENTLY</>, however.)
|
<command>CREATE UNIQUE INDEX CONCURRENTLY</>, however.)
|
||||||
@ -728,8 +740,78 @@ amrestrpos (IndexScanDesc scan);
|
|||||||
</para>
|
</para>
|
||||||
|
|
||||||
<para>
|
<para>
|
||||||
The main limitation of this scheme is that it has no convenient way
|
If the unique constraint is deferrable, there is additional complexity:
|
||||||
to support deferred uniqueness checks.
|
we need to be able to insert an index entry for a new row, but defer any
|
||||||
|
uniqueness-violation error until end of statement or even later. To
|
||||||
|
avoid unnecessary repeat searches of the index, the index access method
|
||||||
|
should do a preliminary uniqueness check during the initial insertion.
|
||||||
|
If this shows that there is definitely no conflicting live tuple, we
|
||||||
|
are done. Otherwise, we schedule a recheck to occur when it is time to
|
||||||
|
enforce the constraint. If, at the time of the recheck, both the inserted
|
||||||
|
tuple and some other tuple with the same key are live, then the error
|
||||||
|
must be reported. (Note that for this purpose, <quote>live</> actually
|
||||||
|
means <quote>any tuple in the index entry's HOT chain is live</>.)
|
||||||
|
To implement this, the <function>aminsert</> function is passed a
|
||||||
|
<literal>checkUnique</> parameter having one of the following values:
|
||||||
|
|
||||||
|
<itemizedlist>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
<literal>UNIQUE_CHECK_NO</> indicates that no uniqueness checking
|
||||||
|
should be done (this is not a unique index).
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
<literal>UNIQUE_CHECK_YES</> indicates that this is a non-deferrable
|
||||||
|
unique index, and the uniqueness check must be done immediately, as
|
||||||
|
described above.
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
<literal>UNIQUE_CHECK_PARTIAL</> indicates that the unique
|
||||||
|
constraint is deferrable. <productname>PostgreSQL</productname>
|
||||||
|
will use this mode to insert each row's index entry. The access
|
||||||
|
method must allow duplicate entries into the index, and report any
|
||||||
|
potential duplicates by returning FALSE from <function>aminsert</>.
|
||||||
|
For each row for which FALSE is returned, a deferred recheck will
|
||||||
|
be scheduled.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
The access method must identify any rows which might violate the
|
||||||
|
unique constraint, but it is not an error for it to report false
|
||||||
|
positives. This allows the check to be done without waiting for other
|
||||||
|
transactions to finish; conflicts reported here are not treated as
|
||||||
|
errors and will be rechecked later, by which time they may no longer
|
||||||
|
be conflicts.
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
<literal>UNIQUE_CHECK_EXISTING</> indicates that this is a deferred
|
||||||
|
recheck of a row that was reported as a potential uniqueness violation.
|
||||||
|
Although this is implemented by calling <function>aminsert</>, the
|
||||||
|
access method must <emphasis>not</> insert a new index entry in this
|
||||||
|
case. The index entry is already present. Rather, the access method
|
||||||
|
must check to see if there is another live index entry. If so, and
|
||||||
|
if the target row is also still live, report error.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
It is recommended that in a <literal>UNIQUE_CHECK_EXISTING</> call,
|
||||||
|
the access method further verify that the target row actually does
|
||||||
|
have an existing entry in the index, and report error if not. This
|
||||||
|
is a good idea because the index tuple values passed to
|
||||||
|
<function>aminsert</> will have been recomputed. If the index
|
||||||
|
definition involves functions that are not really immutable, we
|
||||||
|
might be checking the wrong area of the index. Checking that the
|
||||||
|
target row is found in the recheck verifies that we are scanning
|
||||||
|
for the same tuple values as were used in the original insertion.
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
</itemizedlist>
|
||||||
</para>
|
</para>
|
||||||
|
|
||||||
</sect1>
|
</sect1>
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<!--
|
<!--
|
||||||
$PostgreSQL: pgsql/doc/src/sgml/ref/create_table.sgml,v 1.114 2009/02/12 13:25:33 momjian Exp $
|
$PostgreSQL: pgsql/doc/src/sgml/ref/create_table.sgml,v 1.115 2009/07/29 20:56:17 tgl Exp $
|
||||||
PostgreSQL documentation
|
PostgreSQL documentation
|
||||||
-->
|
-->
|
||||||
|
|
||||||
@ -423,11 +423,10 @@ and <replaceable class="PARAMETER">table_constraint</replaceable> is:
|
|||||||
contain values that match values in the referenced
|
contain values that match values in the referenced
|
||||||
column(s) of some row of the referenced table. If <replaceable
|
column(s) of some row of the referenced table. If <replaceable
|
||||||
class="parameter">refcolumn</replaceable> is omitted, the
|
class="parameter">refcolumn</replaceable> is omitted, the
|
||||||
primary key of the <replaceable
|
primary key of the <replaceable class="parameter">reftable</replaceable>
|
||||||
class="parameter">reftable</replaceable> is used. The
|
is used. The referenced columns must be the columns of a non-deferrable
|
||||||
referenced columns must be the columns of a unique or primary
|
unique or primary key constraint in the referenced table. Note that
|
||||||
key constraint in the referenced table. Note that foreign key
|
foreign key constraints cannot be defined between temporary tables and
|
||||||
constraints cannot be defined between temporary tables and
|
|
||||||
permanent tables.
|
permanent tables.
|
||||||
</para>
|
</para>
|
||||||
|
|
||||||
@ -534,9 +533,11 @@ and <replaceable class="PARAMETER">table_constraint</replaceable> is:
|
|||||||
after every command. Checking of constraints that are
|
after every command. Checking of constraints that are
|
||||||
deferrable can be postponed until the end of the transaction
|
deferrable can be postponed until the end of the transaction
|
||||||
(using the <xref linkend="sql-set-constraints" endterm="sql-set-constraints-title"> command).
|
(using the <xref linkend="sql-set-constraints" endterm="sql-set-constraints-title"> command).
|
||||||
<literal>NOT DEFERRABLE</literal> is the default. Only foreign
|
<literal>NOT DEFERRABLE</literal> is the default.
|
||||||
key constraints currently accept this clause. All other
|
Currently, only <literal>UNIQUE</>, <literal>PRIMARY KEY</>, and
|
||||||
constraint types are not deferrable.
|
<literal>REFERENCES</> (foreign key) constraints accept this
|
||||||
|
clause. <literal>NOT NULL</> and <literal>CHECK</> constraints are not
|
||||||
|
deferrable.
|
||||||
</para>
|
</para>
|
||||||
</listitem>
|
</listitem>
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
@ -1140,6 +1141,23 @@ CREATE TABLE cinemas (
|
|||||||
</para>
|
</para>
|
||||||
</refsect2>
|
</refsect2>
|
||||||
|
|
||||||
|
<refsect2>
|
||||||
|
<title>Non-deferred Uniqueness Constraints</title>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
When a <literal>UNIQUE</> or <literal>PRIMARY KEY</> constraint is
|
||||||
|
not deferrable, <productname>PostgreSQL</productname> checks for
|
||||||
|
uniqueness immediately whenever a row is inserted or modified.
|
||||||
|
The SQL standard says that uniqueness should be enforced only at
|
||||||
|
the end of the statement; this makes a difference when, for example,
|
||||||
|
a single command updates multiple key values. To obtain
|
||||||
|
standard-compliant behavior, declare the constraint as
|
||||||
|
<literal>DEFERRABLE</> but not deferred (i.e., <literal>INITIALLY
|
||||||
|
IMMEDIATE</>). Be aware that this can be significantly slower than
|
||||||
|
immediate uniqueness checking.
|
||||||
|
</para>
|
||||||
|
</refsect2>
|
||||||
|
|
||||||
<refsect2>
|
<refsect2>
|
||||||
<title>Column Check Constraints</title>
|
<title>Column Check Constraints</title>
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
<!-- $PostgreSQL: pgsql/doc/src/sgml/ref/set_constraints.sgml,v 1.16 2008/11/14 10:22:47 petere Exp $ -->
|
<!-- $PostgreSQL: pgsql/doc/src/sgml/ref/set_constraints.sgml,v 1.17 2009/07/29 20:56:17 tgl Exp $ -->
|
||||||
<refentry id="SQL-SET-CONSTRAINTS">
|
<refentry id="SQL-SET-CONSTRAINTS">
|
||||||
<refmeta>
|
<refmeta>
|
||||||
<refentrytitle id="SQL-SET-CONSTRAINTS-title">SET CONSTRAINTS</refentrytitle>
|
<refentrytitle id="SQL-SET-CONSTRAINTS-title">SET CONSTRAINTS</refentrytitle>
|
||||||
@ -66,10 +66,19 @@ SET CONSTRAINTS { ALL | <replaceable class="parameter">name</replaceable> [, ...
|
|||||||
</para>
|
</para>
|
||||||
|
|
||||||
<para>
|
<para>
|
||||||
Currently, only foreign key constraints are affected by this
|
Currently, only <literal>UNIQUE</>, <literal>PRIMARY KEY</>, and
|
||||||
setting. Check and unique constraints are always effectively
|
<literal>REFERENCES</> (foreign key) constraints are affected by this
|
||||||
not deferrable. Triggers that are declared as <quote>constraint
|
setting. <literal>NOT NULL</> and <literal>CHECK</> constraints are
|
||||||
triggers</> are also affected.
|
always checked immediately when a row is inserted or modified
|
||||||
|
(<emphasis>not</> at the end of the statement).
|
||||||
|
Uniqueness constraints that have not been declared <literal>DEFERRABLE</>
|
||||||
|
are also checked immediately.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
The firing of triggers that are declared as <quote>constraint triggers</>
|
||||||
|
is also controlled by this setting — they fire at the same time
|
||||||
|
that the associated constraint should be checked.
|
||||||
</para>
|
</para>
|
||||||
</refsect1>
|
</refsect1>
|
||||||
|
|
||||||
@ -92,7 +101,7 @@ SET CONSTRAINTS { ALL | <replaceable class="parameter">name</replaceable> [, ...
|
|||||||
This command complies with the behavior defined in the SQL
|
This command complies with the behavior defined in the SQL
|
||||||
standard, except for the limitation that, in
|
standard, except for the limitation that, in
|
||||||
<productname>PostgreSQL</productname>, it only applies to
|
<productname>PostgreSQL</productname>, it only applies to
|
||||||
foreign-key constraints.
|
foreign-key and uniqueness constraints.
|
||||||
</para>
|
</para>
|
||||||
|
|
||||||
</refsect1>
|
</refsect1>
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/access/gin/gininsert.c,v 1.22 2009/06/11 14:48:53 momjian Exp $
|
* $PostgreSQL: pgsql/src/backend/access/gin/gininsert.c,v 1.23 2009/07/29 20:56:17 tgl Exp $
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@ -415,12 +415,11 @@ gininsert(PG_FUNCTION_ARGS)
|
|||||||
|
|
||||||
#ifdef NOT_USED
|
#ifdef NOT_USED
|
||||||
Relation heapRel = (Relation) PG_GETARG_POINTER(4);
|
Relation heapRel = (Relation) PG_GETARG_POINTER(4);
|
||||||
bool checkUnique = PG_GETARG_BOOL(5);
|
IndexUniqueCheck checkUnique = (IndexUniqueCheck) PG_GETARG_INT32(5);
|
||||||
#endif
|
#endif
|
||||||
GinState ginstate;
|
GinState ginstate;
|
||||||
MemoryContext oldCtx;
|
MemoryContext oldCtx;
|
||||||
MemoryContext insertCtx;
|
MemoryContext insertCtx;
|
||||||
uint32 res = 0;
|
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
insertCtx = AllocSetContextCreate(CurrentMemoryContext,
|
insertCtx = AllocSetContextCreate(CurrentMemoryContext,
|
||||||
@ -440,7 +439,7 @@ gininsert(PG_FUNCTION_ARGS)
|
|||||||
memset(&collector, 0, sizeof(GinTupleCollector));
|
memset(&collector, 0, sizeof(GinTupleCollector));
|
||||||
for (i = 0; i < ginstate.origTupdesc->natts; i++)
|
for (i = 0; i < ginstate.origTupdesc->natts; i++)
|
||||||
if (!isnull[i])
|
if (!isnull[i])
|
||||||
res += ginHeapTupleFastCollect(index, &ginstate, &collector,
|
ginHeapTupleFastCollect(index, &ginstate, &collector,
|
||||||
(OffsetNumber) (i + 1), values[i], ht_ctid);
|
(OffsetNumber) (i + 1), values[i], ht_ctid);
|
||||||
|
|
||||||
ginHeapTupleFastInsert(index, &ginstate, &collector);
|
ginHeapTupleFastInsert(index, &ginstate, &collector);
|
||||||
@ -449,7 +448,7 @@ gininsert(PG_FUNCTION_ARGS)
|
|||||||
{
|
{
|
||||||
for (i = 0; i < ginstate.origTupdesc->natts; i++)
|
for (i = 0; i < ginstate.origTupdesc->natts; i++)
|
||||||
if (!isnull[i])
|
if (!isnull[i])
|
||||||
res += ginHeapTupleInsert(index, &ginstate,
|
ginHeapTupleInsert(index, &ginstate,
|
||||||
(OffsetNumber) (i + 1), values[i], ht_ctid);
|
(OffsetNumber) (i + 1), values[i], ht_ctid);
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -457,5 +456,5 @@ gininsert(PG_FUNCTION_ARGS)
|
|||||||
MemoryContextSwitchTo(oldCtx);
|
MemoryContextSwitchTo(oldCtx);
|
||||||
MemoryContextDelete(insertCtx);
|
MemoryContextDelete(insertCtx);
|
||||||
|
|
||||||
PG_RETURN_BOOL(res > 0);
|
PG_RETURN_BOOL(false);
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/access/gist/gist.c,v 1.156 2009/01/01 17:23:34 momjian Exp $
|
* $PostgreSQL: pgsql/src/backend/access/gist/gist.c,v 1.157 2009/07/29 20:56:18 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -225,7 +225,7 @@ gistinsert(PG_FUNCTION_ARGS)
|
|||||||
|
|
||||||
#ifdef NOT_USED
|
#ifdef NOT_USED
|
||||||
Relation heapRel = (Relation) PG_GETARG_POINTER(4);
|
Relation heapRel = (Relation) PG_GETARG_POINTER(4);
|
||||||
bool checkUnique = PG_GETARG_BOOL(5);
|
IndexUniqueCheck checkUnique = (IndexUniqueCheck) PG_GETARG_INT32(5);
|
||||||
#endif
|
#endif
|
||||||
IndexTuple itup;
|
IndexTuple itup;
|
||||||
GISTSTATE giststate;
|
GISTSTATE giststate;
|
||||||
@ -248,7 +248,7 @@ gistinsert(PG_FUNCTION_ARGS)
|
|||||||
MemoryContextSwitchTo(oldCtx);
|
MemoryContextSwitchTo(oldCtx);
|
||||||
MemoryContextDelete(insertCtx);
|
MemoryContextDelete(insertCtx);
|
||||||
|
|
||||||
PG_RETURN_BOOL(true);
|
PG_RETURN_BOOL(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/access/hash/hash.c,v 1.112 2009/06/11 14:48:53 momjian Exp $
|
* $PostgreSQL: pgsql/src/backend/access/hash/hash.c,v 1.113 2009/07/29 20:56:18 tgl Exp $
|
||||||
*
|
*
|
||||||
* NOTES
|
* NOTES
|
||||||
* This file contains only the public interface routines.
|
* This file contains only the public interface routines.
|
||||||
@ -165,7 +165,7 @@ hashinsert(PG_FUNCTION_ARGS)
|
|||||||
|
|
||||||
#ifdef NOT_USED
|
#ifdef NOT_USED
|
||||||
Relation heapRel = (Relation) PG_GETARG_POINTER(4);
|
Relation heapRel = (Relation) PG_GETARG_POINTER(4);
|
||||||
bool checkUnique = PG_GETARG_BOOL(5);
|
IndexUniqueCheck checkUnique = (IndexUniqueCheck) PG_GETARG_INT32(5);
|
||||||
#endif
|
#endif
|
||||||
IndexTuple itup;
|
IndexTuple itup;
|
||||||
|
|
||||||
@ -192,7 +192,7 @@ hashinsert(PG_FUNCTION_ARGS)
|
|||||||
|
|
||||||
pfree(itup);
|
pfree(itup);
|
||||||
|
|
||||||
PG_RETURN_BOOL(true);
|
PG_RETURN_BOOL(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/access/heap/tuptoaster.c,v 1.94 2009/07/22 01:21:22 tgl Exp $
|
* $PostgreSQL: pgsql/src/backend/access/heap/tuptoaster.c,v 1.95 2009/07/29 20:56:18 tgl Exp $
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* INTERFACE ROUTINES
|
* INTERFACE ROUTINES
|
||||||
@ -1229,7 +1229,9 @@ toast_save_datum(Relation rel, Datum value, int options)
|
|||||||
*/
|
*/
|
||||||
index_insert(toastidx, t_values, t_isnull,
|
index_insert(toastidx, t_values, t_isnull,
|
||||||
&(toasttup->t_self),
|
&(toasttup->t_self),
|
||||||
toastrel, toastidx->rd_index->indisunique);
|
toastrel,
|
||||||
|
toastidx->rd_index->indisunique ?
|
||||||
|
UNIQUE_CHECK_YES : UNIQUE_CHECK_NO);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Free memory
|
* Free memory
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/access/index/indexam.c,v 1.114 2009/06/11 14:48:54 momjian Exp $
|
* $PostgreSQL: pgsql/src/backend/access/index/indexam.c,v 1.115 2009/07/29 20:56:18 tgl Exp $
|
||||||
*
|
*
|
||||||
* INTERFACE ROUTINES
|
* INTERFACE ROUTINES
|
||||||
* index_open - open an index relation by relation OID
|
* index_open - open an index relation by relation OID
|
||||||
@ -185,7 +185,7 @@ index_insert(Relation indexRelation,
|
|||||||
bool *isnull,
|
bool *isnull,
|
||||||
ItemPointer heap_t_ctid,
|
ItemPointer heap_t_ctid,
|
||||||
Relation heapRelation,
|
Relation heapRelation,
|
||||||
bool check_uniqueness)
|
IndexUniqueCheck checkUnique)
|
||||||
{
|
{
|
||||||
FmgrInfo *procedure;
|
FmgrInfo *procedure;
|
||||||
|
|
||||||
@ -201,7 +201,7 @@ index_insert(Relation indexRelation,
|
|||||||
PointerGetDatum(isnull),
|
PointerGetDatum(isnull),
|
||||||
PointerGetDatum(heap_t_ctid),
|
PointerGetDatum(heap_t_ctid),
|
||||||
PointerGetDatum(heapRelation),
|
PointerGetDatum(heapRelation),
|
||||||
BoolGetDatum(check_uniqueness)));
|
Int32GetDatum((int32) checkUnique)));
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/access/nbtree/nbtinsert.c,v 1.170 2009/06/11 14:48:54 momjian Exp $
|
* $PostgreSQL: pgsql/src/backend/access/nbtree/nbtinsert.c,v 1.171 2009/07/29 20:56:18 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -49,8 +49,9 @@ typedef struct
|
|||||||
static Buffer _bt_newroot(Relation rel, Buffer lbuf, Buffer rbuf);
|
static Buffer _bt_newroot(Relation rel, Buffer lbuf, Buffer rbuf);
|
||||||
|
|
||||||
static TransactionId _bt_check_unique(Relation rel, IndexTuple itup,
|
static TransactionId _bt_check_unique(Relation rel, IndexTuple itup,
|
||||||
Relation heapRel, Buffer buf, OffsetNumber ioffset,
|
Relation heapRel, Buffer buf, OffsetNumber offset,
|
||||||
ScanKey itup_scankey);
|
ScanKey itup_scankey,
|
||||||
|
IndexUniqueCheck checkUnique, bool *is_unique);
|
||||||
static void _bt_findinsertloc(Relation rel,
|
static void _bt_findinsertloc(Relation rel,
|
||||||
Buffer *bufptr,
|
Buffer *bufptr,
|
||||||
OffsetNumber *offsetptr,
|
OffsetNumber *offsetptr,
|
||||||
@ -85,11 +86,24 @@ static void _bt_vacuum_one_page(Relation rel, Buffer buffer);
|
|||||||
*
|
*
|
||||||
* This routine is called by the public interface routines, btbuild
|
* This routine is called by the public interface routines, btbuild
|
||||||
* and btinsert. By here, itup is filled in, including the TID.
|
* and btinsert. By here, itup is filled in, including the TID.
|
||||||
|
*
|
||||||
|
* If checkUnique is UNIQUE_CHECK_NO or UNIQUE_CHECK_PARTIAL, this
|
||||||
|
* will allow duplicates. Otherwise (UNIQUE_CHECK_YES or
|
||||||
|
* UNIQUE_CHECK_EXISTING) it will throw error for a duplicate.
|
||||||
|
* For UNIQUE_CHECK_EXISTING we merely run the duplicate check, and
|
||||||
|
* don't actually insert.
|
||||||
|
*
|
||||||
|
* The result value is only significant for UNIQUE_CHECK_PARTIAL:
|
||||||
|
* it must be TRUE if the entry is known unique, else FALSE.
|
||||||
|
* (In the current implementation we'll also return TRUE after a
|
||||||
|
* successful UNIQUE_CHECK_YES or UNIQUE_CHECK_EXISTING call, but
|
||||||
|
* that's just a coding artifact.)
|
||||||
*/
|
*/
|
||||||
void
|
bool
|
||||||
_bt_doinsert(Relation rel, IndexTuple itup,
|
_bt_doinsert(Relation rel, IndexTuple itup,
|
||||||
bool index_is_unique, Relation heapRel)
|
IndexUniqueCheck checkUnique, Relation heapRel)
|
||||||
{
|
{
|
||||||
|
bool is_unique = false;
|
||||||
int natts = rel->rd_rel->relnatts;
|
int natts = rel->rd_rel->relnatts;
|
||||||
ScanKey itup_scankey;
|
ScanKey itup_scankey;
|
||||||
BTStack stack;
|
BTStack stack;
|
||||||
@ -134,13 +148,18 @@ top:
|
|||||||
*
|
*
|
||||||
* If we must wait for another xact, we release the lock while waiting,
|
* If we must wait for another xact, we release the lock while waiting,
|
||||||
* and then must start over completely.
|
* and then must start over completely.
|
||||||
|
*
|
||||||
|
* For a partial uniqueness check, we don't wait for the other xact.
|
||||||
|
* Just let the tuple in and return false for possibly non-unique,
|
||||||
|
* or true for definitely unique.
|
||||||
*/
|
*/
|
||||||
if (index_is_unique)
|
if (checkUnique != UNIQUE_CHECK_NO)
|
||||||
{
|
{
|
||||||
TransactionId xwait;
|
TransactionId xwait;
|
||||||
|
|
||||||
offset = _bt_binsrch(rel, buf, natts, itup_scankey, false);
|
offset = _bt_binsrch(rel, buf, natts, itup_scankey, false);
|
||||||
xwait = _bt_check_unique(rel, itup, heapRel, buf, offset, itup_scankey);
|
xwait = _bt_check_unique(rel, itup, heapRel, buf, offset, itup_scankey,
|
||||||
|
checkUnique, &is_unique);
|
||||||
|
|
||||||
if (TransactionIdIsValid(xwait))
|
if (TransactionIdIsValid(xwait))
|
||||||
{
|
{
|
||||||
@ -153,13 +172,23 @@ top:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (checkUnique != UNIQUE_CHECK_EXISTING)
|
||||||
|
{
|
||||||
/* do the insertion */
|
/* do the insertion */
|
||||||
_bt_findinsertloc(rel, &buf, &offset, natts, itup_scankey, itup);
|
_bt_findinsertloc(rel, &buf, &offset, natts, itup_scankey, itup);
|
||||||
_bt_insertonpg(rel, buf, stack, itup, offset, false);
|
_bt_insertonpg(rel, buf, stack, itup, offset, false);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* just release the buffer */
|
||||||
|
_bt_relbuf(rel, buf);
|
||||||
|
}
|
||||||
|
|
||||||
/* be tidy */
|
/* be tidy */
|
||||||
_bt_freestack(stack);
|
_bt_freestack(stack);
|
||||||
_bt_freeskey(itup_scankey);
|
_bt_freeskey(itup_scankey);
|
||||||
|
|
||||||
|
return is_unique;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -172,10 +201,16 @@ top:
|
|||||||
* Returns InvalidTransactionId if there is no conflict, else an xact ID
|
* Returns InvalidTransactionId if there is no conflict, else an xact ID
|
||||||
* we must wait for to see if it commits a conflicting tuple. If an actual
|
* we must wait for to see if it commits a conflicting tuple. If an actual
|
||||||
* conflict is detected, no return --- just ereport().
|
* conflict is detected, no return --- just ereport().
|
||||||
|
*
|
||||||
|
* However, if checkUnique == UNIQUE_CHECK_PARTIAL, we always return
|
||||||
|
* InvalidTransactionId because we don't want to wait. In this case we
|
||||||
|
* set *is_unique to false if there is a potential conflict, and the
|
||||||
|
* core code must redo the uniqueness check later.
|
||||||
*/
|
*/
|
||||||
static TransactionId
|
static TransactionId
|
||||||
_bt_check_unique(Relation rel, IndexTuple itup, Relation heapRel,
|
_bt_check_unique(Relation rel, IndexTuple itup, Relation heapRel,
|
||||||
Buffer buf, OffsetNumber offset, ScanKey itup_scankey)
|
Buffer buf, OffsetNumber offset, ScanKey itup_scankey,
|
||||||
|
IndexUniqueCheck checkUnique, bool *is_unique)
|
||||||
{
|
{
|
||||||
TupleDesc itupdesc = RelationGetDescr(rel);
|
TupleDesc itupdesc = RelationGetDescr(rel);
|
||||||
int natts = rel->rd_rel->relnatts;
|
int natts = rel->rd_rel->relnatts;
|
||||||
@ -184,6 +219,10 @@ _bt_check_unique(Relation rel, IndexTuple itup, Relation heapRel,
|
|||||||
Page page;
|
Page page;
|
||||||
BTPageOpaque opaque;
|
BTPageOpaque opaque;
|
||||||
Buffer nbuf = InvalidBuffer;
|
Buffer nbuf = InvalidBuffer;
|
||||||
|
bool found = false;
|
||||||
|
|
||||||
|
/* Assume unique until we find a duplicate */
|
||||||
|
*is_unique = true;
|
||||||
|
|
||||||
InitDirtySnapshot(SnapshotDirty);
|
InitDirtySnapshot(SnapshotDirty);
|
||||||
|
|
||||||
@ -240,22 +279,49 @@ _bt_check_unique(Relation rel, IndexTuple itup, Relation heapRel,
|
|||||||
curitup = (IndexTuple) PageGetItem(page, curitemid);
|
curitup = (IndexTuple) PageGetItem(page, curitemid);
|
||||||
htid = curitup->t_tid;
|
htid = curitup->t_tid;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If we are doing a recheck, we expect to find the tuple we
|
||||||
|
* are rechecking. It's not a duplicate, but we have to keep
|
||||||
|
* scanning.
|
||||||
|
*/
|
||||||
|
if (checkUnique == UNIQUE_CHECK_EXISTING &&
|
||||||
|
ItemPointerCompare(&htid, &itup->t_tid) == 0)
|
||||||
|
{
|
||||||
|
found = true;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We check the whole HOT-chain to see if there is any tuple
|
* We check the whole HOT-chain to see if there is any tuple
|
||||||
* that satisfies SnapshotDirty. This is necessary because we
|
* that satisfies SnapshotDirty. This is necessary because we
|
||||||
* have just a single index entry for the entire chain.
|
* have just a single index entry for the entire chain.
|
||||||
*/
|
*/
|
||||||
if (heap_hot_search(&htid, heapRel, &SnapshotDirty, &all_dead))
|
else if (heap_hot_search(&htid, heapRel, &SnapshotDirty,
|
||||||
|
&all_dead))
|
||||||
{
|
{
|
||||||
/* it is a duplicate */
|
TransactionId xwait;
|
||||||
TransactionId xwait =
|
|
||||||
(TransactionIdIsValid(SnapshotDirty.xmin)) ?
|
/*
|
||||||
SnapshotDirty.xmin : SnapshotDirty.xmax;
|
* It is a duplicate. If we are only doing a partial
|
||||||
|
* check, then don't bother checking if the tuple is
|
||||||
|
* being updated in another transaction. Just return
|
||||||
|
* the fact that it is a potential conflict and leave
|
||||||
|
* the full check till later.
|
||||||
|
*/
|
||||||
|
if (checkUnique == UNIQUE_CHECK_PARTIAL)
|
||||||
|
{
|
||||||
|
if (nbuf != InvalidBuffer)
|
||||||
|
_bt_relbuf(rel, nbuf);
|
||||||
|
*is_unique = false;
|
||||||
|
return InvalidTransactionId;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If this tuple is being updated by other transaction
|
* If this tuple is being updated by other transaction
|
||||||
* then we have to wait for its commit/abort.
|
* then we have to wait for its commit/abort.
|
||||||
*/
|
*/
|
||||||
|
xwait = (TransactionIdIsValid(SnapshotDirty.xmin)) ?
|
||||||
|
SnapshotDirty.xmin : SnapshotDirty.xmax;
|
||||||
|
|
||||||
if (TransactionIdIsValid(xwait))
|
if (TransactionIdIsValid(xwait))
|
||||||
{
|
{
|
||||||
if (nbuf != InvalidBuffer)
|
if (nbuf != InvalidBuffer)
|
||||||
@ -295,6 +361,9 @@ _bt_check_unique(Relation rel, IndexTuple itup, Relation heapRel,
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This is a definite conflict.
|
||||||
|
*/
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errcode(ERRCODE_UNIQUE_VIOLATION),
|
(errcode(ERRCODE_UNIQUE_VIOLATION),
|
||||||
errmsg("duplicate key value violates unique constraint \"%s\"",
|
errmsg("duplicate key value violates unique constraint \"%s\"",
|
||||||
@ -349,6 +418,18 @@ _bt_check_unique(Relation rel, IndexTuple itup, Relation heapRel,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If we are doing a recheck then we should have found the tuple we
|
||||||
|
* are checking. Otherwise there's something very wrong --- probably,
|
||||||
|
* the index is on a non-immutable expression.
|
||||||
|
*/
|
||||||
|
if (checkUnique == UNIQUE_CHECK_EXISTING && !found)
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_INTERNAL_ERROR),
|
||||||
|
errmsg("failed to re-find tuple within index \"%s\"",
|
||||||
|
RelationGetRelationName(rel)),
|
||||||
|
errhint("This may be because of a non-immutable index expression.")));
|
||||||
|
|
||||||
if (nbuf != InvalidBuffer)
|
if (nbuf != InvalidBuffer)
|
||||||
_bt_relbuf(rel, nbuf);
|
_bt_relbuf(rel, nbuf);
|
||||||
|
|
||||||
|
@ -12,7 +12,7 @@
|
|||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/access/nbtree/nbtree.c,v 1.171 2009/06/11 14:48:54 momjian Exp $
|
* $PostgreSQL: pgsql/src/backend/access/nbtree/nbtree.c,v 1.172 2009/07/29 20:56:18 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -217,18 +217,19 @@ btinsert(PG_FUNCTION_ARGS)
|
|||||||
bool *isnull = (bool *) PG_GETARG_POINTER(2);
|
bool *isnull = (bool *) PG_GETARG_POINTER(2);
|
||||||
ItemPointer ht_ctid = (ItemPointer) PG_GETARG_POINTER(3);
|
ItemPointer ht_ctid = (ItemPointer) PG_GETARG_POINTER(3);
|
||||||
Relation heapRel = (Relation) PG_GETARG_POINTER(4);
|
Relation heapRel = (Relation) PG_GETARG_POINTER(4);
|
||||||
bool checkUnique = PG_GETARG_BOOL(5);
|
IndexUniqueCheck checkUnique = (IndexUniqueCheck) PG_GETARG_INT32(5);
|
||||||
|
bool result;
|
||||||
IndexTuple itup;
|
IndexTuple itup;
|
||||||
|
|
||||||
/* generate an index tuple */
|
/* generate an index tuple */
|
||||||
itup = index_form_tuple(RelationGetDescr(rel), values, isnull);
|
itup = index_form_tuple(RelationGetDescr(rel), values, isnull);
|
||||||
itup->t_tid = *ht_ctid;
|
itup->t_tid = *ht_ctid;
|
||||||
|
|
||||||
_bt_doinsert(rel, itup, checkUnique, heapRel);
|
result = _bt_doinsert(rel, itup, checkUnique, heapRel);
|
||||||
|
|
||||||
pfree(itup);
|
pfree(itup);
|
||||||
|
|
||||||
PG_RETURN_BOOL(true);
|
PG_RETURN_BOOL(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/bootstrap/bootparse.y,v 1.96 2009/01/01 17:23:36 momjian Exp $
|
* $PostgreSQL: pgsql/src/backend/bootstrap/bootparse.y,v 1.97 2009/07/29 20:56:18 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -266,7 +266,7 @@ Boot_DeclareIndexStmt:
|
|||||||
NULL,
|
NULL,
|
||||||
$10,
|
$10,
|
||||||
NULL, NIL,
|
NULL, NIL,
|
||||||
false, false, false,
|
false, false, false, false, false,
|
||||||
false, false, true, false, false);
|
false, false, true, false, false);
|
||||||
do_end();
|
do_end();
|
||||||
}
|
}
|
||||||
@ -284,7 +284,7 @@ Boot_DeclareUniqueIndexStmt:
|
|||||||
NULL,
|
NULL,
|
||||||
$11,
|
$11,
|
||||||
NULL, NIL,
|
NULL, NIL,
|
||||||
true, false, false,
|
true, false, false, false, false,
|
||||||
false, false, true, false, false);
|
false, false, true, false, false);
|
||||||
do_end();
|
do_end();
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/catalog/index.c,v 1.319 2009/07/28 02:56:29 tgl Exp $
|
* $PostgreSQL: pgsql/src/backend/catalog/index.c,v 1.320 2009/07/29 20:56:18 tgl Exp $
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* INTERFACE ROUTINES
|
* INTERFACE ROUTINES
|
||||||
@ -40,14 +40,18 @@
|
|||||||
#include "catalog/pg_operator.h"
|
#include "catalog/pg_operator.h"
|
||||||
#include "catalog/pg_opclass.h"
|
#include "catalog/pg_opclass.h"
|
||||||
#include "catalog/pg_tablespace.h"
|
#include "catalog/pg_tablespace.h"
|
||||||
|
#include "catalog/pg_trigger.h"
|
||||||
#include "catalog/pg_type.h"
|
#include "catalog/pg_type.h"
|
||||||
#include "catalog/storage.h"
|
#include "catalog/storage.h"
|
||||||
#include "commands/tablecmds.h"
|
#include "commands/tablecmds.h"
|
||||||
|
#include "commands/trigger.h"
|
||||||
#include "executor/executor.h"
|
#include "executor/executor.h"
|
||||||
#include "miscadmin.h"
|
#include "miscadmin.h"
|
||||||
|
#include "nodes/makefuncs.h"
|
||||||
#include "nodes/nodeFuncs.h"
|
#include "nodes/nodeFuncs.h"
|
||||||
#include "optimizer/clauses.h"
|
#include "optimizer/clauses.h"
|
||||||
#include "optimizer/var.h"
|
#include "optimizer/var.h"
|
||||||
|
#include "parser/parser.h"
|
||||||
#include "storage/bufmgr.h"
|
#include "storage/bufmgr.h"
|
||||||
#include "storage/lmgr.h"
|
#include "storage/lmgr.h"
|
||||||
#include "storage/procarray.h"
|
#include "storage/procarray.h"
|
||||||
@ -87,6 +91,7 @@ static void UpdateIndexRelation(Oid indexoid, Oid heapoid,
|
|||||||
Oid *classOids,
|
Oid *classOids,
|
||||||
int16 *coloptions,
|
int16 *coloptions,
|
||||||
bool primary,
|
bool primary,
|
||||||
|
bool immediate,
|
||||||
bool isvalid);
|
bool isvalid);
|
||||||
static void index_update_stats(Relation rel, bool hasindex, bool isprimary,
|
static void index_update_stats(Relation rel, bool hasindex, bool isprimary,
|
||||||
Oid reltoastidxid, double reltuples);
|
Oid reltoastidxid, double reltuples);
|
||||||
@ -372,6 +377,7 @@ UpdateIndexRelation(Oid indexoid,
|
|||||||
Oid *classOids,
|
Oid *classOids,
|
||||||
int16 *coloptions,
|
int16 *coloptions,
|
||||||
bool primary,
|
bool primary,
|
||||||
|
bool immediate,
|
||||||
bool isvalid)
|
bool isvalid)
|
||||||
{
|
{
|
||||||
int2vector *indkey;
|
int2vector *indkey;
|
||||||
@ -439,6 +445,7 @@ UpdateIndexRelation(Oid indexoid,
|
|||||||
values[Anum_pg_index_indnatts - 1] = Int16GetDatum(indexInfo->ii_NumIndexAttrs);
|
values[Anum_pg_index_indnatts - 1] = Int16GetDatum(indexInfo->ii_NumIndexAttrs);
|
||||||
values[Anum_pg_index_indisunique - 1] = BoolGetDatum(indexInfo->ii_Unique);
|
values[Anum_pg_index_indisunique - 1] = BoolGetDatum(indexInfo->ii_Unique);
|
||||||
values[Anum_pg_index_indisprimary - 1] = BoolGetDatum(primary);
|
values[Anum_pg_index_indisprimary - 1] = BoolGetDatum(primary);
|
||||||
|
values[Anum_pg_index_indimmediate - 1] = BoolGetDatum(immediate);
|
||||||
values[Anum_pg_index_indisclustered - 1] = BoolGetDatum(false);
|
values[Anum_pg_index_indisclustered - 1] = BoolGetDatum(false);
|
||||||
values[Anum_pg_index_indisvalid - 1] = BoolGetDatum(isvalid);
|
values[Anum_pg_index_indisvalid - 1] = BoolGetDatum(isvalid);
|
||||||
values[Anum_pg_index_indcheckxmin - 1] = BoolGetDatum(false);
|
values[Anum_pg_index_indcheckxmin - 1] = BoolGetDatum(false);
|
||||||
@ -488,6 +495,8 @@ UpdateIndexRelation(Oid indexoid,
|
|||||||
* reloptions: AM-specific options
|
* reloptions: AM-specific options
|
||||||
* isprimary: index is a PRIMARY KEY
|
* isprimary: index is a PRIMARY KEY
|
||||||
* isconstraint: index is owned by a PRIMARY KEY or UNIQUE constraint
|
* isconstraint: index is owned by a PRIMARY KEY or UNIQUE constraint
|
||||||
|
* deferrable: constraint is DEFERRABLE
|
||||||
|
* initdeferred: constraint is INITIALLY DEFERRED
|
||||||
* allow_system_table_mods: allow table to be a system catalog
|
* allow_system_table_mods: allow table to be a system catalog
|
||||||
* skip_build: true to skip the index_build() step for the moment; caller
|
* skip_build: true to skip the index_build() step for the moment; caller
|
||||||
* must do it later (typically via reindex_index())
|
* must do it later (typically via reindex_index())
|
||||||
@ -509,6 +518,8 @@ index_create(Oid heapRelationId,
|
|||||||
Datum reloptions,
|
Datum reloptions,
|
||||||
bool isprimary,
|
bool isprimary,
|
||||||
bool isconstraint,
|
bool isconstraint,
|
||||||
|
bool deferrable,
|
||||||
|
bool initdeferred,
|
||||||
bool allow_system_table_mods,
|
bool allow_system_table_mods,
|
||||||
bool skip_build,
|
bool skip_build,
|
||||||
bool concurrent)
|
bool concurrent)
|
||||||
@ -679,7 +690,9 @@ index_create(Oid heapRelationId,
|
|||||||
* ----------------
|
* ----------------
|
||||||
*/
|
*/
|
||||||
UpdateIndexRelation(indexRelationId, heapRelationId, indexInfo,
|
UpdateIndexRelation(indexRelationId, heapRelationId, indexInfo,
|
||||||
classObjectId, coloptions, isprimary, !concurrent);
|
classObjectId, coloptions, isprimary,
|
||||||
|
!deferrable,
|
||||||
|
!concurrent);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Register constraint and dependencies for the index.
|
* Register constraint and dependencies for the index.
|
||||||
@ -726,8 +739,8 @@ index_create(Oid heapRelationId,
|
|||||||
conOid = CreateConstraintEntry(indexRelationName,
|
conOid = CreateConstraintEntry(indexRelationName,
|
||||||
namespaceId,
|
namespaceId,
|
||||||
constraintType,
|
constraintType,
|
||||||
false, /* isDeferrable */
|
deferrable,
|
||||||
false, /* isDeferred */
|
initdeferred,
|
||||||
heapRelationId,
|
heapRelationId,
|
||||||
indexInfo->ii_KeyAttrNumbers,
|
indexInfo->ii_KeyAttrNumbers,
|
||||||
indexInfo->ii_NumIndexAttrs,
|
indexInfo->ii_NumIndexAttrs,
|
||||||
@ -753,6 +766,40 @@ index_create(Oid heapRelationId,
|
|||||||
referenced.objectSubId = 0;
|
referenced.objectSubId = 0;
|
||||||
|
|
||||||
recordDependencyOn(&myself, &referenced, DEPENDENCY_INTERNAL);
|
recordDependencyOn(&myself, &referenced, DEPENDENCY_INTERNAL);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If the constraint is deferrable, create the deferred uniqueness
|
||||||
|
* checking trigger. (The trigger will be given an internal
|
||||||
|
* dependency on the constraint by CreateTrigger, so there's no
|
||||||
|
* need to do anything more here.)
|
||||||
|
*/
|
||||||
|
if (deferrable)
|
||||||
|
{
|
||||||
|
RangeVar *heapRel;
|
||||||
|
CreateTrigStmt *trigger;
|
||||||
|
|
||||||
|
heapRel = makeRangeVar(get_namespace_name(namespaceId),
|
||||||
|
pstrdup(RelationGetRelationName(heapRelation)),
|
||||||
|
-1);
|
||||||
|
|
||||||
|
trigger = makeNode(CreateTrigStmt);
|
||||||
|
trigger->trigname = pstrdup(indexRelationName);
|
||||||
|
trigger->relation = heapRel;
|
||||||
|
trigger->funcname = SystemFuncName("unique_key_recheck");
|
||||||
|
trigger->args = NIL;
|
||||||
|
trigger->before = false;
|
||||||
|
trigger->row = true;
|
||||||
|
trigger->events = TRIGGER_TYPE_INSERT | TRIGGER_TYPE_UPDATE;
|
||||||
|
trigger->isconstraint = true;
|
||||||
|
trigger->deferrable = true;
|
||||||
|
trigger->initdeferred = initdeferred;
|
||||||
|
trigger->constrrel = NULL;
|
||||||
|
|
||||||
|
(void) CreateTrigger(trigger, conOid, indexRelationId,
|
||||||
|
isprimary ? "PK_ConstraintTrigger" :
|
||||||
|
"Unique_ConstraintTrigger",
|
||||||
|
false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -791,6 +838,10 @@ index_create(Oid heapRelationId,
|
|||||||
|
|
||||||
recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO);
|
recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Non-constraint indexes can't be deferrable */
|
||||||
|
Assert(!deferrable);
|
||||||
|
Assert(!initdeferred);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Store dependency on operator classes */
|
/* Store dependency on operator classes */
|
||||||
@ -823,6 +874,13 @@ index_create(Oid heapRelationId,
|
|||||||
DEPENDENCY_AUTO);
|
DEPENDENCY_AUTO);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* Bootstrap mode - assert we weren't asked for constraint support */
|
||||||
|
Assert(!isconstraint);
|
||||||
|
Assert(!deferrable);
|
||||||
|
Assert(!initdeferred);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Advance the command counter so that we can see the newly-entered
|
* Advance the command counter so that we can see the newly-entered
|
||||||
@ -2190,7 +2248,8 @@ validate_index_heapscan(Relation heapRelation,
|
|||||||
isnull,
|
isnull,
|
||||||
&rootTuple,
|
&rootTuple,
|
||||||
heapRelation,
|
heapRelation,
|
||||||
indexInfo->ii_Unique);
|
indexInfo->ii_Unique ?
|
||||||
|
UNIQUE_CHECK_YES : UNIQUE_CHECK_NO);
|
||||||
|
|
||||||
state->tups_inserted += 1;
|
state->tups_inserted += 1;
|
||||||
}
|
}
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/catalog/indexing.c,v 1.117 2009/01/01 17:23:37 momjian Exp $
|
* $PostgreSQL: pgsql/src/backend/catalog/indexing.c,v 1.118 2009/07/29 20:56:18 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -134,7 +134,8 @@ CatalogIndexInsert(CatalogIndexState indstate, HeapTuple heapTuple)
|
|||||||
isnull, /* is-null flags */
|
isnull, /* is-null flags */
|
||||||
&(heapTuple->t_self), /* tid of heap tuple */
|
&(heapTuple->t_self), /* tid of heap tuple */
|
||||||
heapRelation,
|
heapRelation,
|
||||||
relationDescs[i]->rd_index->indisunique);
|
relationDescs[i]->rd_index->indisunique ?
|
||||||
|
UNIQUE_CHECK_YES : UNIQUE_CHECK_NO);
|
||||||
}
|
}
|
||||||
|
|
||||||
ExecDropSingleTupleTableSlot(slot);
|
ExecDropSingleTupleTableSlot(slot);
|
||||||
|
@ -289,7 +289,7 @@ F695 Translation support NO
|
|||||||
F696 Additional translation documentation NO
|
F696 Additional translation documentation NO
|
||||||
F701 Referential update actions YES
|
F701 Referential update actions YES
|
||||||
F711 ALTER domain YES
|
F711 ALTER domain YES
|
||||||
F721 Deferrable constraints NO foreign keys only
|
F721 Deferrable constraints NO foreign and unique keys only
|
||||||
F731 INSERT column privileges YES
|
F731 INSERT column privileges YES
|
||||||
F741 Referential MATCH types NO no partial match yet
|
F741 Referential MATCH types NO no partial match yet
|
||||||
F751 View CHECK enhancements NO
|
F751 View CHECK enhancements NO
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/catalog/toasting.c,v 1.17 2009/06/11 20:46:11 tgl Exp $
|
* $PostgreSQL: pgsql/src/backend/catalog/toasting.c,v 1.18 2009/07/29 20:56:18 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -253,7 +253,8 @@ create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid,
|
|||||||
BTREE_AM_OID,
|
BTREE_AM_OID,
|
||||||
rel->rd_rel->reltablespace,
|
rel->rd_rel->reltablespace,
|
||||||
classObjectId, coloptions, (Datum) 0,
|
classObjectId, coloptions, (Datum) 0,
|
||||||
true, false, true, false, false);
|
true, false, false, false,
|
||||||
|
true, false, false);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Store the toast table's OID in the parent relation's pg_class row
|
* Store the toast table's OID in the parent relation's pg_class row
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
# Makefile for backend/commands
|
# Makefile for backend/commands
|
||||||
#
|
#
|
||||||
# IDENTIFICATION
|
# IDENTIFICATION
|
||||||
# $PostgreSQL: pgsql/src/backend/commands/Makefile,v 1.39 2008/12/19 16:25:17 petere Exp $
|
# $PostgreSQL: pgsql/src/backend/commands/Makefile,v 1.40 2009/07/29 20:56:18 tgl Exp $
|
||||||
#
|
#
|
||||||
#-------------------------------------------------------------------------
|
#-------------------------------------------------------------------------
|
||||||
|
|
||||||
@ -13,7 +13,7 @@ top_builddir = ../../..
|
|||||||
include $(top_builddir)/src/Makefile.global
|
include $(top_builddir)/src/Makefile.global
|
||||||
|
|
||||||
OBJS = aggregatecmds.o alter.o analyze.o async.o cluster.o comment.o \
|
OBJS = aggregatecmds.o alter.o analyze.o async.o cluster.o comment.o \
|
||||||
conversioncmds.o copy.o \
|
constraint.o conversioncmds.o copy.o \
|
||||||
dbcommands.o define.o discard.o explain.o foreigncmds.o functioncmds.o \
|
dbcommands.o define.o discard.o explain.o foreigncmds.o functioncmds.o \
|
||||||
indexcmds.o lockcmds.o operatorcmds.o opclasscmds.o \
|
indexcmds.o lockcmds.o operatorcmds.o opclasscmds.o \
|
||||||
portalcmds.o prepare.o proclang.o \
|
portalcmds.o prepare.o proclang.o \
|
||||||
|
166
src/backend/commands/constraint.c
Normal file
166
src/backend/commands/constraint.c
Normal file
@ -0,0 +1,166 @@
|
|||||||
|
/*-------------------------------------------------------------------------
|
||||||
|
*
|
||||||
|
* constraint.c
|
||||||
|
* PostgreSQL CONSTRAINT support code.
|
||||||
|
*
|
||||||
|
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
|
||||||
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
|
*
|
||||||
|
* IDENTIFICATION
|
||||||
|
* $PostgreSQL: pgsql/src/backend/commands/constraint.c,v 1.1 2009/07/29 20:56:18 tgl Exp $
|
||||||
|
*
|
||||||
|
*-------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
#include "postgres.h"
|
||||||
|
|
||||||
|
#include "catalog/index.h"
|
||||||
|
#include "commands/trigger.h"
|
||||||
|
#include "executor/executor.h"
|
||||||
|
#include "utils/builtins.h"
|
||||||
|
#include "utils/tqual.h"
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* unique_key_recheck - trigger function to do a deferred uniqueness check.
|
||||||
|
*
|
||||||
|
* This is invoked as an AFTER ROW trigger for both INSERT and UPDATE,
|
||||||
|
* for any rows recorded as potentially violating a deferrable unique
|
||||||
|
* constraint.
|
||||||
|
*
|
||||||
|
* This may be an end-of-statement check, a commit-time check, or a
|
||||||
|
* check triggered by a SET CONSTRAINTS command.
|
||||||
|
*/
|
||||||
|
Datum
|
||||||
|
unique_key_recheck(PG_FUNCTION_ARGS)
|
||||||
|
{
|
||||||
|
TriggerData *trigdata = (TriggerData *) fcinfo->context;
|
||||||
|
const char *funcname = "unique_key_recheck";
|
||||||
|
HeapTuple new_row;
|
||||||
|
ItemPointerData tmptid;
|
||||||
|
Relation indexRel;
|
||||||
|
IndexInfo *indexInfo;
|
||||||
|
EState *estate;
|
||||||
|
ExprContext *econtext;
|
||||||
|
TupleTableSlot *slot;
|
||||||
|
Datum values[INDEX_MAX_KEYS];
|
||||||
|
bool isnull[INDEX_MAX_KEYS];
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Make sure this is being called as an AFTER ROW trigger. Note:
|
||||||
|
* translatable error strings are shared with ri_triggers.c, so
|
||||||
|
* resist the temptation to fold the function name into them.
|
||||||
|
*/
|
||||||
|
if (!CALLED_AS_TRIGGER(fcinfo))
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_E_R_I_E_TRIGGER_PROTOCOL_VIOLATED),
|
||||||
|
errmsg("function \"%s\" was not called by trigger manager",
|
||||||
|
funcname)));
|
||||||
|
|
||||||
|
if (!TRIGGER_FIRED_AFTER(trigdata->tg_event) ||
|
||||||
|
!TRIGGER_FIRED_FOR_ROW(trigdata->tg_event))
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_E_R_I_E_TRIGGER_PROTOCOL_VIOLATED),
|
||||||
|
errmsg("function \"%s\" must be fired AFTER ROW",
|
||||||
|
funcname)));
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Get the new data that was inserted/updated.
|
||||||
|
*/
|
||||||
|
if (TRIGGER_FIRED_BY_INSERT(trigdata->tg_event))
|
||||||
|
new_row = trigdata->tg_trigtuple;
|
||||||
|
else if (TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event))
|
||||||
|
new_row = trigdata->tg_newtuple;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_E_R_I_E_TRIGGER_PROTOCOL_VIOLATED),
|
||||||
|
errmsg("function \"%s\" must be fired for INSERT or UPDATE",
|
||||||
|
funcname)));
|
||||||
|
new_row = NULL; /* keep compiler quiet */
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If the new_row is now dead (ie, inserted and then deleted within our
|
||||||
|
* transaction), we can skip the check. However, we have to be careful,
|
||||||
|
* because this trigger gets queued only in response to index insertions;
|
||||||
|
* which means it does not get queued for HOT updates. The row we are
|
||||||
|
* called for might now be dead, but have a live HOT child, in which case
|
||||||
|
* we still need to make the uniqueness check. Therefore we have to use
|
||||||
|
* heap_hot_search, not just HeapTupleSatisfiesVisibility as is done in
|
||||||
|
* the comparable test in RI_FKey_check.
|
||||||
|
*
|
||||||
|
* This might look like just an optimization, because the index AM will
|
||||||
|
* make this identical test before throwing an error. But it's actually
|
||||||
|
* needed for correctness, because the index AM will also throw an error
|
||||||
|
* if it doesn't find the index entry for the row. If the row's dead then
|
||||||
|
* it's possible the index entry has also been marked dead, and even
|
||||||
|
* removed.
|
||||||
|
*/
|
||||||
|
tmptid = new_row->t_self;
|
||||||
|
if (!heap_hot_search(&tmptid, trigdata->tg_relation, SnapshotSelf, NULL))
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* All rows in the HOT chain are dead, so skip the check.
|
||||||
|
*/
|
||||||
|
return PointerGetDatum(NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Open the index, acquiring a RowExclusiveLock, just as if we were
|
||||||
|
* going to update it. (This protects against possible changes of the
|
||||||
|
* index schema, not against concurrent updates.)
|
||||||
|
*/
|
||||||
|
indexRel = index_open(trigdata->tg_trigger->tgconstrindid,
|
||||||
|
RowExclusiveLock);
|
||||||
|
indexInfo = BuildIndexInfo(indexRel);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The heap tuple must be put into a slot for FormIndexDatum.
|
||||||
|
*/
|
||||||
|
slot = MakeSingleTupleTableSlot(RelationGetDescr(trigdata->tg_relation));
|
||||||
|
|
||||||
|
ExecStoreTuple(new_row, slot, InvalidBuffer, false);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Typically the index won't have expressions, but if it does we need
|
||||||
|
* an EState to evaluate them.
|
||||||
|
*/
|
||||||
|
if (indexInfo->ii_Expressions != NIL)
|
||||||
|
{
|
||||||
|
estate = CreateExecutorState();
|
||||||
|
econtext = GetPerTupleExprContext(estate);
|
||||||
|
econtext->ecxt_scantuple = slot;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
estate = NULL;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Form the index values and isnull flags for the index entry that
|
||||||
|
* we need to check.
|
||||||
|
*
|
||||||
|
* Note: if the index uses functions that are not as immutable as they
|
||||||
|
* are supposed to be, this could produce an index tuple different from
|
||||||
|
* the original. The index AM can catch such errors by verifying that
|
||||||
|
* it finds a matching index entry with the tuple's TID.
|
||||||
|
*/
|
||||||
|
FormIndexDatum(indexInfo, slot, estate, values, isnull);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Now do the uniqueness check. This is not a real insert; it is a
|
||||||
|
* check that the index entry that has already been inserted is unique.
|
||||||
|
*/
|
||||||
|
index_insert(indexRel, values, isnull, &(new_row->t_self),
|
||||||
|
trigdata->tg_relation, UNIQUE_CHECK_EXISTING);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If that worked, then this index entry is unique, and we are done.
|
||||||
|
*/
|
||||||
|
if (estate != NULL)
|
||||||
|
FreeExecutorState(estate);
|
||||||
|
|
||||||
|
ExecDropSingleTupleTableSlot(slot);
|
||||||
|
|
||||||
|
index_close(indexRel, RowExclusiveLock);
|
||||||
|
|
||||||
|
return PointerGetDatum(NULL);
|
||||||
|
}
|
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/commands/copy.c,v 1.315 2009/07/25 17:04:19 tgl Exp $
|
* $PostgreSQL: pgsql/src/backend/commands/copy.c,v 1.316 2009/07/29 20:56:18 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -2130,6 +2130,8 @@ CopyFrom(CopyState cstate)
|
|||||||
|
|
||||||
if (!skip_tuple)
|
if (!skip_tuple)
|
||||||
{
|
{
|
||||||
|
List *recheckIndexes = NIL;
|
||||||
|
|
||||||
/* Place tuple in tuple slot */
|
/* Place tuple in tuple slot */
|
||||||
ExecStoreTuple(tuple, slot, InvalidBuffer, false);
|
ExecStoreTuple(tuple, slot, InvalidBuffer, false);
|
||||||
|
|
||||||
@ -2141,10 +2143,12 @@ CopyFrom(CopyState cstate)
|
|||||||
heap_insert(cstate->rel, tuple, mycid, hi_options, bistate);
|
heap_insert(cstate->rel, tuple, mycid, hi_options, bistate);
|
||||||
|
|
||||||
if (resultRelInfo->ri_NumIndices > 0)
|
if (resultRelInfo->ri_NumIndices > 0)
|
||||||
ExecInsertIndexTuples(slot, &(tuple->t_self), estate, false);
|
recheckIndexes = ExecInsertIndexTuples(slot, &(tuple->t_self),
|
||||||
|
estate, false);
|
||||||
|
|
||||||
/* AFTER ROW INSERT Triggers */
|
/* AFTER ROW INSERT Triggers */
|
||||||
ExecARInsertTriggers(estate, resultRelInfo, tuple);
|
ExecARInsertTriggers(estate, resultRelInfo, tuple,
|
||||||
|
recheckIndexes);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We count only tuples not suppressed by a BEFORE INSERT trigger;
|
* We count only tuples not suppressed by a BEFORE INSERT trigger;
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/commands/indexcmds.c,v 1.186 2009/07/16 06:33:42 petere Exp $
|
* $PostgreSQL: pgsql/src/backend/commands/indexcmds.c,v 1.187 2009/07/29 20:56:18 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -87,6 +87,8 @@ static bool relationHasPrimaryKey(Relation rel);
|
|||||||
* 'primary': mark the index as a primary key in the catalogs.
|
* 'primary': mark the index as a primary key in the catalogs.
|
||||||
* 'isconstraint': index is for a PRIMARY KEY or UNIQUE constraint,
|
* 'isconstraint': index is for a PRIMARY KEY or UNIQUE constraint,
|
||||||
* so build a pg_constraint entry for it.
|
* so build a pg_constraint entry for it.
|
||||||
|
* 'deferrable': constraint is DEFERRABLE.
|
||||||
|
* 'initdeferred': constraint is INITIALLY DEFERRED.
|
||||||
* 'is_alter_table': this is due to an ALTER rather than a CREATE operation.
|
* 'is_alter_table': this is due to an ALTER rather than a CREATE operation.
|
||||||
* 'check_rights': check for CREATE rights in the namespace. (This should
|
* 'check_rights': check for CREATE rights in the namespace. (This should
|
||||||
* be true except when ALTER is deleting/recreating an index.)
|
* be true except when ALTER is deleting/recreating an index.)
|
||||||
@ -107,6 +109,8 @@ DefineIndex(RangeVar *heapRelation,
|
|||||||
bool unique,
|
bool unique,
|
||||||
bool primary,
|
bool primary,
|
||||||
bool isconstraint,
|
bool isconstraint,
|
||||||
|
bool deferrable,
|
||||||
|
bool initdeferred,
|
||||||
bool is_alter_table,
|
bool is_alter_table,
|
||||||
bool check_rights,
|
bool check_rights,
|
||||||
bool skip_build,
|
bool skip_build,
|
||||||
@ -447,7 +451,8 @@ DefineIndex(RangeVar *heapRelation,
|
|||||||
indexRelationId =
|
indexRelationId =
|
||||||
index_create(relationId, indexRelationName, indexRelationId,
|
index_create(relationId, indexRelationName, indexRelationId,
|
||||||
indexInfo, accessMethodId, tablespaceId, classObjectId,
|
indexInfo, accessMethodId, tablespaceId, classObjectId,
|
||||||
coloptions, reloptions, primary, isconstraint,
|
coloptions, reloptions, primary,
|
||||||
|
isconstraint, deferrable, initdeferred,
|
||||||
allowSystemTableMods, skip_build, concurrent);
|
allowSystemTableMods, skip_build, concurrent);
|
||||||
|
|
||||||
return; /* We're done, in the standard case */
|
return; /* We're done, in the standard case */
|
||||||
@ -465,7 +470,8 @@ DefineIndex(RangeVar *heapRelation,
|
|||||||
indexRelationId =
|
indexRelationId =
|
||||||
index_create(relationId, indexRelationName, indexRelationId,
|
index_create(relationId, indexRelationName, indexRelationId,
|
||||||
indexInfo, accessMethodId, tablespaceId, classObjectId,
|
indexInfo, accessMethodId, tablespaceId, classObjectId,
|
||||||
coloptions, reloptions, primary, isconstraint,
|
coloptions, reloptions, primary,
|
||||||
|
isconstraint, deferrable, initdeferred,
|
||||||
allowSystemTableMods, true, concurrent);
|
allowSystemTableMods, true, concurrent);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.292 2009/07/28 02:56:30 tgl Exp $
|
* $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.293 2009/07/29 20:56:18 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -4391,6 +4391,8 @@ ATExecAddIndex(AlteredTableInfo *tab, Relation rel,
|
|||||||
stmt->unique,
|
stmt->unique,
|
||||||
stmt->primary,
|
stmt->primary,
|
||||||
stmt->isconstraint,
|
stmt->isconstraint,
|
||||||
|
stmt->deferrable,
|
||||||
|
stmt->initdeferred,
|
||||||
true, /* is_alter_table */
|
true, /* is_alter_table */
|
||||||
check_rights,
|
check_rights,
|
||||||
skip_build,
|
skip_build,
|
||||||
@ -4955,6 +4957,17 @@ transformFkeyGetPrimaryKey(Relation pkrel, Oid *indexOid,
|
|||||||
indexStruct = (Form_pg_index) GETSTRUCT(indexTuple);
|
indexStruct = (Form_pg_index) GETSTRUCT(indexTuple);
|
||||||
if (indexStruct->indisprimary)
|
if (indexStruct->indisprimary)
|
||||||
{
|
{
|
||||||
|
/*
|
||||||
|
* Refuse to use a deferrable primary key. This is per SQL spec,
|
||||||
|
* and there would be a lot of interesting semantic problems if
|
||||||
|
* we tried to allow it.
|
||||||
|
*/
|
||||||
|
if (!indexStruct->indimmediate)
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
|
||||||
|
errmsg("cannot use a deferrable primary key for referenced table \"%s\"",
|
||||||
|
RelationGetRelationName(pkrel))));
|
||||||
|
|
||||||
*indexOid = indexoid;
|
*indexOid = indexoid;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -5040,11 +5053,12 @@ transformFkeyCheckAttrs(Relation pkrel,
|
|||||||
indexStruct = (Form_pg_index) GETSTRUCT(indexTuple);
|
indexStruct = (Form_pg_index) GETSTRUCT(indexTuple);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Must have the right number of columns; must be unique and not a
|
* Must have the right number of columns; must be unique (non
|
||||||
* partial index; forget it if there are any expressions, too
|
* deferrable) and not a partial index; forget it if there are any
|
||||||
|
* expressions, too
|
||||||
*/
|
*/
|
||||||
if (indexStruct->indnatts == numattrs &&
|
if (indexStruct->indnatts == numattrs &&
|
||||||
indexStruct->indisunique &&
|
indexStruct->indisunique && indexStruct->indimmediate &&
|
||||||
heap_attisnull(indexTuple, Anum_pg_index_indpred) &&
|
heap_attisnull(indexTuple, Anum_pg_index_indpred) &&
|
||||||
heap_attisnull(indexTuple, Anum_pg_index_indexprs))
|
heap_attisnull(indexTuple, Anum_pg_index_indexprs))
|
||||||
{
|
{
|
||||||
@ -5243,7 +5257,8 @@ CreateFKCheckTrigger(RangeVar *myRel, FkConstraint *fkconstraint,
|
|||||||
fk_trigger->constrrel = fkconstraint->pktable;
|
fk_trigger->constrrel = fkconstraint->pktable;
|
||||||
fk_trigger->args = NIL;
|
fk_trigger->args = NIL;
|
||||||
|
|
||||||
(void) CreateTrigger(fk_trigger, constraintOid, indexOid, false);
|
(void) CreateTrigger(fk_trigger, constraintOid, indexOid,
|
||||||
|
"RI_ConstraintTrigger", false);
|
||||||
|
|
||||||
/* Make changes-so-far visible */
|
/* Make changes-so-far visible */
|
||||||
CommandCounterIncrement();
|
CommandCounterIncrement();
|
||||||
@ -5322,7 +5337,8 @@ createForeignKeyTriggers(Relation rel, FkConstraint *fkconstraint,
|
|||||||
}
|
}
|
||||||
fk_trigger->args = NIL;
|
fk_trigger->args = NIL;
|
||||||
|
|
||||||
(void) CreateTrigger(fk_trigger, constraintOid, indexOid, false);
|
(void) CreateTrigger(fk_trigger, constraintOid, indexOid,
|
||||||
|
"RI_ConstraintTrigger", false);
|
||||||
|
|
||||||
/* Make changes-so-far visible */
|
/* Make changes-so-far visible */
|
||||||
CommandCounterIncrement();
|
CommandCounterIncrement();
|
||||||
@ -5373,7 +5389,8 @@ createForeignKeyTriggers(Relation rel, FkConstraint *fkconstraint,
|
|||||||
}
|
}
|
||||||
fk_trigger->args = NIL;
|
fk_trigger->args = NIL;
|
||||||
|
|
||||||
(void) CreateTrigger(fk_trigger, constraintOid, indexOid, false);
|
(void) CreateTrigger(fk_trigger, constraintOid, indexOid,
|
||||||
|
"RI_ConstraintTrigger", false);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/commands/trigger.c,v 1.249 2009/07/28 02:56:30 tgl Exp $
|
* $PostgreSQL: pgsql/src/backend/commands/trigger.c,v 1.250 2009/07/29 20:56:18 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -63,7 +63,8 @@ static HeapTuple ExecCallTriggerFunc(TriggerData *trigdata,
|
|||||||
Instrumentation *instr,
|
Instrumentation *instr,
|
||||||
MemoryContext per_tuple_context);
|
MemoryContext per_tuple_context);
|
||||||
static void AfterTriggerSaveEvent(ResultRelInfo *relinfo, int event,
|
static void AfterTriggerSaveEvent(ResultRelInfo *relinfo, int event,
|
||||||
bool row_trigger, HeapTuple oldtup, HeapTuple newtup);
|
bool row_trigger, HeapTuple oldtup, HeapTuple newtup,
|
||||||
|
List *recheckIndexes);
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -77,6 +78,10 @@ static void AfterTriggerSaveEvent(ResultRelInfo *relinfo, int event,
|
|||||||
* indexOid, if nonzero, is the OID of an index associated with the constraint.
|
* indexOid, if nonzero, is the OID of an index associated with the constraint.
|
||||||
* We do nothing with this except store it into pg_trigger.tgconstrindid.
|
* We do nothing with this except store it into pg_trigger.tgconstrindid.
|
||||||
*
|
*
|
||||||
|
* prefix is NULL for user-created triggers. For internally generated
|
||||||
|
* constraint triggers, it is a prefix string to use in building the
|
||||||
|
* trigger name. (stmt->trigname is the constraint name in such cases.)
|
||||||
|
*
|
||||||
* If checkPermissions is true we require ACL_TRIGGER permissions on the
|
* If checkPermissions is true we require ACL_TRIGGER permissions on the
|
||||||
* relation. If not, the caller already checked permissions. (This is
|
* relation. If not, the caller already checked permissions. (This is
|
||||||
* currently redundant with constraintOid being zero, but it's clearer to
|
* currently redundant with constraintOid being zero, but it's clearer to
|
||||||
@ -87,7 +92,7 @@ static void AfterTriggerSaveEvent(ResultRelInfo *relinfo, int event,
|
|||||||
*/
|
*/
|
||||||
Oid
|
Oid
|
||||||
CreateTrigger(CreateTrigStmt *stmt,
|
CreateTrigger(CreateTrigStmt *stmt,
|
||||||
Oid constraintOid, Oid indexOid,
|
Oid constraintOid, Oid indexOid, const char *prefix,
|
||||||
bool checkPermissions)
|
bool checkPermissions)
|
||||||
{
|
{
|
||||||
int16 tgtype;
|
int16 tgtype;
|
||||||
@ -216,20 +221,20 @@ CreateTrigger(CreateTrigStmt *stmt,
|
|||||||
trigoid = GetNewOid(tgrel);
|
trigoid = GetNewOid(tgrel);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If trigger is for an RI constraint, the passed-in name is the
|
* If trigger is for a constraint, stmt->trigname is the constraint
|
||||||
* constraint name; save that and build a unique trigger name to avoid
|
* name; save that and build a unique trigger name based on the supplied
|
||||||
* collisions with user-selected trigger names.
|
* prefix, to avoid collisions with user-selected trigger names.
|
||||||
*/
|
*/
|
||||||
if (OidIsValid(constraintOid))
|
if (prefix != NULL)
|
||||||
{
|
{
|
||||||
snprintf(constrtrigname, sizeof(constrtrigname),
|
snprintf(constrtrigname, sizeof(constrtrigname),
|
||||||
"RI_ConstraintTrigger_%u", trigoid);
|
"%s_%u", prefix, trigoid);
|
||||||
trigname = constrtrigname;
|
trigname = constrtrigname;
|
||||||
constrname = stmt->trigname;
|
constrname = stmt->trigname;
|
||||||
}
|
}
|
||||||
else if (stmt->isconstraint)
|
else if (stmt->isconstraint)
|
||||||
{
|
{
|
||||||
/* constraint trigger: trigger name is also constraint name */
|
/* user constraint trigger: trigger name is also constraint name */
|
||||||
trigname = stmt->trigname;
|
trigname = stmt->trigname;
|
||||||
constrname = stmt->trigname;
|
constrname = stmt->trigname;
|
||||||
}
|
}
|
||||||
@ -1650,7 +1655,7 @@ ExecASInsertTriggers(EState *estate, ResultRelInfo *relinfo)
|
|||||||
|
|
||||||
if (trigdesc && trigdesc->n_after_statement[TRIGGER_EVENT_INSERT] > 0)
|
if (trigdesc && trigdesc->n_after_statement[TRIGGER_EVENT_INSERT] > 0)
|
||||||
AfterTriggerSaveEvent(relinfo, TRIGGER_EVENT_INSERT,
|
AfterTriggerSaveEvent(relinfo, TRIGGER_EVENT_INSERT,
|
||||||
false, NULL, NULL);
|
false, NULL, NULL, NIL);
|
||||||
}
|
}
|
||||||
|
|
||||||
HeapTuple
|
HeapTuple
|
||||||
@ -1706,13 +1711,13 @@ ExecBRInsertTriggers(EState *estate, ResultRelInfo *relinfo,
|
|||||||
|
|
||||||
void
|
void
|
||||||
ExecARInsertTriggers(EState *estate, ResultRelInfo *relinfo,
|
ExecARInsertTriggers(EState *estate, ResultRelInfo *relinfo,
|
||||||
HeapTuple trigtuple)
|
HeapTuple trigtuple, List *recheckIndexes)
|
||||||
{
|
{
|
||||||
TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
|
TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
|
||||||
|
|
||||||
if (trigdesc && trigdesc->n_after_row[TRIGGER_EVENT_INSERT] > 0)
|
if (trigdesc && trigdesc->n_after_row[TRIGGER_EVENT_INSERT] > 0)
|
||||||
AfterTriggerSaveEvent(relinfo, TRIGGER_EVENT_INSERT,
|
AfterTriggerSaveEvent(relinfo, TRIGGER_EVENT_INSERT,
|
||||||
true, NULL, trigtuple);
|
true, NULL, trigtuple, recheckIndexes);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@ -1781,7 +1786,7 @@ ExecASDeleteTriggers(EState *estate, ResultRelInfo *relinfo)
|
|||||||
|
|
||||||
if (trigdesc && trigdesc->n_after_statement[TRIGGER_EVENT_DELETE] > 0)
|
if (trigdesc && trigdesc->n_after_statement[TRIGGER_EVENT_DELETE] > 0)
|
||||||
AfterTriggerSaveEvent(relinfo, TRIGGER_EVENT_DELETE,
|
AfterTriggerSaveEvent(relinfo, TRIGGER_EVENT_DELETE,
|
||||||
false, NULL, NULL);
|
false, NULL, NULL, NIL);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
@ -1858,7 +1863,7 @@ ExecARDeleteTriggers(EState *estate, ResultRelInfo *relinfo,
|
|||||||
tupleid, NULL);
|
tupleid, NULL);
|
||||||
|
|
||||||
AfterTriggerSaveEvent(relinfo, TRIGGER_EVENT_DELETE,
|
AfterTriggerSaveEvent(relinfo, TRIGGER_EVENT_DELETE,
|
||||||
true, trigtuple, NULL);
|
true, trigtuple, NULL, NIL);
|
||||||
heap_freetuple(trigtuple);
|
heap_freetuple(trigtuple);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1929,7 +1934,7 @@ ExecASUpdateTriggers(EState *estate, ResultRelInfo *relinfo)
|
|||||||
|
|
||||||
if (trigdesc && trigdesc->n_after_statement[TRIGGER_EVENT_UPDATE] > 0)
|
if (trigdesc && trigdesc->n_after_statement[TRIGGER_EVENT_UPDATE] > 0)
|
||||||
AfterTriggerSaveEvent(relinfo, TRIGGER_EVENT_UPDATE,
|
AfterTriggerSaveEvent(relinfo, TRIGGER_EVENT_UPDATE,
|
||||||
false, NULL, NULL);
|
false, NULL, NULL, NIL);
|
||||||
}
|
}
|
||||||
|
|
||||||
HeapTuple
|
HeapTuple
|
||||||
@ -1999,7 +2004,8 @@ ExecBRUpdateTriggers(EState *estate, ResultRelInfo *relinfo,
|
|||||||
|
|
||||||
void
|
void
|
||||||
ExecARUpdateTriggers(EState *estate, ResultRelInfo *relinfo,
|
ExecARUpdateTriggers(EState *estate, ResultRelInfo *relinfo,
|
||||||
ItemPointer tupleid, HeapTuple newtuple)
|
ItemPointer tupleid, HeapTuple newtuple,
|
||||||
|
List *recheckIndexes)
|
||||||
{
|
{
|
||||||
TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
|
TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
|
||||||
|
|
||||||
@ -2009,7 +2015,7 @@ ExecARUpdateTriggers(EState *estate, ResultRelInfo *relinfo,
|
|||||||
tupleid, NULL);
|
tupleid, NULL);
|
||||||
|
|
||||||
AfterTriggerSaveEvent(relinfo, TRIGGER_EVENT_UPDATE,
|
AfterTriggerSaveEvent(relinfo, TRIGGER_EVENT_UPDATE,
|
||||||
true, trigtuple, newtuple);
|
true, trigtuple, newtuple, recheckIndexes);
|
||||||
heap_freetuple(trigtuple);
|
heap_freetuple(trigtuple);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2080,7 +2086,7 @@ ExecASTruncateTriggers(EState *estate, ResultRelInfo *relinfo)
|
|||||||
|
|
||||||
if (trigdesc && trigdesc->n_after_statement[TRIGGER_EVENT_TRUNCATE] > 0)
|
if (trigdesc && trigdesc->n_after_statement[TRIGGER_EVENT_TRUNCATE] > 0)
|
||||||
AfterTriggerSaveEvent(relinfo, TRIGGER_EVENT_TRUNCATE,
|
AfterTriggerSaveEvent(relinfo, TRIGGER_EVENT_TRUNCATE,
|
||||||
false, NULL, NULL);
|
false, NULL, NULL, NIL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -3793,15 +3799,18 @@ AfterTriggerPendingOnRel(Oid relid)
|
|||||||
/* ----------
|
/* ----------
|
||||||
* AfterTriggerSaveEvent()
|
* AfterTriggerSaveEvent()
|
||||||
*
|
*
|
||||||
* Called by ExecA[RS]...Triggers() to add the event to the queue.
|
* Called by ExecA[RS]...Triggers() to queue up the triggers that should
|
||||||
|
* be fired for an event.
|
||||||
*
|
*
|
||||||
* NOTE: should be called only if we've determined that an event must
|
* NOTE: this is called whenever there are any triggers associated with
|
||||||
* be added to the queue.
|
* the event (even if they are disabled). This function decides which
|
||||||
|
* triggers actually need to be queued.
|
||||||
* ----------
|
* ----------
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
AfterTriggerSaveEvent(ResultRelInfo *relinfo, int event, bool row_trigger,
|
AfterTriggerSaveEvent(ResultRelInfo *relinfo, int event, bool row_trigger,
|
||||||
HeapTuple oldtup, HeapTuple newtup)
|
HeapTuple oldtup, HeapTuple newtup,
|
||||||
|
List *recheckIndexes)
|
||||||
{
|
{
|
||||||
Relation rel = relinfo->ri_RelationDesc;
|
Relation rel = relinfo->ri_RelationDesc;
|
||||||
TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
|
TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
|
||||||
@ -3961,6 +3970,17 @@ AfterTriggerSaveEvent(ResultRelInfo *relinfo, int event, bool row_trigger,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If the trigger is a deferred unique constraint check trigger,
|
||||||
|
* only queue it if the unique constraint was potentially violated,
|
||||||
|
* which we know from index insertion time.
|
||||||
|
*/
|
||||||
|
if (trigger->tgfoid == F_UNIQUE_KEY_RECHECK)
|
||||||
|
{
|
||||||
|
if (!list_member_oid(recheckIndexes, trigger->tgconstrindid))
|
||||||
|
continue; /* Uniqueness definitely not violated */
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Fill in event structure and add it to the current query's queue.
|
* Fill in event structure and add it to the current query's queue.
|
||||||
*/
|
*/
|
||||||
|
@ -26,7 +26,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/executor/execMain.c,v 1.326 2009/06/11 20:46:11 tgl Exp $
|
* $PostgreSQL: pgsql/src/backend/executor/execMain.c,v 1.327 2009/07/29 20:56:18 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -1753,6 +1753,7 @@ ExecInsert(TupleTableSlot *slot,
|
|||||||
ResultRelInfo *resultRelInfo;
|
ResultRelInfo *resultRelInfo;
|
||||||
Relation resultRelationDesc;
|
Relation resultRelationDesc;
|
||||||
Oid newId;
|
Oid newId;
|
||||||
|
List *recheckIndexes = NIL;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* get the heap tuple out of the tuple table slot, making sure we have a
|
* get the heap tuple out of the tuple table slot, making sure we have a
|
||||||
@ -1834,10 +1835,11 @@ ExecInsert(TupleTableSlot *slot,
|
|||||||
* insert index entries for tuple
|
* insert index entries for tuple
|
||||||
*/
|
*/
|
||||||
if (resultRelInfo->ri_NumIndices > 0)
|
if (resultRelInfo->ri_NumIndices > 0)
|
||||||
ExecInsertIndexTuples(slot, &(tuple->t_self), estate, false);
|
recheckIndexes = ExecInsertIndexTuples(slot, &(tuple->t_self),
|
||||||
|
estate, false);
|
||||||
|
|
||||||
/* AFTER ROW INSERT Triggers */
|
/* AFTER ROW INSERT Triggers */
|
||||||
ExecARInsertTriggers(estate, resultRelInfo, tuple);
|
ExecARInsertTriggers(estate, resultRelInfo, tuple, recheckIndexes);
|
||||||
|
|
||||||
/* Process RETURNING if present */
|
/* Process RETURNING if present */
|
||||||
if (resultRelInfo->ri_projectReturning)
|
if (resultRelInfo->ri_projectReturning)
|
||||||
@ -1999,6 +2001,7 @@ ExecUpdate(TupleTableSlot *slot,
|
|||||||
HTSU_Result result;
|
HTSU_Result result;
|
||||||
ItemPointerData update_ctid;
|
ItemPointerData update_ctid;
|
||||||
TransactionId update_xmax;
|
TransactionId update_xmax;
|
||||||
|
List *recheckIndexes = NIL;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* abort the operation if not running transactions
|
* abort the operation if not running transactions
|
||||||
@ -2132,10 +2135,12 @@ lreplace:;
|
|||||||
* If it's a HOT update, we mustn't insert new index entries.
|
* If it's a HOT update, we mustn't insert new index entries.
|
||||||
*/
|
*/
|
||||||
if (resultRelInfo->ri_NumIndices > 0 && !HeapTupleIsHeapOnly(tuple))
|
if (resultRelInfo->ri_NumIndices > 0 && !HeapTupleIsHeapOnly(tuple))
|
||||||
ExecInsertIndexTuples(slot, &(tuple->t_self), estate, false);
|
recheckIndexes = ExecInsertIndexTuples(slot, &(tuple->t_self),
|
||||||
|
estate, false);
|
||||||
|
|
||||||
/* AFTER ROW UPDATE Triggers */
|
/* AFTER ROW UPDATE Triggers */
|
||||||
ExecARUpdateTriggers(estate, resultRelInfo, tupleid, tuple);
|
ExecARUpdateTriggers(estate, resultRelInfo, tupleid, tuple,
|
||||||
|
recheckIndexes);
|
||||||
|
|
||||||
/* Process RETURNING if present */
|
/* Process RETURNING if present */
|
||||||
if (resultRelInfo->ri_projectReturning)
|
if (resultRelInfo->ri_projectReturning)
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/executor/execUtils.c,v 1.160 2009/07/18 19:15:41 tgl Exp $
|
* $PostgreSQL: pgsql/src/backend/executor/execUtils.c,v 1.161 2009/07/29 20:56:18 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -1033,17 +1033,22 @@ ExecCloseIndices(ResultRelInfo *resultRelInfo)
|
|||||||
* doesn't provide the functionality needed by the
|
* doesn't provide the functionality needed by the
|
||||||
* executor.. -cim 9/27/89
|
* executor.. -cim 9/27/89
|
||||||
*
|
*
|
||||||
|
* This returns a list of OIDs for any unique indexes
|
||||||
|
* whose constraint check was deferred and which had
|
||||||
|
* potential (unconfirmed) conflicts.
|
||||||
|
*
|
||||||
* CAUTION: this must not be called for a HOT update.
|
* CAUTION: this must not be called for a HOT update.
|
||||||
* We can't defend against that here for lack of info.
|
* We can't defend against that here for lack of info.
|
||||||
* Should we change the API to make it safer?
|
* Should we change the API to make it safer?
|
||||||
* ----------------------------------------------------------------
|
* ----------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
void
|
List *
|
||||||
ExecInsertIndexTuples(TupleTableSlot *slot,
|
ExecInsertIndexTuples(TupleTableSlot *slot,
|
||||||
ItemPointer tupleid,
|
ItemPointer tupleid,
|
||||||
EState *estate,
|
EState *estate,
|
||||||
bool is_vacuum)
|
bool is_vacuum_full)
|
||||||
{
|
{
|
||||||
|
List *result = NIL;
|
||||||
ResultRelInfo *resultRelInfo;
|
ResultRelInfo *resultRelInfo;
|
||||||
int i;
|
int i;
|
||||||
int numIndices;
|
int numIndices;
|
||||||
@ -1077,9 +1082,12 @@ ExecInsertIndexTuples(TupleTableSlot *slot,
|
|||||||
*/
|
*/
|
||||||
for (i = 0; i < numIndices; i++)
|
for (i = 0; i < numIndices; i++)
|
||||||
{
|
{
|
||||||
|
Relation indexRelation = relationDescs[i];
|
||||||
IndexInfo *indexInfo;
|
IndexInfo *indexInfo;
|
||||||
|
IndexUniqueCheck checkUnique;
|
||||||
|
bool isUnique;
|
||||||
|
|
||||||
if (relationDescs[i] == NULL)
|
if (indexRelation == NULL)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
indexInfo = indexInfoArray[i];
|
indexInfo = indexInfoArray[i];
|
||||||
@ -1122,22 +1130,50 @@ ExecInsertIndexTuples(TupleTableSlot *slot,
|
|||||||
isnull);
|
isnull);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The index AM does the rest. Note we suppress unique-index checks
|
* The index AM does the rest, including uniqueness checking.
|
||||||
* if we are being called from VACUUM, since VACUUM may need to move
|
*
|
||||||
* dead tuples that have the same keys as live ones.
|
* For an immediate-mode unique index, we just tell the index AM to
|
||||||
|
* throw error if not unique.
|
||||||
|
*
|
||||||
|
* For a deferrable unique index, we tell the index AM to just detect
|
||||||
|
* possible non-uniqueness, and we add the index OID to the result
|
||||||
|
* list if further checking is needed.
|
||||||
|
*
|
||||||
|
* Special hack: we suppress unique-index checks if we are being
|
||||||
|
* called from VACUUM FULL, since VACUUM FULL may need to move dead
|
||||||
|
* tuples that have the same keys as live ones.
|
||||||
*/
|
*/
|
||||||
index_insert(relationDescs[i], /* index relation */
|
if (is_vacuum_full || !indexRelation->rd_index->indisunique)
|
||||||
|
checkUnique = UNIQUE_CHECK_NO;
|
||||||
|
else if (indexRelation->rd_index->indimmediate)
|
||||||
|
checkUnique = UNIQUE_CHECK_YES;
|
||||||
|
else
|
||||||
|
checkUnique = UNIQUE_CHECK_PARTIAL;
|
||||||
|
|
||||||
|
isUnique =
|
||||||
|
index_insert(indexRelation, /* index relation */
|
||||||
values, /* array of index Datums */
|
values, /* array of index Datums */
|
||||||
isnull, /* null flags */
|
isnull, /* null flags */
|
||||||
tupleid, /* tid of heap tuple */
|
tupleid, /* tid of heap tuple */
|
||||||
heapRelation,
|
heapRelation, /* heap relation */
|
||||||
relationDescs[i]->rd_index->indisunique && !is_vacuum);
|
checkUnique); /* type of uniqueness check to do */
|
||||||
|
|
||||||
|
if (checkUnique == UNIQUE_CHECK_PARTIAL && !isUnique)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* The tuple potentially violates the uniqueness constraint,
|
||||||
|
* so make a note of the index so that we can re-check it later.
|
||||||
|
*/
|
||||||
|
result = lappend_oid(result, RelationGetRelid(indexRelation));
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* keep track of index inserts for debugging
|
* keep track of index inserts for debugging
|
||||||
*/
|
*/
|
||||||
IncrIndexInserted();
|
IncrIndexInserted();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -15,7 +15,7 @@
|
|||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.435 2009/07/26 23:34:17 tgl Exp $
|
* $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.436 2009/07/29 20:56:19 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -2091,6 +2091,8 @@ _copyConstraint(Constraint *from)
|
|||||||
COPY_NODE_FIELD(keys);
|
COPY_NODE_FIELD(keys);
|
||||||
COPY_NODE_FIELD(options);
|
COPY_NODE_FIELD(options);
|
||||||
COPY_STRING_FIELD(indexspace);
|
COPY_STRING_FIELD(indexspace);
|
||||||
|
COPY_SCALAR_FIELD(deferrable);
|
||||||
|
COPY_SCALAR_FIELD(initdeferred);
|
||||||
|
|
||||||
return newnode;
|
return newnode;
|
||||||
}
|
}
|
||||||
@ -2510,6 +2512,8 @@ _copyIndexStmt(IndexStmt *from)
|
|||||||
COPY_SCALAR_FIELD(unique);
|
COPY_SCALAR_FIELD(unique);
|
||||||
COPY_SCALAR_FIELD(primary);
|
COPY_SCALAR_FIELD(primary);
|
||||||
COPY_SCALAR_FIELD(isconstraint);
|
COPY_SCALAR_FIELD(isconstraint);
|
||||||
|
COPY_SCALAR_FIELD(deferrable);
|
||||||
|
COPY_SCALAR_FIELD(initdeferred);
|
||||||
COPY_SCALAR_FIELD(concurrent);
|
COPY_SCALAR_FIELD(concurrent);
|
||||||
|
|
||||||
return newnode;
|
return newnode;
|
||||||
|
@ -22,7 +22,7 @@
|
|||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.358 2009/07/26 23:34:18 tgl Exp $
|
* $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.359 2009/07/29 20:56:19 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -1160,6 +1160,8 @@ _equalIndexStmt(IndexStmt *a, IndexStmt *b)
|
|||||||
COMPARE_SCALAR_FIELD(unique);
|
COMPARE_SCALAR_FIELD(unique);
|
||||||
COMPARE_SCALAR_FIELD(primary);
|
COMPARE_SCALAR_FIELD(primary);
|
||||||
COMPARE_SCALAR_FIELD(isconstraint);
|
COMPARE_SCALAR_FIELD(isconstraint);
|
||||||
|
COMPARE_SCALAR_FIELD(deferrable);
|
||||||
|
COMPARE_SCALAR_FIELD(initdeferred);
|
||||||
COMPARE_SCALAR_FIELD(concurrent);
|
COMPARE_SCALAR_FIELD(concurrent);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@ -2068,6 +2070,8 @@ _equalConstraint(Constraint *a, Constraint *b)
|
|||||||
COMPARE_NODE_FIELD(keys);
|
COMPARE_NODE_FIELD(keys);
|
||||||
COMPARE_NODE_FIELD(options);
|
COMPARE_NODE_FIELD(options);
|
||||||
COMPARE_STRING_FIELD(indexspace);
|
COMPARE_STRING_FIELD(indexspace);
|
||||||
|
COMPARE_SCALAR_FIELD(deferrable);
|
||||||
|
COMPARE_SCALAR_FIELD(initdeferred);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.361 2009/07/16 06:33:42 petere Exp $
|
* $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.362 2009/07/29 20:56:19 tgl Exp $
|
||||||
*
|
*
|
||||||
* NOTES
|
* NOTES
|
||||||
* Every node type that can appear in stored rules' parsetrees *must*
|
* Every node type that can appear in stored rules' parsetrees *must*
|
||||||
@ -1734,6 +1734,8 @@ _outIndexStmt(StringInfo str, IndexStmt *node)
|
|||||||
WRITE_BOOL_FIELD(unique);
|
WRITE_BOOL_FIELD(unique);
|
||||||
WRITE_BOOL_FIELD(primary);
|
WRITE_BOOL_FIELD(primary);
|
||||||
WRITE_BOOL_FIELD(isconstraint);
|
WRITE_BOOL_FIELD(isconstraint);
|
||||||
|
WRITE_BOOL_FIELD(deferrable);
|
||||||
|
WRITE_BOOL_FIELD(initdeferred);
|
||||||
WRITE_BOOL_FIELD(concurrent);
|
WRITE_BOOL_FIELD(concurrent);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2285,6 +2287,8 @@ _outConstraint(StringInfo str, Constraint *node)
|
|||||||
WRITE_NODE_FIELD(keys);
|
WRITE_NODE_FIELD(keys);
|
||||||
WRITE_NODE_FIELD(options);
|
WRITE_NODE_FIELD(options);
|
||||||
WRITE_STRING_FIELD(indexspace);
|
WRITE_STRING_FIELD(indexspace);
|
||||||
|
WRITE_BOOL_FIELD(deferrable);
|
||||||
|
WRITE_BOOL_FIELD(initdeferred);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case CONSTR_UNIQUE:
|
case CONSTR_UNIQUE:
|
||||||
@ -2292,6 +2296,8 @@ _outConstraint(StringInfo str, Constraint *node)
|
|||||||
WRITE_NODE_FIELD(keys);
|
WRITE_NODE_FIELD(keys);
|
||||||
WRITE_NODE_FIELD(options);
|
WRITE_NODE_FIELD(options);
|
||||||
WRITE_STRING_FIELD(indexspace);
|
WRITE_STRING_FIELD(indexspace);
|
||||||
|
WRITE_BOOL_FIELD(deferrable);
|
||||||
|
WRITE_BOOL_FIELD(initdeferred);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case CONSTR_CHECK:
|
case CONSTR_CHECK:
|
||||||
|
@ -11,7 +11,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.673 2009/07/26 23:34:18 tgl Exp $
|
* $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.674 2009/07/29 20:56:19 tgl Exp $
|
||||||
*
|
*
|
||||||
* HISTORY
|
* HISTORY
|
||||||
* AUTHOR DATE MAJOR EVENT
|
* AUTHOR DATE MAJOR EVENT
|
||||||
@ -2220,6 +2220,8 @@ ColConstraintElem:
|
|||||||
n->cooked_expr = NULL;
|
n->cooked_expr = NULL;
|
||||||
n->keys = NULL;
|
n->keys = NULL;
|
||||||
n->indexspace = NULL;
|
n->indexspace = NULL;
|
||||||
|
n->deferrable = FALSE;
|
||||||
|
n->initdeferred = FALSE;
|
||||||
$$ = (Node *)n;
|
$$ = (Node *)n;
|
||||||
}
|
}
|
||||||
| NULL_P
|
| NULL_P
|
||||||
@ -2231,6 +2233,8 @@ ColConstraintElem:
|
|||||||
n->cooked_expr = NULL;
|
n->cooked_expr = NULL;
|
||||||
n->keys = NULL;
|
n->keys = NULL;
|
||||||
n->indexspace = NULL;
|
n->indexspace = NULL;
|
||||||
|
n->deferrable = FALSE;
|
||||||
|
n->initdeferred = FALSE;
|
||||||
$$ = (Node *)n;
|
$$ = (Node *)n;
|
||||||
}
|
}
|
||||||
| UNIQUE opt_definition OptConsTableSpace
|
| UNIQUE opt_definition OptConsTableSpace
|
||||||
@ -2243,6 +2247,8 @@ ColConstraintElem:
|
|||||||
n->keys = NULL;
|
n->keys = NULL;
|
||||||
n->options = $2;
|
n->options = $2;
|
||||||
n->indexspace = $3;
|
n->indexspace = $3;
|
||||||
|
n->deferrable = FALSE;
|
||||||
|
n->initdeferred = FALSE;
|
||||||
$$ = (Node *)n;
|
$$ = (Node *)n;
|
||||||
}
|
}
|
||||||
| PRIMARY KEY opt_definition OptConsTableSpace
|
| PRIMARY KEY opt_definition OptConsTableSpace
|
||||||
@ -2255,6 +2261,8 @@ ColConstraintElem:
|
|||||||
n->keys = NULL;
|
n->keys = NULL;
|
||||||
n->options = $3;
|
n->options = $3;
|
||||||
n->indexspace = $4;
|
n->indexspace = $4;
|
||||||
|
n->deferrable = FALSE;
|
||||||
|
n->initdeferred = FALSE;
|
||||||
$$ = (Node *)n;
|
$$ = (Node *)n;
|
||||||
}
|
}
|
||||||
| CHECK '(' a_expr ')'
|
| CHECK '(' a_expr ')'
|
||||||
@ -2266,6 +2274,8 @@ ColConstraintElem:
|
|||||||
n->cooked_expr = NULL;
|
n->cooked_expr = NULL;
|
||||||
n->keys = NULL;
|
n->keys = NULL;
|
||||||
n->indexspace = NULL;
|
n->indexspace = NULL;
|
||||||
|
n->deferrable = FALSE;
|
||||||
|
n->initdeferred = FALSE;
|
||||||
$$ = (Node *)n;
|
$$ = (Node *)n;
|
||||||
}
|
}
|
||||||
| DEFAULT b_expr
|
| DEFAULT b_expr
|
||||||
@ -2277,6 +2287,8 @@ ColConstraintElem:
|
|||||||
n->cooked_expr = NULL;
|
n->cooked_expr = NULL;
|
||||||
n->keys = NULL;
|
n->keys = NULL;
|
||||||
n->indexspace = NULL;
|
n->indexspace = NULL;
|
||||||
|
n->deferrable = FALSE;
|
||||||
|
n->initdeferred = FALSE;
|
||||||
$$ = (Node *)n;
|
$$ = (Node *)n;
|
||||||
}
|
}
|
||||||
| REFERENCES qualified_name opt_column_list key_match key_actions
|
| REFERENCES qualified_name opt_column_list key_match key_actions
|
||||||
@ -2398,7 +2410,7 @@ TableConstraint:
|
|||||||
;
|
;
|
||||||
|
|
||||||
ConstraintElem:
|
ConstraintElem:
|
||||||
CHECK '(' a_expr ')'
|
CHECK '(' a_expr ')' ConstraintAttributeSpec
|
||||||
{
|
{
|
||||||
Constraint *n = makeNode(Constraint);
|
Constraint *n = makeNode(Constraint);
|
||||||
n->contype = CONSTR_CHECK;
|
n->contype = CONSTR_CHECK;
|
||||||
@ -2406,9 +2418,17 @@ ConstraintElem:
|
|||||||
n->raw_expr = $3;
|
n->raw_expr = $3;
|
||||||
n->cooked_expr = NULL;
|
n->cooked_expr = NULL;
|
||||||
n->indexspace = NULL;
|
n->indexspace = NULL;
|
||||||
|
if ($5 != 0)
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||||
|
errmsg("CHECK constraints cannot be deferred"),
|
||||||
|
parser_errposition(@5)));
|
||||||
|
n->deferrable = FALSE;
|
||||||
|
n->initdeferred = FALSE;
|
||||||
$$ = (Node *)n;
|
$$ = (Node *)n;
|
||||||
}
|
}
|
||||||
| UNIQUE '(' columnList ')' opt_definition OptConsTableSpace
|
| UNIQUE '(' columnList ')' opt_definition OptConsTableSpace
|
||||||
|
ConstraintAttributeSpec
|
||||||
{
|
{
|
||||||
Constraint *n = makeNode(Constraint);
|
Constraint *n = makeNode(Constraint);
|
||||||
n->contype = CONSTR_UNIQUE;
|
n->contype = CONSTR_UNIQUE;
|
||||||
@ -2418,9 +2438,12 @@ ConstraintElem:
|
|||||||
n->keys = $3;
|
n->keys = $3;
|
||||||
n->options = $5;
|
n->options = $5;
|
||||||
n->indexspace = $6;
|
n->indexspace = $6;
|
||||||
|
n->deferrable = ($7 & 1) != 0;
|
||||||
|
n->initdeferred = ($7 & 2) != 0;
|
||||||
$$ = (Node *)n;
|
$$ = (Node *)n;
|
||||||
}
|
}
|
||||||
| PRIMARY KEY '(' columnList ')' opt_definition OptConsTableSpace
|
| PRIMARY KEY '(' columnList ')' opt_definition OptConsTableSpace
|
||||||
|
ConstraintAttributeSpec
|
||||||
{
|
{
|
||||||
Constraint *n = makeNode(Constraint);
|
Constraint *n = makeNode(Constraint);
|
||||||
n->contype = CONSTR_PRIMARY;
|
n->contype = CONSTR_PRIMARY;
|
||||||
@ -2430,6 +2453,8 @@ ConstraintElem:
|
|||||||
n->keys = $4;
|
n->keys = $4;
|
||||||
n->options = $6;
|
n->options = $6;
|
||||||
n->indexspace = $7;
|
n->indexspace = $7;
|
||||||
|
n->deferrable = ($8 & 1) != 0;
|
||||||
|
n->initdeferred = ($8 & 2) != 0;
|
||||||
$$ = (Node *)n;
|
$$ = (Node *)n;
|
||||||
}
|
}
|
||||||
| FOREIGN KEY '(' columnList ')' REFERENCES qualified_name
|
| FOREIGN KEY '(' columnList ')' REFERENCES qualified_name
|
||||||
|
@ -19,7 +19,7 @@
|
|||||||
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* $PostgreSQL: pgsql/src/backend/parser/parse_utilcmd.c,v 2.23 2009/07/16 06:33:43 petere Exp $
|
* $PostgreSQL: pgsql/src/backend/parser/parse_utilcmd.c,v 2.24 2009/07/29 20:56:19 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -33,6 +33,7 @@
|
|||||||
#include "catalog/heap.h"
|
#include "catalog/heap.h"
|
||||||
#include "catalog/index.h"
|
#include "catalog/index.h"
|
||||||
#include "catalog/namespace.h"
|
#include "catalog/namespace.h"
|
||||||
|
#include "catalog/pg_constraint.h"
|
||||||
#include "catalog/pg_opclass.h"
|
#include "catalog/pg_opclass.h"
|
||||||
#include "catalog/pg_type.h"
|
#include "catalog/pg_type.h"
|
||||||
#include "commands/defrem.h"
|
#include "commands/defrem.h"
|
||||||
@ -801,15 +802,38 @@ generateClonedIndexStmt(CreateStmtContext *cxt, Relation source_idx,
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* If the index is marked PRIMARY, it's certainly from a constraint; else,
|
* If the index is marked PRIMARY, it's certainly from a constraint; else,
|
||||||
* if it's not marked UNIQUE, it certainly isn't; else, we have to search
|
* if it's not marked UNIQUE, it certainly isn't. If it is or might be
|
||||||
* pg_depend to see if there's an associated unique constraint.
|
* from a constraint, we have to fetch the constraint to check for
|
||||||
|
* deferrability attributes.
|
||||||
*/
|
*/
|
||||||
if (index->primary)
|
if (index->primary || index->unique)
|
||||||
|
{
|
||||||
|
Oid constraintId = get_index_constraint(source_relid);
|
||||||
|
|
||||||
|
if (OidIsValid(constraintId))
|
||||||
|
{
|
||||||
|
HeapTuple ht_constr;
|
||||||
|
Form_pg_constraint conrec;
|
||||||
|
|
||||||
|
ht_constr = SearchSysCache(CONSTROID,
|
||||||
|
ObjectIdGetDatum(constraintId),
|
||||||
|
0, 0, 0);
|
||||||
|
if (!HeapTupleIsValid(ht_constr))
|
||||||
|
elog(ERROR, "cache lookup failed for constraint %u",
|
||||||
|
constraintId);
|
||||||
|
conrec = (Form_pg_constraint) GETSTRUCT(ht_constr);
|
||||||
|
|
||||||
index->isconstraint = true;
|
index->isconstraint = true;
|
||||||
else if (!index->unique)
|
index->deferrable = conrec->condeferrable;
|
||||||
index->isconstraint = false;
|
index->initdeferred = conrec->condeferred;
|
||||||
|
|
||||||
|
ReleaseSysCache(ht_constr);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
index->isconstraint = OidIsValid(get_index_constraint(source_relid));
|
index->isconstraint = false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
index->isconstraint = false;
|
||||||
|
|
||||||
/* Get the index expressions, if any */
|
/* Get the index expressions, if any */
|
||||||
datum = SysCacheGetAttr(INDEXRELID, ht_idx,
|
datum = SysCacheGetAttr(INDEXRELID, ht_idx,
|
||||||
@ -1039,7 +1063,9 @@ transformIndexConstraints(ParseState *pstate, CreateStmtContext *cxt)
|
|||||||
|
|
||||||
if (equal(index->indexParams, priorindex->indexParams) &&
|
if (equal(index->indexParams, priorindex->indexParams) &&
|
||||||
equal(index->whereClause, priorindex->whereClause) &&
|
equal(index->whereClause, priorindex->whereClause) &&
|
||||||
strcmp(index->accessMethod, priorindex->accessMethod) == 0)
|
strcmp(index->accessMethod, priorindex->accessMethod) == 0 &&
|
||||||
|
index->deferrable == priorindex->deferrable &&
|
||||||
|
index->initdeferred == priorindex->initdeferred)
|
||||||
{
|
{
|
||||||
priorindex->unique |= index->unique;
|
priorindex->unique |= index->unique;
|
||||||
|
|
||||||
@ -1092,6 +1118,8 @@ transformIndexConstraint(Constraint *constraint, CreateStmtContext *cxt)
|
|||||||
*/
|
*/
|
||||||
}
|
}
|
||||||
index->isconstraint = true;
|
index->isconstraint = true;
|
||||||
|
index->deferrable = constraint->deferrable;
|
||||||
|
index->initdeferred = constraint->initdeferred;
|
||||||
|
|
||||||
if (constraint->name != NULL)
|
if (constraint->name != NULL)
|
||||||
index->idxname = pstrdup(constraint->name);
|
index->idxname = pstrdup(constraint->name);
|
||||||
@ -1853,8 +1881,9 @@ transformAlterTableStmt(AlterTableStmt *stmt, const char *queryString)
|
|||||||
* to attach constraint attributes to their primary constraint nodes
|
* to attach constraint attributes to their primary constraint nodes
|
||||||
* and detect inconsistent/misplaced constraint attributes.
|
* and detect inconsistent/misplaced constraint attributes.
|
||||||
*
|
*
|
||||||
* NOTE: currently, attributes are only supported for FOREIGN KEY primary
|
* NOTE: currently, attributes are only supported for FOREIGN KEY, UNIQUE,
|
||||||
* constraints, but someday they ought to be supported for other constraints.
|
* and PRIMARY KEY constraints, but someday they ought to be supported
|
||||||
|
* for other constraint types.
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
transformConstraintAttrs(List *constraintList)
|
transformConstraintAttrs(List *constraintList)
|
||||||
@ -1864,6 +1893,13 @@ transformConstraintAttrs(List *constraintList)
|
|||||||
bool saw_initially = false;
|
bool saw_initially = false;
|
||||||
ListCell *clist;
|
ListCell *clist;
|
||||||
|
|
||||||
|
#define SUPPORTS_ATTRS(node) \
|
||||||
|
((node) != NULL && \
|
||||||
|
(IsA((node), FkConstraint) || \
|
||||||
|
(IsA((node), Constraint) && \
|
||||||
|
(((Constraint *) (node))->contype == CONSTR_PRIMARY || \
|
||||||
|
((Constraint *) (node))->contype == CONSTR_UNIQUE))))
|
||||||
|
|
||||||
foreach(clist, constraintList)
|
foreach(clist, constraintList)
|
||||||
{
|
{
|
||||||
Node *node = lfirst(clist);
|
Node *node = lfirst(clist);
|
||||||
@ -1882,8 +1918,7 @@ transformConstraintAttrs(List *constraintList)
|
|||||||
switch (con->contype)
|
switch (con->contype)
|
||||||
{
|
{
|
||||||
case CONSTR_ATTR_DEFERRABLE:
|
case CONSTR_ATTR_DEFERRABLE:
|
||||||
if (lastprimarynode == NULL ||
|
if (!SUPPORTS_ATTRS(lastprimarynode))
|
||||||
!IsA(lastprimarynode, FkConstraint))
|
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errcode(ERRCODE_SYNTAX_ERROR),
|
(errcode(ERRCODE_SYNTAX_ERROR),
|
||||||
errmsg("misplaced DEFERRABLE clause")));
|
errmsg("misplaced DEFERRABLE clause")));
|
||||||
@ -1892,11 +1927,14 @@ transformConstraintAttrs(List *constraintList)
|
|||||||
(errcode(ERRCODE_SYNTAX_ERROR),
|
(errcode(ERRCODE_SYNTAX_ERROR),
|
||||||
errmsg("multiple DEFERRABLE/NOT DEFERRABLE clauses not allowed")));
|
errmsg("multiple DEFERRABLE/NOT DEFERRABLE clauses not allowed")));
|
||||||
saw_deferrability = true;
|
saw_deferrability = true;
|
||||||
|
if (IsA(lastprimarynode, FkConstraint))
|
||||||
((FkConstraint *) lastprimarynode)->deferrable = true;
|
((FkConstraint *) lastprimarynode)->deferrable = true;
|
||||||
|
else
|
||||||
|
((Constraint *) lastprimarynode)->deferrable = true;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case CONSTR_ATTR_NOT_DEFERRABLE:
|
case CONSTR_ATTR_NOT_DEFERRABLE:
|
||||||
if (lastprimarynode == NULL ||
|
if (!SUPPORTS_ATTRS(lastprimarynode))
|
||||||
!IsA(lastprimarynode, FkConstraint))
|
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errcode(ERRCODE_SYNTAX_ERROR),
|
(errcode(ERRCODE_SYNTAX_ERROR),
|
||||||
errmsg("misplaced NOT DEFERRABLE clause")));
|
errmsg("misplaced NOT DEFERRABLE clause")));
|
||||||
@ -1905,16 +1943,28 @@ transformConstraintAttrs(List *constraintList)
|
|||||||
(errcode(ERRCODE_SYNTAX_ERROR),
|
(errcode(ERRCODE_SYNTAX_ERROR),
|
||||||
errmsg("multiple DEFERRABLE/NOT DEFERRABLE clauses not allowed")));
|
errmsg("multiple DEFERRABLE/NOT DEFERRABLE clauses not allowed")));
|
||||||
saw_deferrability = true;
|
saw_deferrability = true;
|
||||||
|
if (IsA(lastprimarynode, FkConstraint))
|
||||||
|
{
|
||||||
((FkConstraint *) lastprimarynode)->deferrable = false;
|
((FkConstraint *) lastprimarynode)->deferrable = false;
|
||||||
if (saw_initially &&
|
if (saw_initially &&
|
||||||
((FkConstraint *) lastprimarynode)->initdeferred)
|
((FkConstraint *) lastprimarynode)->initdeferred)
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errcode(ERRCODE_SYNTAX_ERROR),
|
(errcode(ERRCODE_SYNTAX_ERROR),
|
||||||
errmsg("constraint declared INITIALLY DEFERRED must be DEFERRABLE")));
|
errmsg("constraint declared INITIALLY DEFERRED must be DEFERRABLE")));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
((Constraint *) lastprimarynode)->deferrable = false;
|
||||||
|
if (saw_initially &&
|
||||||
|
((Constraint *) lastprimarynode)->initdeferred)
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_SYNTAX_ERROR),
|
||||||
|
errmsg("constraint declared INITIALLY DEFERRED must be DEFERRABLE")));
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case CONSTR_ATTR_DEFERRED:
|
case CONSTR_ATTR_DEFERRED:
|
||||||
if (lastprimarynode == NULL ||
|
if (!SUPPORTS_ATTRS(lastprimarynode))
|
||||||
!IsA(lastprimarynode, FkConstraint))
|
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errcode(ERRCODE_SYNTAX_ERROR),
|
(errcode(ERRCODE_SYNTAX_ERROR),
|
||||||
errmsg("misplaced INITIALLY DEFERRED clause")));
|
errmsg("misplaced INITIALLY DEFERRED clause")));
|
||||||
@ -1923,21 +1973,36 @@ transformConstraintAttrs(List *constraintList)
|
|||||||
(errcode(ERRCODE_SYNTAX_ERROR),
|
(errcode(ERRCODE_SYNTAX_ERROR),
|
||||||
errmsg("multiple INITIALLY IMMEDIATE/DEFERRED clauses not allowed")));
|
errmsg("multiple INITIALLY IMMEDIATE/DEFERRED clauses not allowed")));
|
||||||
saw_initially = true;
|
saw_initially = true;
|
||||||
((FkConstraint *) lastprimarynode)->initdeferred = true;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If only INITIALLY DEFERRED appears, assume DEFERRABLE
|
* If only INITIALLY DEFERRED appears, assume DEFERRABLE
|
||||||
*/
|
*/
|
||||||
|
if (IsA(lastprimarynode, FkConstraint))
|
||||||
|
{
|
||||||
|
((FkConstraint *) lastprimarynode)->initdeferred = true;
|
||||||
|
|
||||||
if (!saw_deferrability)
|
if (!saw_deferrability)
|
||||||
((FkConstraint *) lastprimarynode)->deferrable = true;
|
((FkConstraint *) lastprimarynode)->deferrable = true;
|
||||||
else if (!((FkConstraint *) lastprimarynode)->deferrable)
|
else if (!((FkConstraint *) lastprimarynode)->deferrable)
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errcode(ERRCODE_SYNTAX_ERROR),
|
(errcode(ERRCODE_SYNTAX_ERROR),
|
||||||
errmsg("constraint declared INITIALLY DEFERRED must be DEFERRABLE")));
|
errmsg("constraint declared INITIALLY DEFERRED must be DEFERRABLE")));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
((Constraint *) lastprimarynode)->initdeferred = true;
|
||||||
|
|
||||||
|
if (!saw_deferrability)
|
||||||
|
((Constraint *) lastprimarynode)->deferrable = true;
|
||||||
|
else if (!((Constraint *) lastprimarynode)->deferrable)
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_SYNTAX_ERROR),
|
||||||
|
errmsg("constraint declared INITIALLY DEFERRED must be DEFERRABLE")));
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case CONSTR_ATTR_IMMEDIATE:
|
case CONSTR_ATTR_IMMEDIATE:
|
||||||
if (lastprimarynode == NULL ||
|
if (!SUPPORTS_ATTRS(lastprimarynode))
|
||||||
!IsA(lastprimarynode, FkConstraint))
|
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errcode(ERRCODE_SYNTAX_ERROR),
|
(errcode(ERRCODE_SYNTAX_ERROR),
|
||||||
errmsg("misplaced INITIALLY IMMEDIATE clause")));
|
errmsg("misplaced INITIALLY IMMEDIATE clause")));
|
||||||
@ -1946,8 +2011,12 @@ transformConstraintAttrs(List *constraintList)
|
|||||||
(errcode(ERRCODE_SYNTAX_ERROR),
|
(errcode(ERRCODE_SYNTAX_ERROR),
|
||||||
errmsg("multiple INITIALLY IMMEDIATE/DEFERRED clauses not allowed")));
|
errmsg("multiple INITIALLY IMMEDIATE/DEFERRED clauses not allowed")));
|
||||||
saw_initially = true;
|
saw_initially = true;
|
||||||
|
if (IsA(lastprimarynode, FkConstraint))
|
||||||
((FkConstraint *) lastprimarynode)->initdeferred = false;
|
((FkConstraint *) lastprimarynode)->initdeferred = false;
|
||||||
|
else
|
||||||
|
((Constraint *) lastprimarynode)->initdeferred = false;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
/* Otherwise it's not an attribute */
|
/* Otherwise it's not an attribute */
|
||||||
lastprimarynode = node;
|
lastprimarynode = node;
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/tcop/utility.c,v 1.312 2009/07/28 02:56:30 tgl Exp $
|
* $PostgreSQL: pgsql/src/backend/tcop/utility.c,v 1.313 2009/07/29 20:56:19 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -795,6 +795,8 @@ ProcessUtility(Node *parsetree,
|
|||||||
stmt->unique,
|
stmt->unique,
|
||||||
stmt->primary,
|
stmt->primary,
|
||||||
stmt->isconstraint,
|
stmt->isconstraint,
|
||||||
|
stmt->deferrable,
|
||||||
|
stmt->initdeferred,
|
||||||
false, /* is_alter_table */
|
false, /* is_alter_table */
|
||||||
true, /* check_rights */
|
true, /* check_rights */
|
||||||
false, /* skip_build */
|
false, /* skip_build */
|
||||||
@ -929,7 +931,7 @@ ProcessUtility(Node *parsetree,
|
|||||||
|
|
||||||
case T_CreateTrigStmt:
|
case T_CreateTrigStmt:
|
||||||
CreateTrigger((CreateTrigStmt *) parsetree,
|
CreateTrigger((CreateTrigStmt *) parsetree,
|
||||||
InvalidOid, InvalidOid, true);
|
InvalidOid, InvalidOid, NULL, true);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case T_DropPropertyStmt:
|
case T_DropPropertyStmt:
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/utils/adt/ruleutils.c,v 1.304 2009/07/24 21:08:42 tgl Exp $
|
* $PostgreSQL: pgsql/src/backend/utils/adt/ruleutils.c,v 1.305 2009/07/29 20:56:19 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -1044,11 +1044,6 @@ pg_get_constraintdef_worker(Oid constraintId, bool fullCommand,
|
|||||||
if (string)
|
if (string)
|
||||||
appendStringInfo(&buf, " ON DELETE %s", string);
|
appendStringInfo(&buf, " ON DELETE %s", string);
|
||||||
|
|
||||||
if (conForm->condeferrable)
|
|
||||||
appendStringInfo(&buf, " DEFERRABLE");
|
|
||||||
if (conForm->condeferred)
|
|
||||||
appendStringInfo(&buf, " INITIALLY DEFERRED");
|
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case CONSTRAINT_PRIMARY:
|
case CONSTRAINT_PRIMARY:
|
||||||
@ -1150,6 +1145,11 @@ pg_get_constraintdef_worker(Oid constraintId, bool fullCommand,
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (conForm->condeferrable)
|
||||||
|
appendStringInfo(&buf, " DEFERRABLE");
|
||||||
|
if (conForm->condeferred)
|
||||||
|
appendStringInfo(&buf, " INITIALLY DEFERRED");
|
||||||
|
|
||||||
/* Cleanup */
|
/* Cleanup */
|
||||||
ReleaseSysCache(tup);
|
ReleaseSysCache(tup);
|
||||||
|
|
||||||
|
4
src/backend/utils/cache/relcache.c
vendored
4
src/backend/utils/cache/relcache.c
vendored
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/utils/cache/relcache.c,v 1.287 2009/06/11 14:49:05 momjian Exp $
|
* $PostgreSQL: pgsql/src/backend/utils/cache/relcache.c,v 1.288 2009/07/29 20:56:19 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -2938,7 +2938,7 @@ RelationGetIndexList(Relation relation)
|
|||||||
|
|
||||||
/* Check to see if it is a unique, non-partial btree index on OID */
|
/* Check to see if it is a unique, non-partial btree index on OID */
|
||||||
if (index->indnatts == 1 &&
|
if (index->indnatts == 1 &&
|
||||||
index->indisunique &&
|
index->indisunique && index->indimmediate &&
|
||||||
index->indkey.values[0] == ObjectIdAttributeNumber &&
|
index->indkey.values[0] == ObjectIdAttributeNumber &&
|
||||||
index->indclass.values[0] == OID_BTREE_OPS_OID &&
|
index->indclass.values[0] == OID_BTREE_OPS_OID &&
|
||||||
heap_attisnull(htup, Anum_pg_index_indpred))
|
heap_attisnull(htup, Anum_pg_index_indpred))
|
||||||
|
@ -12,7 +12,7 @@
|
|||||||
* by PostgreSQL
|
* by PostgreSQL
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/bin/pg_dump/pg_dump.c,v 1.542 2009/07/23 22:59:40 tgl Exp $
|
* $PostgreSQL: pgsql/src/bin/pg_dump/pg_dump.c,v 1.543 2009/07/29 20:56:19 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -3655,6 +3655,8 @@ getIndexes(TableInfo tblinfo[], int numTables)
|
|||||||
i_indisclustered,
|
i_indisclustered,
|
||||||
i_contype,
|
i_contype,
|
||||||
i_conname,
|
i_conname,
|
||||||
|
i_condeferrable,
|
||||||
|
i_condeferred,
|
||||||
i_contableoid,
|
i_contableoid,
|
||||||
i_conoid,
|
i_conoid,
|
||||||
i_tablespace,
|
i_tablespace,
|
||||||
@ -3696,6 +3698,7 @@ getIndexes(TableInfo tblinfo[], int numTables)
|
|||||||
"t.relnatts AS indnkeys, "
|
"t.relnatts AS indnkeys, "
|
||||||
"i.indkey, i.indisclustered, "
|
"i.indkey, i.indisclustered, "
|
||||||
"c.contype, c.conname, "
|
"c.contype, c.conname, "
|
||||||
|
"c.condeferrable, c.condeferred, "
|
||||||
"c.tableoid AS contableoid, "
|
"c.tableoid AS contableoid, "
|
||||||
"c.oid AS conoid, "
|
"c.oid AS conoid, "
|
||||||
"(SELECT spcname FROM pg_catalog.pg_tablespace s WHERE s.oid = t.reltablespace) AS tablespace, "
|
"(SELECT spcname FROM pg_catalog.pg_tablespace s WHERE s.oid = t.reltablespace) AS tablespace, "
|
||||||
@ -3722,6 +3725,7 @@ getIndexes(TableInfo tblinfo[], int numTables)
|
|||||||
"t.relnatts AS indnkeys, "
|
"t.relnatts AS indnkeys, "
|
||||||
"i.indkey, i.indisclustered, "
|
"i.indkey, i.indisclustered, "
|
||||||
"c.contype, c.conname, "
|
"c.contype, c.conname, "
|
||||||
|
"c.condeferrable, c.condeferred, "
|
||||||
"c.tableoid AS contableoid, "
|
"c.tableoid AS contableoid, "
|
||||||
"c.oid AS conoid, "
|
"c.oid AS conoid, "
|
||||||
"(SELECT spcname FROM pg_catalog.pg_tablespace s WHERE s.oid = t.reltablespace) AS tablespace, "
|
"(SELECT spcname FROM pg_catalog.pg_tablespace s WHERE s.oid = t.reltablespace) AS tablespace, "
|
||||||
@ -3748,6 +3752,7 @@ getIndexes(TableInfo tblinfo[], int numTables)
|
|||||||
"t.relnatts AS indnkeys, "
|
"t.relnatts AS indnkeys, "
|
||||||
"i.indkey, i.indisclustered, "
|
"i.indkey, i.indisclustered, "
|
||||||
"c.contype, c.conname, "
|
"c.contype, c.conname, "
|
||||||
|
"c.condeferrable, c.condeferred, "
|
||||||
"c.tableoid AS contableoid, "
|
"c.tableoid AS contableoid, "
|
||||||
"c.oid AS conoid, "
|
"c.oid AS conoid, "
|
||||||
"NULL AS tablespace, "
|
"NULL AS tablespace, "
|
||||||
@ -3776,6 +3781,8 @@ getIndexes(TableInfo tblinfo[], int numTables)
|
|||||||
"CASE WHEN i.indisprimary THEN 'p'::char "
|
"CASE WHEN i.indisprimary THEN 'p'::char "
|
||||||
"ELSE '0'::char END AS contype, "
|
"ELSE '0'::char END AS contype, "
|
||||||
"t.relname AS conname, "
|
"t.relname AS conname, "
|
||||||
|
"false AS condeferrable, "
|
||||||
|
"false AS condeferred, "
|
||||||
"0::oid AS contableoid, "
|
"0::oid AS contableoid, "
|
||||||
"t.oid AS conoid, "
|
"t.oid AS conoid, "
|
||||||
"NULL AS tablespace, "
|
"NULL AS tablespace, "
|
||||||
@ -3799,6 +3806,8 @@ getIndexes(TableInfo tblinfo[], int numTables)
|
|||||||
"CASE WHEN i.indisprimary THEN 'p'::char "
|
"CASE WHEN i.indisprimary THEN 'p'::char "
|
||||||
"ELSE '0'::char END AS contype, "
|
"ELSE '0'::char END AS contype, "
|
||||||
"t.relname AS conname, "
|
"t.relname AS conname, "
|
||||||
|
"false AS condeferrable, "
|
||||||
|
"false AS condeferred, "
|
||||||
"0::oid AS contableoid, "
|
"0::oid AS contableoid, "
|
||||||
"t.oid AS conoid, "
|
"t.oid AS conoid, "
|
||||||
"NULL AS tablespace, "
|
"NULL AS tablespace, "
|
||||||
@ -3824,6 +3833,8 @@ getIndexes(TableInfo tblinfo[], int numTables)
|
|||||||
i_indisclustered = PQfnumber(res, "indisclustered");
|
i_indisclustered = PQfnumber(res, "indisclustered");
|
||||||
i_contype = PQfnumber(res, "contype");
|
i_contype = PQfnumber(res, "contype");
|
||||||
i_conname = PQfnumber(res, "conname");
|
i_conname = PQfnumber(res, "conname");
|
||||||
|
i_condeferrable = PQfnumber(res, "condeferrable");
|
||||||
|
i_condeferred = PQfnumber(res, "condeferred");
|
||||||
i_contableoid = PQfnumber(res, "contableoid");
|
i_contableoid = PQfnumber(res, "contableoid");
|
||||||
i_conoid = PQfnumber(res, "conoid");
|
i_conoid = PQfnumber(res, "conoid");
|
||||||
i_tablespace = PQfnumber(res, "tablespace");
|
i_tablespace = PQfnumber(res, "tablespace");
|
||||||
@ -3884,6 +3895,8 @@ getIndexes(TableInfo tblinfo[], int numTables)
|
|||||||
constrinfo[j].condef = NULL;
|
constrinfo[j].condef = NULL;
|
||||||
constrinfo[j].confrelid = InvalidOid;
|
constrinfo[j].confrelid = InvalidOid;
|
||||||
constrinfo[j].conindex = indxinfo[j].dobj.dumpId;
|
constrinfo[j].conindex = indxinfo[j].dobj.dumpId;
|
||||||
|
constrinfo[j].condeferrable = *(PQgetvalue(res, j, i_condeferrable)) == 't';
|
||||||
|
constrinfo[j].condeferred = *(PQgetvalue(res, j, i_condeferred)) == 't';
|
||||||
constrinfo[j].conislocal = true;
|
constrinfo[j].conislocal = true;
|
||||||
constrinfo[j].separate = true;
|
constrinfo[j].separate = true;
|
||||||
|
|
||||||
@ -3988,6 +4001,8 @@ getConstraints(TableInfo tblinfo[], int numTables)
|
|||||||
constrinfo[j].condef = strdup(PQgetvalue(res, j, i_condef));
|
constrinfo[j].condef = strdup(PQgetvalue(res, j, i_condef));
|
||||||
constrinfo[j].confrelid = atooid(PQgetvalue(res, j, i_confrelid));
|
constrinfo[j].confrelid = atooid(PQgetvalue(res, j, i_confrelid));
|
||||||
constrinfo[j].conindex = 0;
|
constrinfo[j].conindex = 0;
|
||||||
|
constrinfo[j].condeferrable = false;
|
||||||
|
constrinfo[j].condeferred = false;
|
||||||
constrinfo[j].conislocal = true;
|
constrinfo[j].conislocal = true;
|
||||||
constrinfo[j].separate = true;
|
constrinfo[j].separate = true;
|
||||||
}
|
}
|
||||||
@ -4072,6 +4087,8 @@ getDomainConstraints(TypeInfo *tinfo)
|
|||||||
constrinfo[i].condef = strdup(PQgetvalue(res, i, i_consrc));
|
constrinfo[i].condef = strdup(PQgetvalue(res, i, i_consrc));
|
||||||
constrinfo[i].confrelid = InvalidOid;
|
constrinfo[i].confrelid = InvalidOid;
|
||||||
constrinfo[i].conindex = 0;
|
constrinfo[i].conindex = 0;
|
||||||
|
constrinfo[i].condeferrable = false;
|
||||||
|
constrinfo[i].condeferred = false;
|
||||||
constrinfo[i].conislocal = true;
|
constrinfo[i].conislocal = true;
|
||||||
constrinfo[i].separate = false;
|
constrinfo[i].separate = false;
|
||||||
|
|
||||||
@ -5071,6 +5088,8 @@ getTableAttrs(TableInfo *tblinfo, int numTables)
|
|||||||
constrs[j].condef = strdup(PQgetvalue(res, j, 3));
|
constrs[j].condef = strdup(PQgetvalue(res, j, 3));
|
||||||
constrs[j].confrelid = InvalidOid;
|
constrs[j].confrelid = InvalidOid;
|
||||||
constrs[j].conindex = 0;
|
constrs[j].conindex = 0;
|
||||||
|
constrs[j].condeferrable = false;
|
||||||
|
constrs[j].condeferred = false;
|
||||||
constrs[j].conislocal = (PQgetvalue(res, j, 4)[0] == 't');
|
constrs[j].conislocal = (PQgetvalue(res, j, 4)[0] == 't');
|
||||||
constrs[j].separate = false;
|
constrs[j].separate = false;
|
||||||
|
|
||||||
@ -10454,6 +10473,13 @@ dumpConstraint(Archive *fout, ConstraintInfo *coninfo)
|
|||||||
if (indxinfo->options && strlen(indxinfo->options) > 0)
|
if (indxinfo->options && strlen(indxinfo->options) > 0)
|
||||||
appendPQExpBuffer(q, " WITH (%s)", indxinfo->options);
|
appendPQExpBuffer(q, " WITH (%s)", indxinfo->options);
|
||||||
|
|
||||||
|
if (coninfo->condeferrable)
|
||||||
|
{
|
||||||
|
appendPQExpBuffer(q, " DEFERRABLE");
|
||||||
|
if (coninfo->condeferred)
|
||||||
|
appendPQExpBuffer(q, " INITIALLY DEFERRED");
|
||||||
|
}
|
||||||
|
|
||||||
appendPQExpBuffer(q, ";\n");
|
appendPQExpBuffer(q, ";\n");
|
||||||
|
|
||||||
/* If the index is clustered, we need to record that. */
|
/* If the index is clustered, we need to record that. */
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* $PostgreSQL: pgsql/src/bin/pg_dump/pg_dump.h,v 1.154 2009/06/11 14:49:07 momjian Exp $
|
* $PostgreSQL: pgsql/src/bin/pg_dump/pg_dump.h,v 1.155 2009/07/29 20:56:19 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -332,6 +332,9 @@ typedef struct _triggerInfo
|
|||||||
* struct ConstraintInfo is used for all constraint types. However we
|
* struct ConstraintInfo is used for all constraint types. However we
|
||||||
* use a different objType for foreign key constraints, to make it easier
|
* use a different objType for foreign key constraints, to make it easier
|
||||||
* to sort them the way we want.
|
* to sort them the way we want.
|
||||||
|
*
|
||||||
|
* Note: condeferrable and condeferred are currently only valid for
|
||||||
|
* unique/primary-key constraints. Otherwise that info is in condef.
|
||||||
*/
|
*/
|
||||||
typedef struct _constraintInfo
|
typedef struct _constraintInfo
|
||||||
{
|
{
|
||||||
@ -342,6 +345,8 @@ typedef struct _constraintInfo
|
|||||||
char *condef; /* definition, if CHECK or FOREIGN KEY */
|
char *condef; /* definition, if CHECK or FOREIGN KEY */
|
||||||
Oid confrelid; /* referenced table, if FOREIGN KEY */
|
Oid confrelid; /* referenced table, if FOREIGN KEY */
|
||||||
DumpId conindex; /* identifies associated index if any */
|
DumpId conindex; /* identifies associated index if any */
|
||||||
|
bool condeferrable; /* TRUE if constraint is DEFERRABLE */
|
||||||
|
bool condeferred; /* TRUE if constraint is INITIALLY DEFERRED */
|
||||||
bool conislocal; /* TRUE if constraint has local definition */
|
bool conislocal; /* TRUE if constraint has local definition */
|
||||||
bool separate; /* TRUE if must dump as separate item */
|
bool separate; /* TRUE if must dump as separate item */
|
||||||
} ConstraintInfo;
|
} ConstraintInfo;
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
* Copyright (c) 2000-2009, PostgreSQL Global Development Group
|
* Copyright (c) 2000-2009, PostgreSQL Global Development Group
|
||||||
*
|
*
|
||||||
* $PostgreSQL: pgsql/src/bin/psql/describe.c,v 1.225 2009/07/20 03:46:45 tgl Exp $
|
* $PostgreSQL: pgsql/src/bin/psql/describe.c,v 1.226 2009/07/29 20:56:19 tgl Exp $
|
||||||
*/
|
*/
|
||||||
#include "postgres_fe.h"
|
#include "postgres_fe.h"
|
||||||
|
|
||||||
@ -1321,11 +1321,32 @@ describeOneTableDetails(const char *schemaname,
|
|||||||
printfPQExpBuffer(&buf,
|
printfPQExpBuffer(&buf,
|
||||||
"SELECT i.indisunique, i.indisprimary, i.indisclustered, ");
|
"SELECT i.indisunique, i.indisprimary, i.indisclustered, ");
|
||||||
if (pset.sversion >= 80200)
|
if (pset.sversion >= 80200)
|
||||||
appendPQExpBuffer(&buf, "i.indisvalid, ");
|
appendPQExpBuffer(&buf, "i.indisvalid,\n");
|
||||||
else
|
else
|
||||||
appendPQExpBuffer(&buf, "true as indisvalid, ");
|
appendPQExpBuffer(&buf, "true AS indisvalid,\n");
|
||||||
appendPQExpBuffer(&buf, "a.amname, c2.relname,\n"
|
if (pset.sversion >= 80500)
|
||||||
" pg_catalog.pg_get_expr(i.indpred, i.indrelid, true)\n"
|
appendPQExpBuffer(&buf,
|
||||||
|
" (NOT i.indimmediate) AND "
|
||||||
|
"EXISTS (SELECT 1 FROM pg_catalog.pg_depend d, "
|
||||||
|
"pg_catalog.pg_constraint con WHERE "
|
||||||
|
"d.classid = 'pg_catalog.pg_class'::pg_catalog.regclass AND "
|
||||||
|
"d.objid = i.indexrelid AND "
|
||||||
|
"d.refclassid = 'pg_catalog.pg_constraint'::pg_catalog.regclass AND "
|
||||||
|
"d.refobjid = con.oid AND d.deptype = 'i' AND "
|
||||||
|
"con.condeferrable) AS condeferrable,\n"
|
||||||
|
" (NOT i.indimmediate) AND "
|
||||||
|
"EXISTS (SELECT 1 FROM pg_catalog.pg_depend d, "
|
||||||
|
"pg_catalog.pg_constraint con WHERE "
|
||||||
|
"d.classid = 'pg_catalog.pg_class'::pg_catalog.regclass AND "
|
||||||
|
"d.objid = i.indexrelid AND "
|
||||||
|
"d.refclassid = 'pg_catalog.pg_constraint'::pg_catalog.regclass AND "
|
||||||
|
"d.refobjid = con.oid AND d.deptype = 'i' AND "
|
||||||
|
"con.condeferred) AS condeferred,\n");
|
||||||
|
else
|
||||||
|
appendPQExpBuffer(&buf,
|
||||||
|
" false AS condeferrable, false AS condeferred,\n");
|
||||||
|
appendPQExpBuffer(&buf, " a.amname, c2.relname, "
|
||||||
|
"pg_catalog.pg_get_expr(i.indpred, i.indrelid, true)\n"
|
||||||
"FROM pg_catalog.pg_index i, pg_catalog.pg_class c, pg_catalog.pg_class c2, pg_catalog.pg_am a\n"
|
"FROM pg_catalog.pg_index i, pg_catalog.pg_class c, pg_catalog.pg_class c2, pg_catalog.pg_am a\n"
|
||||||
"WHERE i.indexrelid = c.oid AND c.oid = '%s' AND c.relam = a.oid\n"
|
"WHERE i.indexrelid = c.oid AND c.oid = '%s' AND c.relam = a.oid\n"
|
||||||
"AND i.indrelid = c2.oid",
|
"AND i.indrelid = c2.oid",
|
||||||
@ -1345,9 +1366,11 @@ describeOneTableDetails(const char *schemaname,
|
|||||||
char *indisprimary = PQgetvalue(result, 0, 1);
|
char *indisprimary = PQgetvalue(result, 0, 1);
|
||||||
char *indisclustered = PQgetvalue(result, 0, 2);
|
char *indisclustered = PQgetvalue(result, 0, 2);
|
||||||
char *indisvalid = PQgetvalue(result, 0, 3);
|
char *indisvalid = PQgetvalue(result, 0, 3);
|
||||||
char *indamname = PQgetvalue(result, 0, 4);
|
char *deferrable = PQgetvalue(result, 0, 4);
|
||||||
char *indtable = PQgetvalue(result, 0, 5);
|
char *deferred = PQgetvalue(result, 0, 5);
|
||||||
char *indpred = PQgetvalue(result, 0, 6);
|
char *indamname = PQgetvalue(result, 0, 6);
|
||||||
|
char *indtable = PQgetvalue(result, 0, 7);
|
||||||
|
char *indpred = PQgetvalue(result, 0, 8);
|
||||||
|
|
||||||
if (strcmp(indisprimary, "t") == 0)
|
if (strcmp(indisprimary, "t") == 0)
|
||||||
printfPQExpBuffer(&tmpbuf, _("primary key, "));
|
printfPQExpBuffer(&tmpbuf, _("primary key, "));
|
||||||
@ -1370,6 +1393,12 @@ describeOneTableDetails(const char *schemaname,
|
|||||||
if (strcmp(indisvalid, "t") != 0)
|
if (strcmp(indisvalid, "t") != 0)
|
||||||
appendPQExpBuffer(&tmpbuf, _(", invalid"));
|
appendPQExpBuffer(&tmpbuf, _(", invalid"));
|
||||||
|
|
||||||
|
if (strcmp(deferrable, "t") == 0)
|
||||||
|
appendPQExpBuffer(&tmpbuf, _(", deferrable"));
|
||||||
|
|
||||||
|
if (strcmp(deferred, "t") == 0)
|
||||||
|
appendPQExpBuffer(&tmpbuf, _(", initially deferred"));
|
||||||
|
|
||||||
printTableAddFooter(&cont, tmpbuf.data);
|
printTableAddFooter(&cont, tmpbuf.data);
|
||||||
add_tablespace_footer(&cont, tableinfo.relkind,
|
add_tablespace_footer(&cont, tableinfo.relkind,
|
||||||
tableinfo.tablespace, true);
|
tableinfo.tablespace, true);
|
||||||
@ -1431,6 +1460,26 @@ describeOneTableDetails(const char *schemaname,
|
|||||||
else
|
else
|
||||||
appendPQExpBuffer(&buf, "true as indisvalid, ");
|
appendPQExpBuffer(&buf, "true as indisvalid, ");
|
||||||
appendPQExpBuffer(&buf, "pg_catalog.pg_get_indexdef(i.indexrelid, 0, true)");
|
appendPQExpBuffer(&buf, "pg_catalog.pg_get_indexdef(i.indexrelid, 0, true)");
|
||||||
|
if (pset.sversion >= 80500)
|
||||||
|
appendPQExpBuffer(&buf,
|
||||||
|
",\n (NOT i.indimmediate) AND "
|
||||||
|
"EXISTS (SELECT 1 FROM pg_catalog.pg_depend d, "
|
||||||
|
"pg_catalog.pg_constraint con WHERE "
|
||||||
|
"d.classid = 'pg_catalog.pg_class'::pg_catalog.regclass AND "
|
||||||
|
"d.objid = i.indexrelid AND "
|
||||||
|
"d.refclassid = 'pg_catalog.pg_constraint'::pg_catalog.regclass AND "
|
||||||
|
"d.refobjid = con.oid AND d.deptype = 'i' AND "
|
||||||
|
"con.condeferrable) AS condeferrable"
|
||||||
|
",\n (NOT i.indimmediate) AND "
|
||||||
|
"EXISTS (SELECT 1 FROM pg_catalog.pg_depend d, "
|
||||||
|
"pg_catalog.pg_constraint con WHERE "
|
||||||
|
"d.classid = 'pg_catalog.pg_class'::pg_catalog.regclass AND "
|
||||||
|
"d.objid = i.indexrelid AND "
|
||||||
|
"d.refclassid = 'pg_catalog.pg_constraint'::pg_catalog.regclass AND "
|
||||||
|
"d.refobjid = con.oid AND d.deptype = 'i' AND "
|
||||||
|
"con.condeferred) AS condeferred");
|
||||||
|
else
|
||||||
|
appendPQExpBuffer(&buf, ", false AS condeferrable, false AS condeferred");
|
||||||
if (pset.sversion >= 80000)
|
if (pset.sversion >= 80000)
|
||||||
appendPQExpBuffer(&buf, ", c2.reltablespace");
|
appendPQExpBuffer(&buf, ", c2.reltablespace");
|
||||||
appendPQExpBuffer(&buf,
|
appendPQExpBuffer(&buf,
|
||||||
@ -1477,12 +1526,18 @@ describeOneTableDetails(const char *schemaname,
|
|||||||
if (strcmp(PQgetvalue(result, i, 4), "t") != 0)
|
if (strcmp(PQgetvalue(result, i, 4), "t") != 0)
|
||||||
appendPQExpBuffer(&buf, " INVALID");
|
appendPQExpBuffer(&buf, " INVALID");
|
||||||
|
|
||||||
|
if (strcmp(PQgetvalue(result, i, 6), "t") == 0)
|
||||||
|
appendPQExpBuffer(&buf, " DEFERRABLE");
|
||||||
|
|
||||||
|
if (strcmp(PQgetvalue(result, i, 7), "t") == 0)
|
||||||
|
appendPQExpBuffer(&buf, " INITIALLY DEFERRED");
|
||||||
|
|
||||||
printTableAddFooter(&cont, buf.data);
|
printTableAddFooter(&cont, buf.data);
|
||||||
|
|
||||||
/* Print tablespace of the index on the same line */
|
/* Print tablespace of the index on the same line */
|
||||||
if (pset.sversion >= 80000)
|
if (pset.sversion >= 80000)
|
||||||
add_tablespace_footer(&cont, 'i',
|
add_tablespace_footer(&cont, 'i',
|
||||||
atooid(PQgetvalue(result, i, 6)),
|
atooid(PQgetvalue(result, i, 8)),
|
||||||
false);
|
false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1677,7 +1732,7 @@ describeOneTableDetails(const char *schemaname,
|
|||||||
PQclear(result);
|
PQclear(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* print triggers (but ignore foreign-key triggers) */
|
/* print triggers (but ignore RI and unique constraint triggers) */
|
||||||
if (tableinfo.hastriggers)
|
if (tableinfo.hastriggers)
|
||||||
{
|
{
|
||||||
printfPQExpBuffer(&buf,
|
printfPQExpBuffer(&buf,
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* $PostgreSQL: pgsql/src/include/access/genam.h,v 1.78 2009/06/11 14:49:08 momjian Exp $
|
* $PostgreSQL: pgsql/src/include/access/genam.h,v 1.79 2009/07/29 20:56:19 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -85,6 +85,34 @@ typedef bool (*IndexBulkDeleteCallback) (ItemPointer itemptr, void *state);
|
|||||||
typedef struct IndexScanDescData *IndexScanDesc;
|
typedef struct IndexScanDescData *IndexScanDesc;
|
||||||
typedef struct SysScanDescData *SysScanDesc;
|
typedef struct SysScanDescData *SysScanDesc;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Enumeration specifying the type of uniqueness check to perform in
|
||||||
|
* index_insert().
|
||||||
|
*
|
||||||
|
* UNIQUE_CHECK_YES is the traditional Postgres immediate check, possibly
|
||||||
|
* blocking to see if a conflicting transaction commits.
|
||||||
|
*
|
||||||
|
* For deferrable unique constraints, UNIQUE_CHECK_PARTIAL is specified at
|
||||||
|
* insertion time. The index AM should test if the tuple is unique, but
|
||||||
|
* should not throw error, block, or prevent the insertion if the tuple
|
||||||
|
* appears not to be unique. We'll recheck later when it is time for the
|
||||||
|
* constraint to be enforced. The AM must return true if the tuple is
|
||||||
|
* known unique, false if it is possibly non-unique. In the "true" case
|
||||||
|
* it is safe to omit the later recheck.
|
||||||
|
*
|
||||||
|
* When it is time to recheck the deferred constraint, a pseudo-insertion
|
||||||
|
* call is made with UNIQUE_CHECK_EXISTING. The tuple is already in the
|
||||||
|
* index in this case, so it should not be inserted again. Rather, just
|
||||||
|
* check for conflicting live tuples (possibly blocking).
|
||||||
|
*/
|
||||||
|
typedef enum IndexUniqueCheck
|
||||||
|
{
|
||||||
|
UNIQUE_CHECK_NO, /* Don't do any uniqueness checking */
|
||||||
|
UNIQUE_CHECK_YES, /* Enforce uniqueness at insertion time */
|
||||||
|
UNIQUE_CHECK_PARTIAL, /* Test uniqueness, but no error */
|
||||||
|
UNIQUE_CHECK_EXISTING /* Check if existing tuple is unique */
|
||||||
|
} IndexUniqueCheck;
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* generalized index_ interface routines (in indexam.c)
|
* generalized index_ interface routines (in indexam.c)
|
||||||
@ -103,7 +131,7 @@ extern bool index_insert(Relation indexRelation,
|
|||||||
Datum *values, bool *isnull,
|
Datum *values, bool *isnull,
|
||||||
ItemPointer heap_t_ctid,
|
ItemPointer heap_t_ctid,
|
||||||
Relation heapRelation,
|
Relation heapRelation,
|
||||||
bool check_uniqueness);
|
IndexUniqueCheck checkUnique);
|
||||||
|
|
||||||
extern IndexScanDesc index_beginscan(Relation heapRelation,
|
extern IndexScanDesc index_beginscan(Relation heapRelation,
|
||||||
Relation indexRelation,
|
Relation indexRelation,
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* $PostgreSQL: pgsql/src/include/access/nbtree.h,v 1.124 2009/06/11 14:49:08 momjian Exp $
|
* $PostgreSQL: pgsql/src/include/access/nbtree.h,v 1.125 2009/07/29 20:56:19 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -517,8 +517,8 @@ extern Datum btoptions(PG_FUNCTION_ARGS);
|
|||||||
/*
|
/*
|
||||||
* prototypes for functions in nbtinsert.c
|
* prototypes for functions in nbtinsert.c
|
||||||
*/
|
*/
|
||||||
extern void _bt_doinsert(Relation rel, IndexTuple itup,
|
extern bool _bt_doinsert(Relation rel, IndexTuple itup,
|
||||||
bool index_is_unique, Relation heapRel);
|
IndexUniqueCheck checkUnique, Relation heapRel);
|
||||||
extern Buffer _bt_getstackbuf(Relation rel, BTStack stack, int access);
|
extern Buffer _bt_getstackbuf(Relation rel, BTStack stack, int access);
|
||||||
extern void _bt_insert_parent(Relation rel, Buffer buf, Buffer rbuf,
|
extern void _bt_insert_parent(Relation rel, Buffer buf, Buffer rbuf,
|
||||||
BTStack stack, bool is_root, bool is_only);
|
BTStack stack, bool is_root, bool is_only);
|
||||||
|
@ -37,7 +37,7 @@
|
|||||||
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.533 2009/07/28 02:56:30 tgl Exp $
|
* $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.534 2009/07/29 20:56:19 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -53,6 +53,6 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
/* yyyymmddN */
|
/* yyyymmddN */
|
||||||
#define CATALOG_VERSION_NO 200907271
|
#define CATALOG_VERSION_NO 200907291
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* $PostgreSQL: pgsql/src/include/catalog/index.h,v 1.77 2009/01/01 17:23:56 momjian Exp $
|
* $PostgreSQL: pgsql/src/include/catalog/index.h,v 1.78 2009/07/29 20:56:20 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -39,6 +39,8 @@ extern Oid index_create(Oid heapRelationId,
|
|||||||
Datum reloptions,
|
Datum reloptions,
|
||||||
bool isprimary,
|
bool isprimary,
|
||||||
bool isconstraint,
|
bool isconstraint,
|
||||||
|
bool deferrable,
|
||||||
|
bool initdeferred,
|
||||||
bool allow_system_table_mods,
|
bool allow_system_table_mods,
|
||||||
bool skip_build,
|
bool skip_build,
|
||||||
bool concurrent);
|
bool concurrent);
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* $PostgreSQL: pgsql/src/include/catalog/pg_attribute.h,v 1.148 2009/06/11 14:49:09 momjian Exp $
|
* $PostgreSQL: pgsql/src/include/catalog/pg_attribute.h,v 1.149 2009/07/29 20:56:20 tgl Exp $
|
||||||
*
|
*
|
||||||
* NOTES
|
* NOTES
|
||||||
* the genbki.sh script reads this file and generates .bki
|
* the genbki.sh script reads this file and generates .bki
|
||||||
@ -469,14 +469,15 @@ DATA(insert ( 1259 tableoid 26 0 4 -7 0 -1 -1 t p i t f f t 0 _null_));
|
|||||||
{ 0, {"indnatts"}, 21, -1, 2, 3, 0, -1, -1, true, 'p', 's', true, false, false, true, 0, { 0 } }, \
|
{ 0, {"indnatts"}, 21, -1, 2, 3, 0, -1, -1, true, 'p', 's', true, false, false, true, 0, { 0 } }, \
|
||||||
{ 0, {"indisunique"}, 16, -1, 1, 4, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0, { 0 } }, \
|
{ 0, {"indisunique"}, 16, -1, 1, 4, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0, { 0 } }, \
|
||||||
{ 0, {"indisprimary"}, 16, -1, 1, 5, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0, { 0 } }, \
|
{ 0, {"indisprimary"}, 16, -1, 1, 5, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0, { 0 } }, \
|
||||||
{ 0, {"indisclustered"}, 16, -1, 1, 6, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0, { 0 } }, \
|
{ 0, {"indimmediate"}, 16, -1, 1, 6, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0, { 0 } }, \
|
||||||
{ 0, {"indisvalid"}, 16, -1, 1, 7, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0, { 0 } }, \
|
{ 0, {"indisclustered"}, 16, -1, 1, 7, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0, { 0 } }, \
|
||||||
{ 0, {"indcheckxmin"}, 16, -1, 1, 8, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0, { 0 } }, \
|
{ 0, {"indisvalid"}, 16, -1, 1, 8, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0, { 0 } }, \
|
||||||
{ 0, {"indisready"}, 16, -1, 1, 9, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0, { 0 } }, \
|
{ 0, {"indcheckxmin"}, 16, -1, 1, 9, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0, { 0 } }, \
|
||||||
{ 0, {"indkey"}, 22, -1, -1, 10, 1, -1, -1, false, 'p', 'i', true, false, false, true, 0, { 0 } }, \
|
{ 0, {"indisready"}, 16, -1, 1, 10, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0, { 0 } }, \
|
||||||
{ 0, {"indclass"}, 30, -1, -1, 11, 1, -1, -1, false, 'p', 'i', true, false, false, true, 0, { 0 } }, \
|
{ 0, {"indkey"}, 22, -1, -1, 11, 1, -1, -1, false, 'p', 'i', true, false, false, true, 0, { 0 } }, \
|
||||||
{ 0, {"indoption"}, 22, -1, -1, 12, 1, -1, -1, false, 'p', 'i', true, false, false, true, 0, { 0 } }, \
|
{ 0, {"indclass"}, 30, -1, -1, 12, 1, -1, -1, false, 'p', 'i', true, false, false, true, 0, { 0 } }, \
|
||||||
{ 0, {"indexprs"}, 25, -1, -1, 13, 0, -1, -1, false, 'x', 'i', false, false, false, true, 0, { 0 } }, \
|
{ 0, {"indoption"}, 22, -1, -1, 13, 1, -1, -1, false, 'p', 'i', true, false, false, true, 0, { 0 } }, \
|
||||||
{ 0, {"indpred"}, 25, -1, -1, 14, 0, -1, -1, false, 'x', 'i', false, false, false, true, 0, { 0 } }
|
{ 0, {"indexprs"}, 25, -1, -1, 14, 0, -1, -1, false, 'x', 'i', false, false, false, true, 0, { 0 } }, \
|
||||||
|
{ 0, {"indpred"}, 25, -1, -1, 15, 0, -1, -1, false, 'x', 'i', false, false, false, true, 0, { 0 } }
|
||||||
|
|
||||||
#endif /* PG_ATTRIBUTE_H */
|
#endif /* PG_ATTRIBUTE_H */
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* $PostgreSQL: pgsql/src/include/catalog/pg_index.h,v 1.47 2009/01/01 17:23:57 momjian Exp $
|
* $PostgreSQL: pgsql/src/include/catalog/pg_index.h,v 1.48 2009/07/29 20:56:20 tgl Exp $
|
||||||
*
|
*
|
||||||
* NOTES
|
* NOTES
|
||||||
* the genbki.sh script reads this file and generates .bki
|
* the genbki.sh script reads this file and generates .bki
|
||||||
@ -35,6 +35,7 @@ CATALOG(pg_index,2610) BKI_WITHOUT_OIDS
|
|||||||
int2 indnatts; /* number of columns in index */
|
int2 indnatts; /* number of columns in index */
|
||||||
bool indisunique; /* is this a unique index? */
|
bool indisunique; /* is this a unique index? */
|
||||||
bool indisprimary; /* is this index for primary key? */
|
bool indisprimary; /* is this index for primary key? */
|
||||||
|
bool indimmediate; /* is uniqueness enforced immediately? */
|
||||||
bool indisclustered; /* is this the index last clustered by? */
|
bool indisclustered; /* is this the index last clustered by? */
|
||||||
bool indisvalid; /* is this index valid for use by queries? */
|
bool indisvalid; /* is this index valid for use by queries? */
|
||||||
bool indcheckxmin; /* must we wait for xmin to be old? */
|
bool indcheckxmin; /* must we wait for xmin to be old? */
|
||||||
@ -62,21 +63,22 @@ typedef FormData_pg_index *Form_pg_index;
|
|||||||
* compiler constants for pg_index
|
* compiler constants for pg_index
|
||||||
* ----------------
|
* ----------------
|
||||||
*/
|
*/
|
||||||
#define Natts_pg_index 14
|
#define Natts_pg_index 15
|
||||||
#define Anum_pg_index_indexrelid 1
|
#define Anum_pg_index_indexrelid 1
|
||||||
#define Anum_pg_index_indrelid 2
|
#define Anum_pg_index_indrelid 2
|
||||||
#define Anum_pg_index_indnatts 3
|
#define Anum_pg_index_indnatts 3
|
||||||
#define Anum_pg_index_indisunique 4
|
#define Anum_pg_index_indisunique 4
|
||||||
#define Anum_pg_index_indisprimary 5
|
#define Anum_pg_index_indisprimary 5
|
||||||
#define Anum_pg_index_indisclustered 6
|
#define Anum_pg_index_indimmediate 6
|
||||||
#define Anum_pg_index_indisvalid 7
|
#define Anum_pg_index_indisclustered 7
|
||||||
#define Anum_pg_index_indcheckxmin 8
|
#define Anum_pg_index_indisvalid 8
|
||||||
#define Anum_pg_index_indisready 9
|
#define Anum_pg_index_indcheckxmin 9
|
||||||
#define Anum_pg_index_indkey 10
|
#define Anum_pg_index_indisready 10
|
||||||
#define Anum_pg_index_indclass 11
|
#define Anum_pg_index_indkey 11
|
||||||
#define Anum_pg_index_indoption 12
|
#define Anum_pg_index_indclass 12
|
||||||
#define Anum_pg_index_indexprs 13
|
#define Anum_pg_index_indoption 13
|
||||||
#define Anum_pg_index_indpred 14
|
#define Anum_pg_index_indexprs 14
|
||||||
|
#define Anum_pg_index_indpred 15
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Index AMs that support ordered scans must support these two indoption
|
* Index AMs that support ordered scans must support these two indoption
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* $PostgreSQL: pgsql/src/include/catalog/pg_proc.h,v 1.546 2009/07/07 18:49:16 tgl Exp $
|
* $PostgreSQL: pgsql/src/include/catalog/pg_proc.h,v 1.547 2009/07/29 20:56:20 tgl Exp $
|
||||||
*
|
*
|
||||||
* NOTES
|
* NOTES
|
||||||
* The script catalog/genbki.sh reads this file and generates .bki
|
* The script catalog/genbki.sh reads this file and generates .bki
|
||||||
@ -2323,6 +2323,10 @@ DESCR("convert generic options array to name/value table");
|
|||||||
DATA(insert OID = 1619 ( pg_typeof PGNSP PGUID 12 1 0 0 f f f f f s 1 0 2206 "2276" _null_ _null_ _null_ _null_ pg_typeof _null_ _null_ _null_ ));
|
DATA(insert OID = 1619 ( pg_typeof PGNSP PGUID 12 1 0 0 f f f f f s 1 0 2206 "2276" _null_ _null_ _null_ _null_ pg_typeof _null_ _null_ _null_ ));
|
||||||
DESCR("returns the type of the argument");
|
DESCR("returns the type of the argument");
|
||||||
|
|
||||||
|
/* Deferrable unique constraint trigger */
|
||||||
|
DATA(insert OID = 1250 ( unique_key_recheck PGNSP PGUID 12 1 0 0 f f f t f v 0 0 2279 "" _null_ _null_ _null_ _null_ unique_key_recheck _null_ _null_ _null_ ));
|
||||||
|
DESCR("deferred UNIQUE constraint check");
|
||||||
|
|
||||||
/* Generic referential integrity constraint triggers */
|
/* Generic referential integrity constraint triggers */
|
||||||
DATA(insert OID = 1644 ( RI_FKey_check_ins PGNSP PGUID 12 1 0 0 f f f t f v 0 0 2279 "" _null_ _null_ _null_ _null_ RI_FKey_check_ins _null_ _null_ _null_ ));
|
DATA(insert OID = 1644 ( RI_FKey_check_ins PGNSP PGUID 12 1 0 0 f f f t f v 0 0 2279 "" _null_ _null_ _null_ _null_ RI_FKey_check_ins _null_ _null_ _null_ ));
|
||||||
DESCR("referential integrity FOREIGN KEY ... REFERENCES");
|
DESCR("referential integrity FOREIGN KEY ... REFERENCES");
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* $PostgreSQL: pgsql/src/include/commands/defrem.h,v 1.95 2009/07/16 06:33:45 petere Exp $
|
* $PostgreSQL: pgsql/src/include/commands/defrem.h,v 1.96 2009/07/29 20:56:20 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -29,6 +29,8 @@ extern void DefineIndex(RangeVar *heapRelation,
|
|||||||
bool unique,
|
bool unique,
|
||||||
bool primary,
|
bool primary,
|
||||||
bool isconstraint,
|
bool isconstraint,
|
||||||
|
bool deferrable,
|
||||||
|
bool initdeferred,
|
||||||
bool is_alter_table,
|
bool is_alter_table,
|
||||||
bool check_rights,
|
bool check_rights,
|
||||||
bool skip_build,
|
bool skip_build,
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* $PostgreSQL: pgsql/src/include/commands/trigger.h,v 1.74 2009/07/28 02:56:31 tgl Exp $
|
* $PostgreSQL: pgsql/src/include/commands/trigger.h,v 1.75 2009/07/29 20:56:20 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -105,7 +105,7 @@ extern PGDLLIMPORT int SessionReplicationRole;
|
|||||||
#define TRIGGER_DISABLED 'D'
|
#define TRIGGER_DISABLED 'D'
|
||||||
|
|
||||||
extern Oid CreateTrigger(CreateTrigStmt *stmt,
|
extern Oid CreateTrigger(CreateTrigStmt *stmt,
|
||||||
Oid constraintOid, Oid indexOid,
|
Oid constraintOid, Oid indexOid, const char *prefix,
|
||||||
bool checkPermissions);
|
bool checkPermissions);
|
||||||
|
|
||||||
extern void DropTrigger(Oid relid, const char *trigname,
|
extern void DropTrigger(Oid relid, const char *trigname,
|
||||||
@ -132,7 +132,8 @@ extern HeapTuple ExecBRInsertTriggers(EState *estate,
|
|||||||
HeapTuple trigtuple);
|
HeapTuple trigtuple);
|
||||||
extern void ExecARInsertTriggers(EState *estate,
|
extern void ExecARInsertTriggers(EState *estate,
|
||||||
ResultRelInfo *relinfo,
|
ResultRelInfo *relinfo,
|
||||||
HeapTuple trigtuple);
|
HeapTuple trigtuple,
|
||||||
|
List *recheckIndexes);
|
||||||
extern void ExecBSDeleteTriggers(EState *estate,
|
extern void ExecBSDeleteTriggers(EState *estate,
|
||||||
ResultRelInfo *relinfo);
|
ResultRelInfo *relinfo);
|
||||||
extern void ExecASDeleteTriggers(EState *estate,
|
extern void ExecASDeleteTriggers(EState *estate,
|
||||||
@ -154,7 +155,8 @@ extern HeapTuple ExecBRUpdateTriggers(EState *estate,
|
|||||||
extern void ExecARUpdateTriggers(EState *estate,
|
extern void ExecARUpdateTriggers(EState *estate,
|
||||||
ResultRelInfo *relinfo,
|
ResultRelInfo *relinfo,
|
||||||
ItemPointer tupleid,
|
ItemPointer tupleid,
|
||||||
HeapTuple newtuple);
|
HeapTuple newtuple,
|
||||||
|
List *recheckIndexes);
|
||||||
extern void ExecBSTruncateTriggers(EState *estate,
|
extern void ExecBSTruncateTriggers(EState *estate,
|
||||||
ResultRelInfo *relinfo);
|
ResultRelInfo *relinfo);
|
||||||
extern void ExecASTruncateTriggers(EState *estate,
|
extern void ExecASTruncateTriggers(EState *estate,
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* $PostgreSQL: pgsql/src/include/executor/executor.h,v 1.157 2009/07/22 17:00:23 tgl Exp $
|
* $PostgreSQL: pgsql/src/include/executor/executor.h,v 1.158 2009/07/29 20:56:20 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -302,8 +302,8 @@ extern void ExecCloseScanRelation(Relation scanrel);
|
|||||||
|
|
||||||
extern void ExecOpenIndices(ResultRelInfo *resultRelInfo);
|
extern void ExecOpenIndices(ResultRelInfo *resultRelInfo);
|
||||||
extern void ExecCloseIndices(ResultRelInfo *resultRelInfo);
|
extern void ExecCloseIndices(ResultRelInfo *resultRelInfo);
|
||||||
extern void ExecInsertIndexTuples(TupleTableSlot *slot, ItemPointer tupleid,
|
extern List *ExecInsertIndexTuples(TupleTableSlot *slot, ItemPointer tupleid,
|
||||||
EState *estate, bool is_vacuum);
|
EState *estate, bool is_vacuum_full);
|
||||||
|
|
||||||
extern void RegisterExprContextCallback(ExprContext *econtext,
|
extern void RegisterExprContextCallback(ExprContext *econtext,
|
||||||
ExprContextCallbackFunction function,
|
ExprContextCallbackFunction function,
|
||||||
|
@ -13,7 +13,7 @@
|
|||||||
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.398 2009/07/26 23:34:18 tgl Exp $
|
* $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.399 2009/07/29 20:56:21 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -1355,7 +1355,7 @@ typedef struct CreateStmt
|
|||||||
* Constraint attributes (DEFERRABLE etc) are initially represented as
|
* Constraint attributes (DEFERRABLE etc) are initially represented as
|
||||||
* separate Constraint nodes for simplicity of parsing. parse_utilcmd.c makes
|
* separate Constraint nodes for simplicity of parsing. parse_utilcmd.c makes
|
||||||
* a pass through the constraints list to attach the info to the appropriate
|
* a pass through the constraints list to attach the info to the appropriate
|
||||||
* FkConstraint node (and, perhaps, someday to other kinds of constraints).
|
* Constraint and FkConstraint nodes.
|
||||||
* ----------
|
* ----------
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@ -1385,6 +1385,8 @@ typedef struct Constraint
|
|||||||
List *options; /* options from WITH clause */
|
List *options; /* options from WITH clause */
|
||||||
char *indexspace; /* index tablespace for PKEY/UNIQUE
|
char *indexspace; /* index tablespace for PKEY/UNIQUE
|
||||||
* constraints; NULL for default */
|
* constraints; NULL for default */
|
||||||
|
bool deferrable; /* DEFERRABLE */
|
||||||
|
bool initdeferred; /* INITIALLY DEFERRED */
|
||||||
} Constraint;
|
} Constraint;
|
||||||
|
|
||||||
/* ----------
|
/* ----------
|
||||||
@ -1555,12 +1557,11 @@ typedef struct CreateTrigStmt
|
|||||||
/* events uses the TRIGGER_TYPE bits defined in catalog/pg_trigger.h */
|
/* events uses the TRIGGER_TYPE bits defined in catalog/pg_trigger.h */
|
||||||
int16 events; /* INSERT/UPDATE/DELETE/TRUNCATE */
|
int16 events; /* INSERT/UPDATE/DELETE/TRUNCATE */
|
||||||
|
|
||||||
/* The following are used for referential */
|
/* The following are used for constraint triggers (RI and unique checks) */
|
||||||
/* integrity constraint triggers */
|
bool isconstraint; /* This is a constraint trigger */
|
||||||
bool isconstraint; /* This is an RI trigger */
|
|
||||||
bool deferrable; /* [NOT] DEFERRABLE */
|
bool deferrable; /* [NOT] DEFERRABLE */
|
||||||
bool initdeferred; /* INITIALLY {DEFERRED|IMMEDIATE} */
|
bool initdeferred; /* INITIALLY {DEFERRED|IMMEDIATE} */
|
||||||
RangeVar *constrrel; /* opposite relation */
|
RangeVar *constrrel; /* opposite relation, if RI trigger */
|
||||||
} CreateTrigStmt;
|
} CreateTrigStmt;
|
||||||
|
|
||||||
/* ----------------------
|
/* ----------------------
|
||||||
@ -1864,6 +1865,8 @@ typedef struct IndexStmt
|
|||||||
bool unique; /* is index unique? */
|
bool unique; /* is index unique? */
|
||||||
bool primary; /* is index on primary key? */
|
bool primary; /* is index on primary key? */
|
||||||
bool isconstraint; /* is it from a CONSTRAINT clause? */
|
bool isconstraint; /* is it from a CONSTRAINT clause? */
|
||||||
|
bool deferrable; /* is the constraint DEFERRABLE? */
|
||||||
|
bool initdeferred; /* is the constraint INITIALLY DEFERRED? */
|
||||||
bool concurrent; /* should this be a concurrent index build? */
|
bool concurrent; /* should this be a concurrent index build? */
|
||||||
} IndexStmt;
|
} IndexStmt;
|
||||||
|
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* $PostgreSQL: pgsql/src/include/utils/builtins.h,v 1.334 2009/07/16 06:33:46 petere Exp $
|
* $PostgreSQL: pgsql/src/include/utils/builtins.h,v 1.335 2009/07/29 20:56:21 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -1029,6 +1029,9 @@ extern Datum window_nth_value(PG_FUNCTION_ARGS);
|
|||||||
/* access/transam/twophase.c */
|
/* access/transam/twophase.c */
|
||||||
extern Datum pg_prepared_xact(PG_FUNCTION_ARGS);
|
extern Datum pg_prepared_xact(PG_FUNCTION_ARGS);
|
||||||
|
|
||||||
|
/* commands/constraint.c */
|
||||||
|
extern Datum unique_key_recheck(PG_FUNCTION_ARGS);
|
||||||
|
|
||||||
/* commands/prepare.c */
|
/* commands/prepare.c */
|
||||||
extern Datum pg_prepared_statement(PG_FUNCTION_ARGS);
|
extern Datum pg_prepared_statement(PG_FUNCTION_ARGS);
|
||||||
|
|
||||||
|
@ -164,7 +164,8 @@ FROM pg_class c LEFT JOIN pg_namespace n ON n.oid = relnamespace
|
|||||||
WHERE relhasoids
|
WHERE relhasoids
|
||||||
AND ((nspname ~ '^pg_') IS NOT FALSE)
|
AND ((nspname ~ '^pg_') IS NOT FALSE)
|
||||||
AND NOT EXISTS (SELECT 1 FROM pg_index i WHERE indrelid = c.oid
|
AND NOT EXISTS (SELECT 1 FROM pg_index i WHERE indrelid = c.oid
|
||||||
AND indkey[0] = -2 AND indnatts = 1 AND indisunique);
|
AND indkey[0] = -2 AND indnatts = 1
|
||||||
|
AND indisunique AND indimmediate);
|
||||||
relname | nspname
|
relname | nspname
|
||||||
---------+---------
|
---------+---------
|
||||||
(0 rows)
|
(0 rows)
|
||||||
|
@ -259,3 +259,110 @@ SELECT '' AS five, * FROM UNIQUE_TBL;
|
|||||||
|
|
||||||
DROP TABLE UNIQUE_TBL;
|
DROP TABLE UNIQUE_TBL;
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Deferrable unique constraints
|
||||||
|
--
|
||||||
|
|
||||||
|
CREATE TABLE unique_tbl (i int UNIQUE DEFERRABLE, t text);
|
||||||
|
|
||||||
|
INSERT INTO unique_tbl VALUES (0, 'one');
|
||||||
|
INSERT INTO unique_tbl VALUES (1, 'two');
|
||||||
|
INSERT INTO unique_tbl VALUES (2, 'tree');
|
||||||
|
INSERT INTO unique_tbl VALUES (3, 'four');
|
||||||
|
INSERT INTO unique_tbl VALUES (4, 'five');
|
||||||
|
|
||||||
|
BEGIN;
|
||||||
|
|
||||||
|
-- default is immediate so this should fail right away
|
||||||
|
UPDATE unique_tbl SET i = 1 WHERE i = 0;
|
||||||
|
|
||||||
|
ROLLBACK;
|
||||||
|
|
||||||
|
-- check is done at end of statement, so this should succeed
|
||||||
|
UPDATE unique_tbl SET i = i+1;
|
||||||
|
|
||||||
|
SELECT * FROM unique_tbl;
|
||||||
|
|
||||||
|
-- explicitly defer the constraint
|
||||||
|
BEGIN;
|
||||||
|
|
||||||
|
SET CONSTRAINTS unique_tbl_i_key DEFERRED;
|
||||||
|
|
||||||
|
INSERT INTO unique_tbl VALUES (3, 'three');
|
||||||
|
DELETE FROM unique_tbl WHERE t = 'tree'; -- makes constraint valid again
|
||||||
|
|
||||||
|
COMMIT; -- should succeed
|
||||||
|
|
||||||
|
SELECT * FROM unique_tbl;
|
||||||
|
|
||||||
|
-- try adding an initially deferred constraint
|
||||||
|
ALTER TABLE unique_tbl DROP CONSTRAINT unique_tbl_i_key;
|
||||||
|
ALTER TABLE unique_tbl ADD CONSTRAINT unique_tbl_i_key
|
||||||
|
UNIQUE (i) DEFERRABLE INITIALLY DEFERRED;
|
||||||
|
|
||||||
|
BEGIN;
|
||||||
|
|
||||||
|
INSERT INTO unique_tbl VALUES (1, 'five');
|
||||||
|
INSERT INTO unique_tbl VALUES (5, 'one');
|
||||||
|
UPDATE unique_tbl SET i = 4 WHERE i = 2;
|
||||||
|
UPDATE unique_tbl SET i = 2 WHERE i = 4 AND t = 'four';
|
||||||
|
DELETE FROM unique_tbl WHERE i = 1 AND t = 'one';
|
||||||
|
DELETE FROM unique_tbl WHERE i = 5 AND t = 'five';
|
||||||
|
|
||||||
|
COMMIT;
|
||||||
|
|
||||||
|
SELECT * FROM unique_tbl;
|
||||||
|
|
||||||
|
-- should fail at commit-time
|
||||||
|
BEGIN;
|
||||||
|
INSERT INTO unique_tbl VALUES (3, 'Three'); -- should succeed for now
|
||||||
|
COMMIT; -- should fail
|
||||||
|
|
||||||
|
-- make constraint check immediate
|
||||||
|
BEGIN;
|
||||||
|
|
||||||
|
SET CONSTRAINTS ALL IMMEDIATE;
|
||||||
|
|
||||||
|
INSERT INTO unique_tbl VALUES (3, 'Three'); -- should fail
|
||||||
|
|
||||||
|
COMMIT;
|
||||||
|
|
||||||
|
-- forced check when SET CONSTRAINTS is called
|
||||||
|
BEGIN;
|
||||||
|
|
||||||
|
SET CONSTRAINTS ALL DEFERRED;
|
||||||
|
|
||||||
|
INSERT INTO unique_tbl VALUES (3, 'Three'); -- should succeed for now
|
||||||
|
|
||||||
|
SET CONSTRAINTS ALL IMMEDIATE; -- should fail
|
||||||
|
|
||||||
|
COMMIT;
|
||||||
|
|
||||||
|
-- test a HOT update that invalidates the conflicting tuple.
|
||||||
|
-- the trigger should still fire and catch the violation
|
||||||
|
|
||||||
|
BEGIN;
|
||||||
|
|
||||||
|
INSERT INTO unique_tbl VALUES (3, 'Three'); -- should succeed for now
|
||||||
|
UPDATE unique_tbl SET t = 'THREE' WHERE i = 3 AND t = 'Three';
|
||||||
|
|
||||||
|
COMMIT; -- should fail
|
||||||
|
|
||||||
|
SELECT * FROM unique_tbl;
|
||||||
|
|
||||||
|
-- test a HOT update that modifies the newly inserted tuple,
|
||||||
|
-- but should succeed because we then remove the other conflicting tuple.
|
||||||
|
|
||||||
|
BEGIN;
|
||||||
|
|
||||||
|
INSERT INTO unique_tbl VALUES(3, 'tree'); -- should succeed for now
|
||||||
|
UPDATE unique_tbl SET t = 'threex' WHERE t = 'tree';
|
||||||
|
DELETE FROM unique_tbl WHERE t = 'three';
|
||||||
|
|
||||||
|
SELECT * FROM unique_tbl;
|
||||||
|
|
||||||
|
COMMIT;
|
||||||
|
|
||||||
|
SELECT * FROM unique_tbl;
|
||||||
|
|
||||||
|
DROP TABLE unique_tbl;
|
||||||
|
@ -375,3 +375,132 @@ SELECT '' AS five, * FROM UNIQUE_TBL;
|
|||||||
(5 rows)
|
(5 rows)
|
||||||
|
|
||||||
DROP TABLE UNIQUE_TBL;
|
DROP TABLE UNIQUE_TBL;
|
||||||
|
--
|
||||||
|
-- Deferrable unique constraints
|
||||||
|
--
|
||||||
|
CREATE TABLE unique_tbl (i int UNIQUE DEFERRABLE, t text);
|
||||||
|
NOTICE: CREATE TABLE / UNIQUE will create implicit index "unique_tbl_i_key" for table "unique_tbl"
|
||||||
|
INSERT INTO unique_tbl VALUES (0, 'one');
|
||||||
|
INSERT INTO unique_tbl VALUES (1, 'two');
|
||||||
|
INSERT INTO unique_tbl VALUES (2, 'tree');
|
||||||
|
INSERT INTO unique_tbl VALUES (3, 'four');
|
||||||
|
INSERT INTO unique_tbl VALUES (4, 'five');
|
||||||
|
BEGIN;
|
||||||
|
-- default is immediate so this should fail right away
|
||||||
|
UPDATE unique_tbl SET i = 1 WHERE i = 0;
|
||||||
|
ERROR: duplicate key value violates unique constraint "unique_tbl_i_key"
|
||||||
|
ROLLBACK;
|
||||||
|
-- check is done at end of statement, so this should succeed
|
||||||
|
UPDATE unique_tbl SET i = i+1;
|
||||||
|
SELECT * FROM unique_tbl;
|
||||||
|
i | t
|
||||||
|
---+------
|
||||||
|
1 | one
|
||||||
|
2 | two
|
||||||
|
3 | tree
|
||||||
|
4 | four
|
||||||
|
5 | five
|
||||||
|
(5 rows)
|
||||||
|
|
||||||
|
-- explicitly defer the constraint
|
||||||
|
BEGIN;
|
||||||
|
SET CONSTRAINTS unique_tbl_i_key DEFERRED;
|
||||||
|
INSERT INTO unique_tbl VALUES (3, 'three');
|
||||||
|
DELETE FROM unique_tbl WHERE t = 'tree'; -- makes constraint valid again
|
||||||
|
COMMIT; -- should succeed
|
||||||
|
SELECT * FROM unique_tbl;
|
||||||
|
i | t
|
||||||
|
---+-------
|
||||||
|
1 | one
|
||||||
|
2 | two
|
||||||
|
4 | four
|
||||||
|
5 | five
|
||||||
|
3 | three
|
||||||
|
(5 rows)
|
||||||
|
|
||||||
|
-- try adding an initially deferred constraint
|
||||||
|
ALTER TABLE unique_tbl DROP CONSTRAINT unique_tbl_i_key;
|
||||||
|
ALTER TABLE unique_tbl ADD CONSTRAINT unique_tbl_i_key
|
||||||
|
UNIQUE (i) DEFERRABLE INITIALLY DEFERRED;
|
||||||
|
NOTICE: ALTER TABLE / ADD UNIQUE will create implicit index "unique_tbl_i_key" for table "unique_tbl"
|
||||||
|
BEGIN;
|
||||||
|
INSERT INTO unique_tbl VALUES (1, 'five');
|
||||||
|
INSERT INTO unique_tbl VALUES (5, 'one');
|
||||||
|
UPDATE unique_tbl SET i = 4 WHERE i = 2;
|
||||||
|
UPDATE unique_tbl SET i = 2 WHERE i = 4 AND t = 'four';
|
||||||
|
DELETE FROM unique_tbl WHERE i = 1 AND t = 'one';
|
||||||
|
DELETE FROM unique_tbl WHERE i = 5 AND t = 'five';
|
||||||
|
COMMIT;
|
||||||
|
SELECT * FROM unique_tbl;
|
||||||
|
i | t
|
||||||
|
---+-------
|
||||||
|
3 | three
|
||||||
|
1 | five
|
||||||
|
5 | one
|
||||||
|
4 | two
|
||||||
|
2 | four
|
||||||
|
(5 rows)
|
||||||
|
|
||||||
|
-- should fail at commit-time
|
||||||
|
BEGIN;
|
||||||
|
INSERT INTO unique_tbl VALUES (3, 'Three'); -- should succeed for now
|
||||||
|
COMMIT; -- should fail
|
||||||
|
ERROR: duplicate key value violates unique constraint "unique_tbl_i_key"
|
||||||
|
-- make constraint check immediate
|
||||||
|
BEGIN;
|
||||||
|
SET CONSTRAINTS ALL IMMEDIATE;
|
||||||
|
INSERT INTO unique_tbl VALUES (3, 'Three'); -- should fail
|
||||||
|
ERROR: duplicate key value violates unique constraint "unique_tbl_i_key"
|
||||||
|
COMMIT;
|
||||||
|
-- forced check when SET CONSTRAINTS is called
|
||||||
|
BEGIN;
|
||||||
|
SET CONSTRAINTS ALL DEFERRED;
|
||||||
|
INSERT INTO unique_tbl VALUES (3, 'Three'); -- should succeed for now
|
||||||
|
SET CONSTRAINTS ALL IMMEDIATE; -- should fail
|
||||||
|
ERROR: duplicate key value violates unique constraint "unique_tbl_i_key"
|
||||||
|
COMMIT;
|
||||||
|
-- test a HOT update that invalidates the conflicting tuple.
|
||||||
|
-- the trigger should still fire and catch the violation
|
||||||
|
BEGIN;
|
||||||
|
INSERT INTO unique_tbl VALUES (3, 'Three'); -- should succeed for now
|
||||||
|
UPDATE unique_tbl SET t = 'THREE' WHERE i = 3 AND t = 'Three';
|
||||||
|
COMMIT; -- should fail
|
||||||
|
ERROR: duplicate key value violates unique constraint "unique_tbl_i_key"
|
||||||
|
SELECT * FROM unique_tbl;
|
||||||
|
i | t
|
||||||
|
---+-------
|
||||||
|
3 | three
|
||||||
|
1 | five
|
||||||
|
5 | one
|
||||||
|
4 | two
|
||||||
|
2 | four
|
||||||
|
(5 rows)
|
||||||
|
|
||||||
|
-- test a HOT update that modifies the newly inserted tuple,
|
||||||
|
-- but should succeed because we then remove the other conflicting tuple.
|
||||||
|
BEGIN;
|
||||||
|
INSERT INTO unique_tbl VALUES(3, 'tree'); -- should succeed for now
|
||||||
|
UPDATE unique_tbl SET t = 'threex' WHERE t = 'tree';
|
||||||
|
DELETE FROM unique_tbl WHERE t = 'three';
|
||||||
|
SELECT * FROM unique_tbl;
|
||||||
|
i | t
|
||||||
|
---+--------
|
||||||
|
1 | five
|
||||||
|
5 | one
|
||||||
|
4 | two
|
||||||
|
2 | four
|
||||||
|
3 | threex
|
||||||
|
(5 rows)
|
||||||
|
|
||||||
|
COMMIT;
|
||||||
|
SELECT * FROM unique_tbl;
|
||||||
|
i | t
|
||||||
|
---+--------
|
||||||
|
1 | five
|
||||||
|
5 | one
|
||||||
|
4 | two
|
||||||
|
2 | four
|
||||||
|
3 | threex
|
||||||
|
(5 rows)
|
||||||
|
|
||||||
|
DROP TABLE unique_tbl;
|
||||||
|
@ -22,4 +22,5 @@ FROM pg_class c LEFT JOIN pg_namespace n ON n.oid = relnamespace
|
|||||||
WHERE relhasoids
|
WHERE relhasoids
|
||||||
AND ((nspname ~ '^pg_') IS NOT FALSE)
|
AND ((nspname ~ '^pg_') IS NOT FALSE)
|
||||||
AND NOT EXISTS (SELECT 1 FROM pg_index i WHERE indrelid = c.oid
|
AND NOT EXISTS (SELECT 1 FROM pg_index i WHERE indrelid = c.oid
|
||||||
AND indkey[0] = -2 AND indnatts = 1 AND indisunique);
|
AND indkey[0] = -2 AND indnatts = 1
|
||||||
|
AND indisunique AND indimmediate);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user