2009-07-29 22:56:21 +02:00
|
|
|
/*-------------------------------------------------------------------------
|
|
|
|
*
|
|
|
|
* constraint.c
|
|
|
|
* PostgreSQL CONSTRAINT support code.
|
|
|
|
*
|
2019-01-02 18:44:25 +01:00
|
|
|
* Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
|
2009-07-29 22:56:21 +02:00
|
|
|
* Portions Copyright (c) 1994, Regents of the University of California
|
|
|
|
*
|
|
|
|
* IDENTIFICATION
|
2010-09-20 22:08:53 +02:00
|
|
|
* src/backend/commands/constraint.c
|
2009-07-29 22:56:21 +02:00
|
|
|
*
|
|
|
|
*-------------------------------------------------------------------------
|
|
|
|
*/
|
|
|
|
#include "postgres.h"
|
|
|
|
|
2019-01-15 02:02:12 +01:00
|
|
|
#include "access/genam.h"
|
Don't include heapam.h from others headers.
heapam.h previously was included in a number of widely used
headers (e.g. execnodes.h, indirectly in executor.h, ...). That's
problematic on its own, as heapam.h contains a lot of low-level
details that don't need to be exposed that widely, but becomes more
problematic with the upcoming introduction of pluggable table storage
- it seems inappropriate for heapam.h to be included that widely
afterwards.
heapam.h was largely only included in other headers to get the
HeapScanDesc typedef (which was defined in heapam.h, even though
HeapScanDescData is defined in relscan.h). The better solution here
seems to be to just use the underlying struct (forward declared where
necessary). Similar for BulkInsertState.
Another problem was that LockTupleMode was used in executor.h - parts
of the file tried to cope without heapam.h, but due to the fact that
it indirectly included it, several subsequent violations of that goal
were not not noticed. We could just reuse the approach of declaring
parameters as int, but it seems nicer to move LockTupleMode to
lockoptions.h - that's not a perfect location, but also doesn't seem
bad.
As a number of files relied on implicitly included heapam.h, a
significant number of files grew an explicit include. It's quite
probably that a few external projects will need to do the same.
Author: Andres Freund
Reviewed-By: Alvaro Herrera
Discussion: https://postgr.es/m/20190114000701.y4ttcb74jpskkcfb@alap3.anarazel.de
2019-01-15 00:54:18 +01:00
|
|
|
#include "access/heapam.h"
|
2009-07-29 22:56:21 +02:00
|
|
|
#include "catalog/index.h"
|
|
|
|
#include "commands/trigger.h"
|
|
|
|
#include "executor/executor.h"
|
|
|
|
#include "utils/builtins.h"
|
2011-02-23 18:18:09 +01:00
|
|
|
#include "utils/rel.h"
|
2019-01-22 02:03:15 +01:00
|
|
|
#include "utils/snapmgr.h"
|
2009-07-29 22:56:21 +02:00
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* unique_key_recheck - trigger function to do a deferred uniqueness check.
|
|
|
|
*
|
2009-12-07 06:22:23 +01:00
|
|
|
* This now also does deferred exclusion-constraint checks, so the name is
|
|
|
|
* somewhat historical.
|
|
|
|
*
|
2009-07-29 22:56:21 +02:00
|
|
|
* This is invoked as an AFTER ROW trigger for both INSERT and UPDATE,
|
|
|
|
* for any rows recorded as potentially violating a deferrable unique
|
2009-12-07 06:22:23 +01:00
|
|
|
* or exclusion constraint.
|
2009-07-29 22:56:21 +02:00
|
|
|
*
|
|
|
|
* 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)
|
|
|
|
{
|
2017-01-27 01:47:03 +01:00
|
|
|
TriggerData *trigdata = castNode(TriggerData, fcinfo->context);
|
2009-07-29 22:56:21 +02:00
|
|
|
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];
|
|
|
|
|
|
|
|
/*
|
2014-05-06 18:12:18 +02:00
|
|
|
* Make sure this is being called as an AFTER ROW trigger. Note:
|
2010-02-26 03:01:40 +01:00
|
|
|
* translatable error strings are shared with ri_triggers.c, so resist the
|
|
|
|
* temptation to fold the function name into them.
|
2009-07-29 22:56:21 +02:00
|
|
|
*/
|
|
|
|
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;
|
2014-05-06 18:12:18 +02:00
|
|
|
* which means it does not get queued for HOT updates. The row we are
|
2009-07-29 22:56:21 +02:00
|
|
|
* called for might now be dead, but have a live HOT child, in which case
|
2015-05-11 18:25:28 +02:00
|
|
|
* we still need to make the check --- effectively, we're applying the
|
|
|
|
* check against the live child row, although we can use the values from
|
|
|
|
* this row since by definition all columns of interest to us are the
|
|
|
|
* same.
|
2009-07-29 22:56:21 +02:00
|
|
|
*
|
|
|
|
* 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);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2010-02-26 03:01:40 +01:00
|
|
|
* 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.)
|
2009-07-29 22:56:21 +02:00
|
|
|
*/
|
|
|
|
indexRel = index_open(trigdata->tg_trigger->tgconstrindid,
|
|
|
|
RowExclusiveLock);
|
|
|
|
indexInfo = BuildIndexInfo(indexRel);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The heap tuple must be put into a slot for FormIndexDatum.
|
|
|
|
*/
|
Introduce notion of different types of slots (without implementing them).
Upcoming work intends to allow pluggable ways to introduce new ways of
storing table data. Accessing those table access methods from the
executor requires TupleTableSlots to be carry tuples in the native
format of such storage methods; otherwise there'll be a significant
conversion overhead.
Different access methods will require different data to store tuples
efficiently (just like virtual, minimal, heap already require fields
in TupleTableSlot). To allow that without requiring additional pointer
indirections, we want to have different structs (embedding
TupleTableSlot) for different types of slots. Thus different types of
slots are needed, which requires adapting creators of slots.
The slot that most efficiently can represent a type of tuple in an
executor node will often depend on the type of slot a child node
uses. Therefore we need to track the type of slot is returned by
nodes, so parent slots can create slots based on that.
Relatedly, JIT compilation of tuple deforming needs to know which type
of slot a certain expression refers to, so it can create an
appropriate deforming function for the type of tuple in the slot.
But not all nodes will only return one type of slot, e.g. an append
node will potentially return different types of slots for each of its
subplans.
Therefore add function that allows to query the type of a node's
result slot, and whether it'll always be the same type (whether it's
fixed). This can be queried using ExecGetResultSlotOps().
The scan, result, inner, outer type of slots are automatically
inferred from ExecInitScanTupleSlot(), ExecInitResultSlot(),
left/right subtrees respectively. If that's not correct for a node,
that can be overwritten using new fields in PlanState.
This commit does not introduce the actually abstracted implementation
of different kind of TupleTableSlots, that will be left for a followup
commit. The different types of slots introduced will, for now, still
use the same backing implementation.
While this already partially invalidates the big comment in
tuptable.h, it seems to make more sense to update it later, when the
different TupleTableSlot implementations actually exist.
Author: Ashutosh Bapat and Andres Freund, with changes by Amit Khandekar
Discussion: https://postgr.es/m/20181105210039.hh4vvi4vwoq5ba2q@alap3.anarazel.de
2018-11-16 07:00:30 +01:00
|
|
|
slot = MakeSingleTupleTableSlot(RelationGetDescr(trigdata->tg_relation),
|
|
|
|
&TTSOpsHeapTuple);
|
2009-07-29 22:56:21 +02:00
|
|
|
|
2018-09-26 01:27:48 +02:00
|
|
|
ExecStoreHeapTuple(new_row, slot, false);
|
2009-07-29 22:56:21 +02:00
|
|
|
|
|
|
|
/*
|
2010-02-26 03:01:40 +01:00
|
|
|
* Typically the index won't have expressions, but if it does we need an
|
|
|
|
* EState to evaluate them. We need it for exclusion constraints too,
|
|
|
|
* even if they are just on simple columns.
|
2009-07-29 22:56:21 +02:00
|
|
|
*/
|
2009-12-07 06:22:23 +01:00
|
|
|
if (indexInfo->ii_Expressions != NIL ||
|
|
|
|
indexInfo->ii_ExclusionOps != NULL)
|
2009-07-29 22:56:21 +02:00
|
|
|
{
|
|
|
|
estate = CreateExecutorState();
|
|
|
|
econtext = GetPerTupleExprContext(estate);
|
|
|
|
econtext->ecxt_scantuple = slot;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
estate = NULL;
|
|
|
|
|
|
|
|
/*
|
2010-02-26 03:01:40 +01:00
|
|
|
* Form the index values and isnull flags for the index entry that we need
|
|
|
|
* to check.
|
2009-07-29 22:56:21 +02:00
|
|
|
*
|
2010-02-26 03:01:40 +01:00
|
|
|
* 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. For exclusion
|
2009-12-07 06:22:23 +01:00
|
|
|
* constraints we check this in check_exclusion_constraint().
|
2009-07-29 22:56:21 +02:00
|
|
|
*/
|
|
|
|
FormIndexDatum(indexInfo, slot, estate, values, isnull);
|
|
|
|
|
|
|
|
/*
|
2009-12-07 06:22:23 +01:00
|
|
|
* Now do the appropriate check.
|
2009-07-29 22:56:21 +02:00
|
|
|
*/
|
2009-12-07 06:22:23 +01:00
|
|
|
if (indexInfo->ii_ExclusionOps == NULL)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* Note: this is not a real insert; it is a check that the index entry
|
2015-05-11 18:25:28 +02:00
|
|
|
* that has already been inserted is unique. Passing t_self is
|
|
|
|
* correct even if t_self is now dead, because that is the TID the
|
|
|
|
* index will know about.
|
2009-12-07 06:22:23 +01:00
|
|
|
*/
|
|
|
|
index_insert(indexRel, values, isnull, &(new_row->t_self),
|
Allow index AMs to cache data across aminsert calls within a SQL command.
It's always been possible for index AMs to cache data across successive
amgettuple calls within a single SQL command: the IndexScanDesc.opaque
field is meant for precisely that. However, no comparable facility
exists for amortizing setup work across successive aminsert calls.
This patch adds such a feature and teaches GIN, GIST, and BRIN to use it
to amortize catalog lookups they'd previously been doing on every call.
(The other standard index AMs keep everything they need in the relcache,
so there's little to improve there.)
For GIN, the overall improvement in a statement that inserts many rows
can be as much as 10%, though it seems a bit less for the other two.
In addition, this makes a really significant difference in runtime
for CLOBBER_CACHE_ALWAYS tests, since in those builds the repeated
catalog lookups are vastly more expensive.
The reason this has been hard up to now is that the aminsert function is
not passed any useful place to cache per-statement data. What I chose to
do is to add suitable fields to struct IndexInfo and pass that to aminsert.
That's not widening the index AM API very much because IndexInfo is already
within the ken of ambuild; in fact, by passing the same info to aminsert
as to ambuild, this is really removing an inconsistency in the AM API.
Discussion: https://postgr.es/m/27568.1486508680@sss.pgh.pa.us
2017-02-09 17:52:12 +01:00
|
|
|
trigdata->tg_relation, UNIQUE_CHECK_EXISTING,
|
|
|
|
indexInfo);
|
2009-12-07 06:22:23 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/*
|
2010-02-26 03:01:40 +01:00
|
|
|
* For exclusion constraints we just do the normal check, but now it's
|
2015-05-11 18:25:28 +02:00
|
|
|
* okay to throw error. In the HOT-update case, we must use the live
|
|
|
|
* HOT child's TID here, else check_exclusion_constraint will think
|
|
|
|
* the child is a conflict.
|
2009-12-07 06:22:23 +01:00
|
|
|
*/
|
|
|
|
check_exclusion_constraint(trigdata->tg_relation, indexRel, indexInfo,
|
2015-05-11 18:25:28 +02:00
|
|
|
&tmptid, values, isnull,
|
Add support for INSERT ... ON CONFLICT DO NOTHING/UPDATE.
The newly added ON CONFLICT clause allows to specify an alternative to
raising a unique or exclusion constraint violation error when inserting.
ON CONFLICT refers to constraints that can either be specified using a
inference clause (by specifying the columns of a unique constraint) or
by naming a unique or exclusion constraint. DO NOTHING avoids the
constraint violation, without touching the pre-existing row. DO UPDATE
SET ... [WHERE ...] updates the pre-existing tuple, and has access to
both the tuple proposed for insertion and the existing tuple; the
optional WHERE clause can be used to prevent an update from being
executed. The UPDATE SET and WHERE clauses have access to the tuple
proposed for insertion using the "magic" EXCLUDED alias, and to the
pre-existing tuple using the table name or its alias.
This feature is often referred to as upsert.
This is implemented using a new infrastructure called "speculative
insertion". It is an optimistic variant of regular insertion that first
does a pre-check for existing tuples and then attempts an insert. If a
violating tuple was inserted concurrently, the speculatively inserted
tuple is deleted and a new attempt is made. If the pre-check finds a
matching tuple the alternative DO NOTHING or DO UPDATE action is taken.
If the insertion succeeds without detecting a conflict, the tuple is
deemed inserted.
To handle the possible ambiguity between the excluded alias and a table
named excluded, and for convenience with long relation names, INSERT
INTO now can alias its target table.
Bumps catversion as stored rules change.
Author: Peter Geoghegan, with significant contributions from Heikki
Linnakangas and Andres Freund. Testing infrastructure by Jeff Janes.
Reviewed-By: Heikki Linnakangas, Andres Freund, Robert Haas, Simon Riggs,
Dean Rasheed, Stephen Frost and many others.
2015-05-08 05:31:36 +02:00
|
|
|
estate, false);
|
2009-12-07 06:22:23 +01:00
|
|
|
}
|
2009-07-29 22:56:21 +02:00
|
|
|
|
|
|
|
/*
|
2010-02-26 03:01:40 +01:00
|
|
|
* If that worked, then this index entry is unique or non-excluded, and we
|
|
|
|
* are done.
|
2009-07-29 22:56:21 +02:00
|
|
|
*/
|
|
|
|
if (estate != NULL)
|
|
|
|
FreeExecutorState(estate);
|
|
|
|
|
|
|
|
ExecDropSingleTupleTableSlot(slot);
|
|
|
|
|
|
|
|
index_close(indexRel, RowExclusiveLock);
|
|
|
|
|
|
|
|
return PointerGetDatum(NULL);
|
|
|
|
}
|