summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRobin Thomas <robin.thomas@livestream.com>2016-04-14 11:07:49 -0400
committerRobin Thomas <robin.thomas@livestream.com>2016-04-14 11:07:49 -0400
commit3a7e94c4d5091bebb5d67246b9888d97ea85e691 (patch)
treef5ab33f4bf1eb52bb521289659b08ffff5d99ef3
parenta7bc406390ebcce9cb10d2b39bdfa55cdc894b49 (diff)
downloadsqlalchemy-3a7e94c4d5091bebb5d67246b9888d97ea85e691.tar.gz
finished docs (sans formatting and linking checks) for ON CONFLICT,
including explanation of using constraint object as conflict targets.
-rw-r--r--lib/sqlalchemy/dialects/postgresql/base.py41
1 files changed, 37 insertions, 4 deletions
diff --git a/lib/sqlalchemy/dialects/postgresql/base.py b/lib/sqlalchemy/dialects/postgresql/base.py
index 6c50c19c3..4ef6a3087 100644
--- a/lib/sqlalchemy/dialects/postgresql/base.py
+++ b/lib/sqlalchemy/dialects/postgresql/base.py
@@ -247,6 +247,8 @@ use the :meth:`._UpdateBase.returning` method on a per-statement basis::
where(table.c.name=='foo')
print result.fetchall()
+.. _postgresql_insert_on_conflict:
+
INSERT...ON CONFLICT (Upsert)
-------------------------
@@ -273,17 +275,48 @@ of the row proposed for insert. Use the value `'update'` for the keyword argumen
and the SQL compiler will produce an ``ON CONFLICT`` clause that performs
``DO UPDATE SET...`` for every column value in the ``VALUES`` clause that
is not a primary key column for the target table. The produced SQL will use the primary key
-columns as the "conflict target" in the ``ON CONFLICT`` clause.
+columns as the "conflict target" in the ``ON CONFLICT`` clause. This usage
+requires that the targeted table have at least one column participating
+in a `PrimaryKeyConstraint`.
`ON CONFLICT` is also commonly used to skip inserting a row entirely
-if a conflict occurs. To do this, use the value 'nothing' for the keyword argument:
+if any conflict occurs. To do this, use the value 'nothing' for the keyword argument:
table.insert(postgresql_on_conflict='nothing').\\
values(key_column='existing_value', other_column='foo')
-Other, more sophisticated forms of ``ON CONFLICT`` are possible, but they are
+Less commonly, you may need to specify which of several unique constraints on a table
+should be used to determine if an insert conflict exists. In these cases, use
+the :class:`.DoNothing` or :class:`.DoUpdate` object, and pass one of the following
+to indicate the "conflict target" constraint:
+
+* a single Column object or string with the column's name
+* a list or tuple of several Column objects or name strings
+* a :class:`.PrimaryKeyConstraint`, :class:`.UniqueConstraint`,
+ or :class:`.postgresql.ExcludeConstraint` object representing
+ the unique constraint to target.
+
+If you use :class:`.DoUpdate`, you need to specify which columns on the existing row
+to set with values from the row proposed for insert. Use the
+:meth:`.DoUpdate.set_with_excluded` chaining method to do so, passing a variable
+set of Column or Column name string arguments for the columns to set using
+the special `excluded` alias representing the row proposed for insertion:
+
+ from sqlalchemy.dialects.postgresql import DoUpdate, DoNothing
+ from sqlalchemy.schema import UniqueConstraint
+
+ unique_constr = UniqueConstraint(table.c.username)
+ update_action = DoUpdate(unique_constr).set_with_excluded('key_column', 'other_column')
+
+ table.insert(postgresql_on_conflict=update_action).\\
+ .values(key_column='existing_value', other_column='foo')
+
+Other, more sophisticated forms of ``ON CONFLICT`` are possible, especially
+in what can be put in `SET ...` clauses, but they are
not yet supported or documented by the dialect. Use text-based statements
-for more advanced ``ON CONFLICT`` clauses. For more information, see the
+for more advanced ``ON CONFLICT`` clauses.
+
+For more information on the PostgreSQL feature, see the
``ON CONFLICT` section of the `INSERT` statement in the PostgreSQL docs
<http://www.postgresql.org/docs/current/static/sql-insert.html#SQL-ON-CONFLICT>`_.