From b9556286b3710234a32ce48e8d163d5e844154b8 Mon Sep 17 00:00:00 2001 From: Peter Geoghegan Date: Fri, 26 Sep 2014 20:59:04 -0700 Subject: [PATCH 6/6] User-visible documentation for INSERT ... ON CONFLICT {UPDATE | IGNORE} INSERT ... ON CONFLICT {UPDATE | IGNORE} is documented as a new clause of the INSERT command. Some potentially surprising interactions with triggers are noted -- BEFORE INSERT per-row triggers must fire without the INSERT path necessarily being taken, for example. All the existing features that INSERT ... ON CONFLICT {UPDATE | IGNORE} fails to completely play nice with have those limitations noted. (Notes are added to the existing documentation for those other features, although some of these cases will need to be revisited). This includes postgres_fdw, updatable views and table inheritance (although these have most interesting cases covered, particularly inheritance). Finally, a user-level description of the new "MVCC violation" that the ON CONFLICT UPDATE variant sometimes requires has been added to "Chapter 13 - Concurrency Control", beside existing commentary on READ COMMITTED mode's special handling of concurrent updates. The new "MVCC violation" introduced seems somewhat distinct from the existing one (i.e. what is internally referred to as the EvalPlanQual() mechanism), because in READ COMMITTED mode it is no longer necessary for any row version to be conventionally visible to the command's MVCC snapshot for an UPDATE of the row to occur (or for the row to be locked, should the WHERE clause predicate not be satisfied). --- doc/src/sgml/ddl.sgml | 23 +++ doc/src/sgml/fdwhandler.sgml | 8 ++ doc/src/sgml/indices.sgml | 11 +- doc/src/sgml/keywords.sgml | 7 + doc/src/sgml/mvcc.sgml | 24 ++++ doc/src/sgml/plpgsql.sgml | 14 +- doc/src/sgml/postgres-fdw.sgml | 8 ++ doc/src/sgml/ref/create_index.sgml | 7 +- doc/src/sgml/ref/create_rule.sgml | 6 +- doc/src/sgml/ref/create_table.sgml | 5 +- doc/src/sgml/ref/create_trigger.sgml | 5 +- doc/src/sgml/ref/create_view.sgml | 33 ++++- doc/src/sgml/ref/insert.sgml | 262 +++++++++++++++++++++++++++++++--- doc/src/sgml/ref/set_constraints.sgml | 6 +- doc/src/sgml/trigger.sgml | 29 +++- 15 files changed, 419 insertions(+), 29 deletions(-) diff --git a/doc/src/sgml/ddl.sgml b/doc/src/sgml/ddl.sgml index f9dc151..1bf3537 100644 --- a/doc/src/sgml/ddl.sgml +++ b/doc/src/sgml/ddl.sgml @@ -2428,9 +2428,27 @@ VALUES ('Albany', NULL, NULL, 'NY'); + There is limited inheritance support for INSERT + commands with ON CONFLICT clauses. Tables with + children are not generally accepted as targets. One notable + exception is that such tables are accepted as targets for + INSERT commands with ON CONFLICT + IGNORE clauses, provided a unique index inference clause was + omitted (which implies that there is no concern about + which unique index any would-be conflict might arise + from). However, tables that happen to be inheritance children are + accepted as targets for all variants of INSERT + with ON CONFLICT. + + + All check constraints and not-null constraints on a parent table are automatically inherited by its children. Other types of constraints (unique, primary key, and foreign key constraints) are not inherited. + Therefore, INSERT with ON CONFLICT + unique index inference considers only unique constraints/indexes + directly associated with the child + table. @@ -2515,6 +2533,11 @@ VALUES ('Albany', NULL, NULL, 'NY'); not INSERT or ALTER TABLE ... RENAME) typically default to including child tables and support the ONLY notation to exclude them. + INSERT with an ON CONFLICT + UPDATE clause does not support the + ONLY notation, in effect tables with inheritance + children are not supported for that ON CONFLICT + variant. Commands that do database maintenance and tuning (e.g., REINDEX, VACUUM) typically only work on individual, physical tables and do not diff --git a/doc/src/sgml/fdwhandler.sgml b/doc/src/sgml/fdwhandler.sgml index c1daa4b..0c3dcb5 100644 --- a/doc/src/sgml/fdwhandler.sgml +++ b/doc/src/sgml/fdwhandler.sgml @@ -1014,6 +1014,14 @@ GetForeignServerByName(const char *name, bool missing_ok); source provides. + + INSERT with an ON CONFLICT clause is not supported + with a unique index inference specification (this implies that ON + CONFLICT UPDATE is never supported, since the specification is + mandatory there). When planning an INSERT, + PlanForeignModify should reject these cases. + + diff --git a/doc/src/sgml/indices.sgml b/doc/src/sgml/indices.sgml index 64530a1..e6b9112 100644 --- a/doc/src/sgml/indices.sgml +++ b/doc/src/sgml/indices.sgml @@ -922,7 +922,16 @@ CREATE UNIQUE INDEX tests_success_constraint ON tests (subject, target) know when an index might be profitable. Forming this knowledge requires experience and understanding of how indexes in PostgreSQL work. In most cases, the advantage of a - partial index over a regular index will be minimal. + partial index over a regular index will be minimal. Also, note + that partial unique indexes are not compatible with INSERT with an + ON CONFLICT UPDATE clause, if it is expected that a + would-be uniqueness violation associated with the partial index + should provoke an alternative UPDATE or + IGNORE path, and an explicit condition for taking the + alternative path was specified (as it must be with the + UPDATE variant). Unique index inference will never + infer that a partial unique index is appropriate due to + implementation-specific restrictions. diff --git a/doc/src/sgml/keywords.sgml b/doc/src/sgml/keywords.sgml index b0dfd5f..ea58211 100644 --- a/doc/src/sgml/keywords.sgml +++ b/doc/src/sgml/keywords.sgml @@ -854,6 +854,13 @@ + CONFLICT + non-reserved + + + + + CONNECT reserved diff --git a/doc/src/sgml/mvcc.sgml b/doc/src/sgml/mvcc.sgml index cd55be8..0248829 100644 --- a/doc/src/sgml/mvcc.sgml +++ b/doc/src/sgml/mvcc.sgml @@ -326,6 +326,30 @@ + INSERT with an ON CONFLICT UPDATE clause is + another special case. In Read Committed mode, the implementation will + either insert or update each row proposed for insertion, with either one of + those two outcomes guaranteed. This is a useful guarantee for many + use-cases, but it implies that further liberties must be taken with + snapshot isolation. Should a conflict originate in another transaction + whose effects are not visible to the INSERT, the + UPDATE may affect that row, even though it may be the + case that no version of that row is conventionally visible to + the command. In the same vein, if the secondary search condition of the + command (an explicit WHERE clause) is supplied, it is only + evaluated on the most recent row version, which is not necessarily the + version conventionally visible to the command (if indeed there is a row + version conventionally visible to the command at all). + + + + INSERT with an ON CONFLICT IGNORE clause may + have insertion not proceed for a row due to the outcome of another + transaction whose effects are not visible to the INSERT + snapshot. Again, this is only the case in Read Committed mode. + + + Because of the above rule, it is possible for an updating command to see an inconsistent snapshot: it can see the effects of concurrent updating commands on the same rows it is trying to update, but it diff --git a/doc/src/sgml/plpgsql.sgml b/doc/src/sgml/plpgsql.sgml index f195495..c4d9004 100644 --- a/doc/src/sgml/plpgsql.sgml +++ b/doc/src/sgml/plpgsql.sgml @@ -2599,7 +2599,11 @@ END; This example uses exception handling to perform either - UPDATE or INSERT, as appropriate: + UPDATE or INSERT, as appropriate. It is + recommended that applications use INSERT with + ON CONFLICT UPDATE rather than actually emulating this + pattern. This example serves only to illustrate use of + PL/pgSQL control flow structures: CREATE TABLE db (a INT PRIMARY KEY, b TEXT); @@ -3754,9 +3758,11 @@ RAISE unique_violation USING MESSAGE = 'Duplicate user ID: ' || user_id; INSERT and UPDATE operations, the return value should be NEW, which the trigger function may modify to support INSERT RETURNING and UPDATE RETURNING - (this will also affect the row value passed to any subsequent triggers). - For DELETE operations, the return value should be - OLD. + (this will also affect the row value passed to any subsequent triggers, + or passed to a special EXCLUDED alias reference within + an INSERT statement with an ON CONFLICT UPDATE + clause). For DELETE operations, the return + value should be OLD. diff --git a/doc/src/sgml/postgres-fdw.sgml b/doc/src/sgml/postgres-fdw.sgml index 43adb61..fa39661 100644 --- a/doc/src/sgml/postgres-fdw.sgml +++ b/doc/src/sgml/postgres-fdw.sgml @@ -69,6 +69,14 @@ + Note that postgres_fdw currently lacks support for + INSERT statements with an ON CONFLICT + UPDATE clause. However, the ON CONFLICT IGNORE + clause is supported, provided a unique index inference specification + is omitted. + + + It is generally recommended that the columns of a foreign table be declared with exactly the same data types, and collations if applicable, as the referenced columns of the remote table. Although postgres_fdw diff --git a/doc/src/sgml/ref/create_index.sgml b/doc/src/sgml/ref/create_index.sgml index 43df32f..1bebf4c 100644 --- a/doc/src/sgml/ref/create_index.sgml +++ b/doc/src/sgml/ref/create_index.sgml @@ -72,7 +72,12 @@ CREATE [ UNIQUE ] INDEX [ CONCURRENTLY ] [ nameWHERE with UNIQUE to enforce uniqueness over a subset of a - table. See for more discussion. + table. Note, however, that partial unique indexes are not + compatible with INSERT with an ON CONFLICT UPDATE + clause (if it is expected that a would-be uniqueness violation + associated with the partial index should provoke an alternative + UPDATE/IGNORE path). See for more discussion. diff --git a/doc/src/sgml/ref/create_rule.sgml b/doc/src/sgml/ref/create_rule.sgml index 677766a..9b5c740 100644 --- a/doc/src/sgml/ref/create_rule.sgml +++ b/doc/src/sgml/ref/create_rule.sgml @@ -136,7 +136,11 @@ CREATE [ OR REPLACE ] RULE name AS The event is one of SELECT, INSERT, UPDATE, or - DELETE. + DELETE. Note that an + INSERT containing an ON + CONFLICT clause is unsupported. Consider using an + updatable view instead, which have limited support for + ON CONFLICT IGNORE only. diff --git a/doc/src/sgml/ref/create_table.sgml b/doc/src/sgml/ref/create_table.sgml index 299cce8..a9c1124 100644 --- a/doc/src/sgml/ref/create_table.sgml +++ b/doc/src/sgml/ref/create_table.sgml @@ -708,7 +708,10 @@ CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } | UNLOGGED ] TABLE [ IF NOT EXI EXCLUDE, and REFERENCES (foreign key) constraints accept this clause. NOT NULL and CHECK constraints are not - deferrable. + deferrable. Note that constraints that were created with this + clause cannot be used as arbiters of whether or not to take the + alternative path with an INSERT statement + that includes an ON CONFLICT UPDATE clause. diff --git a/doc/src/sgml/ref/create_trigger.sgml b/doc/src/sgml/ref/create_trigger.sgml index 29b815c..26a0986 100644 --- a/doc/src/sgml/ref/create_trigger.sgml +++ b/doc/src/sgml/ref/create_trigger.sgml @@ -76,7 +76,10 @@ CREATE [ CONSTRAINT ] TRIGGER name executes once for any given operation, regardless of how many rows it modifies (in particular, an operation that modifies zero rows will still result in the execution of any applicable FOR - EACH STATEMENT triggers). + EACH STATEMENT triggers). Note that since + INSERT with an ON CONFLICT UPDATE + clause is considered an INSERT statement, no + UPDATE statement level trigger will be fired. diff --git a/doc/src/sgml/ref/create_view.sgml b/doc/src/sgml/ref/create_view.sgml index 2b7a98f..aea8447 100644 --- a/doc/src/sgml/ref/create_view.sgml +++ b/doc/src/sgml/ref/create_view.sgml @@ -291,8 +291,9 @@ CREATE VIEW vista AS SELECT text 'Hello World' AS hello; Simple views are automatically updatable: the system will allow INSERT, UPDATE and DELETE statements - to be used on the view in the same way as on a regular table. A view is - automatically updatable if it satisfies all of the following conditions: + to be used on the view in the same way as on a regular table (aside from + the limitations on ON CONFLICT noted below). A view is automatically + updatable if it satisfies all of the following conditions: @@ -388,6 +389,34 @@ CREATE VIEW vista AS SELECT text 'Hello World' AS hello; not need any permissions on the underlying base relations (see ). + + INSERT with an ON CONFLICT clause + is only supported on updatable views under specific circumstances. + If a set of columns/expressions has been provided with which to + infer a unique index to consider as the arbiter of whether the + statement ultimately takes an alternative path - if a would-be + duplicate violation in some particular unique index is tacitly + taken as provoking an alternative UPDATE or + IGNORE path - then updatable views are not supported. + Since this specification is already mandatory for + INSERT with ON CONFLICT UPDATE, + this implies that only the ON CONFLICT IGNORE variant + is supported, and only when there is no such specification. For + example: + + + +-- Unsupported: +INSERT INTO my_updatable_view(key, val) VALUES(1, 'foo') ON CONFLICT (key) + UPDATE SET val = EXCLUDED.val; +INSERT INTO my_updatable_view(key, val) VALUES(1, 'bar') ON CONFLICT (key) + IGNORE; + +-- Supported (note the omission of "key" column): +INSERT INTO my_updatable_view(key, val) VALUES(1, 'baz') ON CONFLICT + IGNORE; + + diff --git a/doc/src/sgml/ref/insert.sgml b/doc/src/sgml/ref/insert.sgml index a3cccb9..f5e1541 100644 --- a/doc/src/sgml/ref/insert.sgml +++ b/doc/src/sgml/ref/insert.sgml @@ -24,6 +24,14 @@ PostgreSQL documentation [ WITH [ RECURSIVE ] with_query [, ...] ] INSERT INTO table_name [ ( column_name [, ...] ) ] { DEFAULT VALUES | VALUES ( { expression | DEFAULT } [, ...] ) [, ...] | query } + [ ON CONFLICT [ ( { column_name_index | ( expression_index ) } [, ...] ) ] + { IGNORE | UPDATE + SET { column_name = { expression | DEFAULT } | + ( column_name [, ...] ) = ( { expression | DEFAULT } [, ...] ) + } [, ...] + [ WHERE condition ] + } + ] [ RETURNING * | output_expression [ [ AS ] output_name ] [, ...] ] @@ -32,9 +40,15 @@ INSERT INTO table_name [ ( Description - INSERT inserts new rows into a table. - One can insert one or more rows specified by value expressions, - or zero or more rows resulting from a query. + INSERT inserts new rows into a table. One can + insert one or more rows specified by value expressions, or zero or + more rows resulting from a query. An alternative path + (IGNORE or UPDATE) can + optionally be specified, to be taken in the event of detecting that + proceeding with insertion would result in a uniqueness violation + (i.e. a conflicting tuple already exists). The alternative path is + considered individually for each row proposed for insertion, and is + taken (or not taken) once per row. @@ -59,22 +73,142 @@ INSERT INTO table_name [ ( + The optional ON CONFLICT clause specifies a path to + take as an alternative to raising a uniqueness violation error. + ON CONFLICT IGNORE simply avoids inserting any + individual row when it is determined that a uniqueness violation + error would otherwise need to be raised. ON CONFLICT + UPDATE has the system take an UPDATE path in + respect of such rows instead. ON CONFLICT UPDATE + guarantees an atomic INSERT or + UPDATE outcome - provided there is no incidental + error, one of those two outcomes is guaranteed, even under high + concurrency. Note that in the event of an ON CONFLICT + path being taken, RETURNING returns no value in respect + of any not-inserted rows. + + + + ON CONFLICT UPDATE optionally accepts a + WHERE clause condition. When provided, + the statement only proceeds with updating if the + condition is satisfied. Otherwise, unlike a + conventional UPDATE, the row is still locked for + update. Note that the condition is evaluated last, + after a conflict has been identified as a candidate to update. + + + + ON CONFLICT UPDATE is effectively an auxiliary query of + its parent INSERT. Two aliases are visible to + the auxiliary query only - TARGET and + EXCLUDED. The first alias is just a standard alias for + the target relation in the context of the auxiliary query, while + the second alias refers to rows originally proposed for insertion. + Both aliases can be used in the auxiliary query targetlist and + WHERE clause. This allows expressions (in particular, + assignments) to reference rows originally proposed for insertion. + Note that the effects of all per-row BEFORE INSERT + triggers are carried forward. This is particularly useful for + multi-insert ON CONFLICT UPDATE statements; when + inserting or updating multiple rows, constants need only appear + once. + + + + There are several restrictions on the ON CONFLICT + UPDATE clause that do not apply to UPDATE + statements. Subqueries may not appear in either the + UPDATE targetlist, nor its WHERE + clause (although simple multi-assignment expressions are + supported). WHERE CURRENT OF cannot be used. In + general, only columns in the target table, and excluded values + originally proposed for insertion may be referenced. Operators and + functions may be used freely, though. + + + + INSERT with an ON CONFLICT UPDATE + clause is a deterministic statement. This means + that the command will not UPDATE any single row + more than once; a cardinality violation error will be raised when + this situation arises. Rows proposed for insertion should not + duplicate each other in terms of attributes constrained by the + conflict-arbitrating unique index. Note that the ordinary rules + for unique indexes with regard to null apply analogously to whether + or not an arbitrating unique index indicates if the alternative + path should be taken. This means that when a null value appears in + any uniquely constrained tuple's attribute in an + INSERT statement with ON CONFLICT + UPDATE, rows proposed for insertion will never take the + alternative path (provided that a BEFORE ROW + INSERT trigger does not make null values non-null before + insertion); the statement will always insert, assuming there is no + unrelated error. Note that merely locking a row (by having it not + satisfy the WHERE clause condition) + does not count towards whether or not the row has been affected + multiple times (and whether or not a cardinality violation error is + raised). + + + + ON CONFLICT UPDATE requires a unique index + inference specification, which is an expression + containing one or more columns or expressions on columns. These + are used to infer a single unique index to limit pre-checking for + duplicates to (if no appropriate index is available, an error is + raised). ON CONFLICT IGNORE makes this optional. + Omitting the specification indicates a total indifference to where + any would-be uniqueness violation could occur, which isn't always + appropriate; at times, it may be desirable for ON + CONFLICT IGNORE to not suppress a duplicate + violation within an index where that isn't explicitly anticipated. + Note that ON CONFLICT UPDATE assignment may result in a + uniqueness violation, just as with a conventional + UPDATE. + + + + The rules for unique index inference are straightforward. Columns + and/or expressions specified must match all the columns/expressions + of some existing unique index on table_name. The order of the + columns/expressions in the index definition, or whether or not the + index definition specified NULLS FIRST or + NULLS LAST, or the internal sort order of each column + (whether DESC or ASC were specified) are + all irrelevant. However, partial unique indexes are not supported + as arbiters of whether an alternative ON CONFLICT path + should be taken, nor are deferred unique constraints. + + + The optional RETURNING clause causes INSERT to compute and return value(s) based on each row actually inserted. This is primarily useful for obtaining values that were supplied by defaults, such as a serial sequence number. However, any expression using the table's columns is allowed. The syntax of the RETURNING list is identical to that of the output list - of SELECT. + of SELECT. Only rows that were successfully inserted + will be returned. Since RETURNING is not part of the + UPDATE auxiliary query, the special ON + CONFLICT UPDATE aliases (TARGET and + EXCLUDED) may not be referenced. You must have INSERT privilege on a table in - order to insert into it. If a column list is specified, you only - need INSERT privilege on the listed columns. - Use of the RETURNING clause requires SELECT - privilege on all columns mentioned in RETURNING. - If you use the UPDATE + privilege if and only if ON CONFLICT UPDATE + is specified. If a column list is specified, you only need + INSERT privilege on the listed columns. + Similarly, when ON CONFLICT UPDATE is specified, you + only need UPDATE privilege on the column(s) that are + listed to be updated, as well as SELECT privilege on any column + whose values are read in the ON CONFLICT UPDATE + expressions or condition. Use of the RETURNING clause + requires SELECT privilege on all columns mentioned in + RETURNING. If you use the query clause to insert rows from a query, you of course need to have SELECT privilege on any table or column used in the query. @@ -121,7 +255,45 @@ INSERT INTO table_name [ ( table_name. The column name can be qualified with a subfield name or array subscript, if needed. (Inserting into only some fields of a - composite column leaves the other fields null.) + composite column leaves the other fields null.) When + referencing a column with ON CONFLICT UPDATE, do not + include the table's name in the specification of a target + column. For example, INSERT ... ON CONFLICT UPDATE tab + SET TARGET.col = 1 is invalid. + + + + + + column_name_index + + + The name of a table_name column (with several + columns potentially named). These are used to infer a + particular unique index defined on table_name. This requires + ON CONFLICT UPDATE and ON CONFLICT + IGNORE to assume that all expected sources of uniqueness + violations originate within the columns/rows constrained by the + unique index. When this is omitted, (which is forbidden with + the ON CONFLICT UPDATE variant), the system checks + for sources of uniqueness violations ahead of time in all unique + indexes. Otherwise, only a single specified unique index is + checked ahead of time, and uniqueness violation errors can + appear for conflicts originating in any other unique index. If + a unique index cannot be inferred, an error is raised. + + + + + + expression_index + + + Equivalent to column_name_index, but used to + infer a particular expressional index instead. @@ -167,12 +339,25 @@ INSERT INTO table_name [ ( + condition + + + An expression that returns a value of type boolean. + Only rows for which this expression returns true + will be updated, although all rows will be locked when the + ON CONFLICT UPDATE path is taken. + + + + + output_expression An expression to be computed and returned by the INSERT - command after each row is inserted. The expression can use any - column names of the table named by table_name. + command after each row is inserted (not updated). The + expression can use any column names of the table named by + table_name. Write * to return all columns of the inserted row(s). @@ -204,14 +389,16 @@ INSERT oid countoid is the OID assigned to the inserted row. Otherwise oid is zero. + The command tag does not indicate the number of rows updated by + ON CONFLICT UPDATE. If the INSERT command contains a RETURNING clause, the result will be similar to that of a SELECT statement containing the columns and values defined in the - RETURNING list, computed over the row(s) inserted by the - command. + RETURNING list, computed over the row(s) inserted (not + updated) by the command. @@ -311,7 +498,49 @@ WITH upd AS ( RETURNING * ) INSERT INTO employees_log SELECT *, current_timestamp FROM upd; - + + + + Insert or update new distributors as appropriate. Assumes a unique + index has been defined that constrains values appearing in the + did column. Note that an EXCLUDED + expression is used to reference values originally proposed for + insertion: + + INSERT INTO distributors (did, dname) + VALUES (5, 'Gizmo transglobal'), (6, 'Doohickey, inc') + ON CONFLICT (did) UPDATE + SET dname = EXCLUDED.dname || ' (formerly ' || TARGET.dname || ')' + + + + Insert a distributor, or do nothing for rows proposed for insertion + when an existing, excluded row (a row with a matching constrained + column or columns after before row insert triggers fire) exists. + Assumes a unique index has been defined that constrains values + appearing in the did column (although since the + IGNORE variant was used, the specification of columns + to infer a unique index from is not mandatory): + + INSERT INTO distributors (did, dname) VALUES (7, 'Doodad GmbH') + ON CONFLICT (did) IGNORE + + + + Insert or update new distributors as appropriate. Assumes a unique + index has been defined that constrains values appearing in the + did column. WHERE clause is used to + limit the rows actually updated (any existing row not updated will + still be locked, though): + + -- Don't update any existing row if it was already renamed at some + -- earlier stage + INSERT INTO distributors (did, dname) VALUES (8, 'Thingamabob Distribution') + ON CONFLICT (did) UPDATE + SET dname = EXCLUDED.dname || ' (formerly ' || TARGET.dname || ')' + WHERE TARGET.dname NOT LIKE '%(formerly %)' + + @@ -321,7 +550,8 @@ INSERT INTO employees_log SELECT *, current_timestamp FROM upd; INSERT conforms to the SQL standard, except that the RETURNING clause is a PostgreSQL extension, as is the ability - to use WITH with INSERT. + to use WITH with INSERT, and the ability to + specify an alternative path with ON CONFLICT. Also, the case in which a column name list is omitted, but not all the columns are filled from the VALUES clause or query, diff --git a/doc/src/sgml/ref/set_constraints.sgml b/doc/src/sgml/ref/set_constraints.sgml index 7c31871..1e0a2f8 100644 --- a/doc/src/sgml/ref/set_constraints.sgml +++ b/doc/src/sgml/ref/set_constraints.sgml @@ -69,7 +69,11 @@ SET CONSTRAINTS { ALL | name [, ... Currently, only UNIQUE, PRIMARY KEY, REFERENCES (foreign key), and EXCLUDE - constraints are affected by this setting. + constraints are affected by this setting. Note that constraints + that were created with this clause cannot be used as arbiters of + whether or not to take the alternative path with an + INSERT statement that includes an ON + CONFLICT UPDATE clause. NOT NULL and CHECK constraints are always checked immediately when a row is inserted or modified (not at the end of the statement). diff --git a/doc/src/sgml/trigger.sgml b/doc/src/sgml/trigger.sgml index f94aea1..71ddeee 100644 --- a/doc/src/sgml/trigger.sgml +++ b/doc/src/sgml/trigger.sgml @@ -40,7 +40,10 @@ On tables and foreign tables, triggers can be defined to execute either before or after any INSERT, UPDATE, or DELETE operation, either once per modified row, - or once per SQL statement. + or once per SQL statement. If an INSERT + contains an ON CONFLICT UPDATE clause, it is possible that the + effects of a BEFORE insert trigger and a BEFORE update trigger can both be + applied twice, if an EXCLUDED expression appears. UPDATE triggers can moreover be set to fire only if certain columns are mentioned in the SET clause of the UPDATE statement. @@ -119,6 +122,30 @@ + If an INSERT contains an ON CONFLICT + UPDATE clause, it is possible that the effects of all row-level + BEFORE INSERT triggers and all + row-level BEFORE UPDATE triggers can both be + applied in a way that is apparent from the final state of the + updated row, if an EXCLUDED expression appears. There + need not be an EXCLUDED expression for both sets of + BEFORE row-level triggers to execute, though. The possibility of + surprising outcomes should be considered when there are both + BEFORE INSERT and + BEFORE UPDATE row-level triggers + that both affect a row being inserted/updated (this can still be + problematic if the modifications are more or less equivalent if + they're not also idempotent). Note that statement-level + UPDATE triggers are never executed when + ON CONFLICT UPDATE is specified, since technically an + UPDATE statement was not executed. ON CONFLICT UPDATE + is not supported on views (Only ON CONFLICT IGNORE is + supported on updatable views); therefore, unpredictable + interactions with INSTEAD OF triggers are not + possible. + + + Trigger functions invoked by per-statement triggers should always return NULL. Trigger functions invoked by per-row triggers can return a table row (a value of -- 1.9.1